2018-05-07 13:22:02 qq_26270779 阅读数 317
  • 老孙的游戏课:第2篇 人机交互

    本课是“老孙游戏课”系列课程的第2篇,该课程重点介绍Unity中的人机交互功能,以游戏实例为背景,深入浅出地讲解键盘操作、鼠标操作、碰撞检测、血条制作等多个游戏中常用的交互变换功能。

    18 人正在学习 去看看 孙博文

在开发中,尤其是跟模型与交互的时候,都会用到射线检测,这篇文章给大家分享一些射线检测的方法实现。

射线:射线是3D世界中一个点向一个方向发射的一条无终点的线,在发射轨迹中与其他物体发生碰撞时,它将停止发射 。
用途:射线应用范围比较广, 多用于碰撞检测(如:子弹飞行是否击中目标)、角色移动等。

相关API:

  1、Ray Camera.main.ScreenPointToRay(Vector3 pos)   返回一条射线Ray从摄像机到屏幕指定一个点

  2、Ray Camera.main.ViewportPointToRay(Vector3 pos)  返回一条射线Ray从摄像机到视口(视口之外无效)指定一个点

  3、Ray 射线类

  4、RaycastHit 光线投射碰撞信息

  5、bool Physics.Raycast(Vector3 origin, Vector3 direction, float distance, int layerMask)

  当光线投射与任何碰撞器交叉时为真,否则为假。

  bool Physics.Raycast(Ray ray, Vector3 direction, RaycastHit out hit, float distance, int layerMask)

  在场景中投下可与所有碰撞器碰撞的一条光线,并返回碰撞的细节信息()。

  bool Physics.Raycast(Ray ray, float distance, int layerMask)

  当光线投射与任何碰撞器交叉时为真,否则为假。

  bool Physics.Raycast(Vector3 origin, Vector3 direction, RaycastHit out hit,float distance, int layerMask)

  当光线投射与任何碰撞器交叉时为真,否则为假。

  注意:如果从一个球型体的内部到外部用光线投射,返回为假。

  参数理解:

  origin : 在世界坐标中射线的起始点

  direction: 射线的方向

  distance: 射线的长度

  hit: 使用c#中out关键字传入一个空的碰撞信息类,然后碰撞后赋值。可以得到碰撞物体的transform,rigidbody,point等信息。

  layerMask: 只选定Layermask层内的碰撞器,其它层内碰撞器忽略。 选择性的碰撞

  6、RaycastHit[] RaycastAll(Ray ray, float distance, int layerMask)

  投射一条光线并返回所有碰撞,也就是投射光线并返回一个RaycastHit[]结构体。

  下面一个利用射线做的拾取的小例子(将代码直接拖拽到主相机上)

using UnityEngine;

   using System.Collections;
   public class RayTest : MonoBehaviour {

       // Use this for initialization
       void Start () {

      }

       // Update is called once per frame
       void Update ()
       {
           if(Input.GetMouseButton(0))
           {
               Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//从摄像机发出到点击坐标的射线
               RaycastHit hitInfo;
              if(Physics.Raycast(ray,out hitInfo))
              {
                   Debug.DrawLine(ray.origin,hitInfo.point);//划出射线,只有在scene视图中才能看到
                   GameObject gameObj = hitInfo.collider.gameObject;
                   Debug.Log("click object name is " + gameObj.name);
                   if(gameObj.tag == "boot")//当射线碰撞目标为boot类型的物品 ,执行拾取操作
                   {
                       Debug.Log("pick up!");
                   }
               }
           }
       }
   }


2020-02-18 13:18:10 CSDN_ONION 阅读数 38
  • 老孙的游戏课:第2篇 人机交互

    本课是“老孙游戏课”系列课程的第2篇,该课程重点介绍Unity中的人机交互功能,以游戏实例为背景,深入浅出地讲解键盘操作、鼠标操作、碰撞检测、血条制作等多个游戏中常用的交互变换功能。

    18 人正在学习 去看看 孙博文

本文转自Unity Connect博主 dreamfairy

视频封面

效果图

制作可交互的水体,大致分为三步
1.标记水体碰撞的位置
2.计算水波的传递 通过波动公式,3D或者2D 波动公式都行
3.水面顶点采样波动传递结果计算结果做顶点Y轴偏移 本文参考的波动相关资料 https://en.wikipedia.org/wiki/Wave_equation
https://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869 流体 章节

相关公式

image

根据公式可知波的下次一次传递 z(i,j,k+1) 为 当前波值+上一次波值+周围波值
当前波值 = (4-8c2*t2/d2/d2)/(u*t)
上一次波值 *= (ut-2) / (ut + 2)
四周波值 *= (2c2t2/d^2) / (ut + 2)
其中各参数含义为 c 波速, u 粘度, d 波的递进距离, t 为递进时间

ok~ 我们重头开始

首先要建立水面
这里直接用Unity Wiki的已有轮子的创建平面,下载wiki上的代码,传到项目中 https://wiki.unity3d.com/index.php/CreatePlane

image

这里我们直接创建一个宽10米,长10米,间隔100的平面, 间隔越多,水体的颗粒感越小

对应本文开头描述的三大步骤

创建3个纹理
对应水体碰撞标记,传递,渲染

 m_waterWaveMarkTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
        m_waterWaveMarkTexture.name = "m_waterWaveMarkTexture";
        m_waveTransmitTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
        m_waveTransmitTexture.name = "m_waveTransmitTexture";
        m_prevWaveMarkTexture = new RenderTexture(WaveTextureResolution, WaveTextureResolution, 0, RenderTextureFormat.Default);
        m_prevWaveMarkTexture.name = "m_prevWaveMarkTexture";

标记水体碰撞位置

void WaterPlaneCollider()
    {
        hasHit = false;
        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitInfo = new RaycastHit();
            bool ret = Physics.Raycast(ray.origin, ray.direction, out hitInfo);
            if (ret)
            {
                Vector3 waterPlaneSpacePos = WaterPlane.transform.worldToLocalMatrix * new Vector4(hitInfo.point.x, hitInfo.point.y, hitInfo.point.z, 1);

                float dx = (waterPlaneSpacePos.x / WaterPlaneWidth) + 0.5f;
                float dy = (waterPlaneSpacePos.z / WaterPlaneLength) + 0.5f;

                hitPos.Set(dx, dy);
                m_waveMarkParams.Set(dx, dy, WaveRadius * WaveRadius, WaveHeight);

                hasHit = true;
            }
        }
    }

由于我们默认Raycast 获取的是碰撞的世界坐标,我们期望的是直接获取到 [0-1] 范围的数值用来映射到uv空间,直接在 m_waterWaveMarkTexture 进行标记, 因此我们乘以一个 world2Local 矩阵变换到本地, 又因为CreatePlane默认创建的Pivot 位于中心,再除以宽高缩放到1区间时,值域落在[-0.5,0.5]上,因此我们还要做 + 0.5偏移 标记水体碰撞Shader

 float dx = i.uv.x - _WaveMarkParams.x;
                float dy = i.uv.y - _WaveMarkParams.y;

                float disSqr = dx * dx + dy * dy;

                int hasCol = step(0, _WaveMarkParams.z - disSqr);

                float waveValue = DecodeHeight(tex2D(_MainTex, i.uv));

                if (hasCol == 1) {
                    waveValue = _WaveMarkParams.w;
                }

根据传入的_WaveMarkParams.xy 跟当前uv 对比,在笔刷范围内的像素标记位默认波高度

波的传递Shader

 static const float2 WAVE_DIR[4] = { float2(1, 0), float2(0, 1), float2(-1, 0), float2(0, -1) };

                float dx = _WaveTransmitParams.w;

                float avgWaveHeight = 0;
                for (int s = 0; s < 4; s++)
                {
                    avgWaveHeight += DecodeHeight(tex2D(_MainTex, i.uv + WAVE_DIR[s] * dx));
                }

                //(2 * c^2 * t^2 / d ^2) / (u * t + 2)*(z(x + dx, y, t) + z(x - dx, y, t) + z(x, y + dy, t) + z(x, y - dy, t);
                float agWave = _WaveTransmitParams.z * avgWaveHeight;

                // (4 - 8 * c^2 * t^2 / d^2) / (u * t + 2)
                float curWave = _WaveTransmitParams.x *  DecodeHeight(tex2D(_MainTex, i.uv));
                // (u * t - 2) / (u * t + 2) * z(x,y,z, t - dt) 上一次波浪值 t - dt
                float prevWave = _WaveTransmitParams.y * DecodeHeight(tex2D(_PrevWaveMarkTex, i.uv));

                //波衰减
                float waveValue = (curWave + prevWave + agWave) * _WaveAtten;

最后就是水体的呈现,因为需要做顶点纹理采样,因此需要至少ES3.0 硬体

 v2f vert (appdata v)
            {
                v2f o;

                float4 localPos = v.vertex;
                float4 waveTransmit = tex2Dlod(_WaveResult, float4(v.uv, 0, 0));
                float waveHeight = DecodeFloatRGBA(waveTransmit);

                localPos.y += waveHeight * _WaveScale;

                float3 worldPos = mul(unity_ObjectToWorld, localPos);
                float3 worldSpaceNormal = mul(unity_ObjectToWorld, v.normal);
                float3 worldSpaceViewDir = UnityWorldSpaceViewDir(worldPos);

                o.vertex = mul(UNITY_MATRIX_VP, float4(worldPos, 1));
                o.uv = v.uv;
                o.worldSpaceReflect = reflect(-worldSpaceViewDir, worldSpaceNormal);
                return o;
            }

github地址 https://github.com/dreamfairy/interactivity-waterplane

原文链接:https://connect.unity.com/p/zai-unityzhong-shi-xian-shui-ti-jiao-hu?app=true

欢迎戳上方原文链接,下载Unity官方技术社区app,发现更多资源干货~

2019-02-06 17:05:13 Jaihk662 阅读数 334
  • 老孙的游戏课:第2篇 人机交互

    本课是“老孙游戏课”系列课程的第2篇,该课程重点介绍Unity中的人机交互功能,以游戏实例为背景,深入浅出地讲解键盘操作、鼠标操作、碰撞检测、血条制作等多个游戏中常用的交互变换功能。

    18 人正在学习 去看看 孙博文

 

前文:https://blog.csdn.net/Jaihk662/article/details/86749803(摄像机与Game视图)

一、利用摄像机创建射线

物理射线:从一个点往一个方向发射一根无限长的射线,当这根射线与场景中的其余的游戏物体的碰撞体组件相碰撞时射线结束,由于射线可以与物理组件 Collider 相交互,所以“射线”也称之为“物理射线”

射线的定义:Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition)

  1. Camera.main:Tag为"MainCamera"的摄像机 Camera 组件的引用。
  2. ScreenPointToRay(Vector3):摄像机组件对象下的一个方法,会返回一个指向Vector3,Ray类型的射线
  3. Input.mousePosition:鼠标所在的位置
  4. Ray:一个结构体,代表射线

检查射线与其他物体的碰撞:

Physics.Raycast(Ray, out RaycastHit):射线检查,检查射线Ray,如果它与场景中的物理碰撞了,返回值为真,并且将碰撞信息存储到 RaycastHit 类型的变量中(别忘了out参数的定义)

代码如下:实现鼠标左键销毁金币(将脚本移到主摄像机上)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraRay : MonoBehaviour
{
    RaycastHit hit;
    private Ray ray1;
    private void Start()
    {
        
    }
    private void Update()
    {
        Kill();
    }
    void Kill()
    {
        if (Input.GetMouseButtonDown(0))        //按下鼠标左键
        {
            ray1 = Camera.main.ScreenPointToRay(Input.mousePosition);      //使用主摄像机创建一个射线,瞄准你鼠标的所在位置
            if (Physics.Raycast(ray1, out hit))         //使用物理类检测射线的碰撞
            {
                if(hit.collider.gameObject.tag=="Coin")
                    GameObject.Destroy(hit.collider.gameObject);        //如果碰撞的是金币,销毁掉
            }
        }
    }
}

效果如下:

 

2015-02-04 23:45:00 PanPen120 阅读数 807
  • 老孙的游戏课:第2篇 人机交互

    本课是“老孙游戏课”系列课程的第2篇,该课程重点介绍Unity中的人机交互功能,以游戏实例为背景,深入浅出地讲解键盘操作、鼠标操作、碰撞检测、血条制作等多个游戏中常用的交互变换功能。

    18 人正在学习 去看看 孙博文

潘鹏在CSDN上原创,如其他网站转载请注意排版和写明出处:

十一、点击事件

图片加个触摸Physics2D,关联的脚本void OnMouseDown(){}函数里点击就进入


十二、碰撞检测

两个图片相遇交互想要做些操作

给图片加组件碰撞器,Componet->Physics->Box Colider(Colider都是碰撞器,区别在于形状不同,我常用Box,加了后图片身上有绿色边框,如果是模型用Mesh更精准)

组件附带的Is Trigger选项选取,类似于开关

给图片加组件刚体,Componet->Physics->Rigidbody

(组件附带的Use Gravity取消掉,Is Kinematic选取)

碰撞函数

注意:碰撞的物体都需要按照上面的步骤,其次就是要添加的碰撞器和刚体是一样的,有Physics和Physics2D,选一样的,我可吃了苦头

注意:脚本A继承脚本B,B里面的碰撞函数不适用A,A直接重写,不用管其他的,适用A,就一起连碰撞函数继承

十三、Tag的好处

玩过cocos2d的人知道,Tag相当于别名,特别好用,我们操作方便,我都不知道怎么形容,unity3d开始自带一个tag是Player,主角,也就是一个普通的tag

创建新的Tag:Edit->ProjectSettings->Tags and Layers,在Inspector的窗口里有Tags,size是你想要几个,下面就是给tag起名字,创建

给图片什么的添加Tag:选取图片后在Inspector的窗口里Tag里就有你自己创建的Tag及Player

使用1:附加了tag的对象,点出来tag,引号里是tag,十二和十三两图结合的意思是在添加了这个脚本的物体碰撞到添加了Tag为Player或者Tag为PlayerRocket的物体的话…

使用2:GameObject.FindGameObjectWithTag ("Tag")获取其他游戏对象GameObject可以点出来找Tag


十四、增量时间Time.deltaTime

这是一个按秒算的时间,特别好用

假如你控制图片移动float i = Time.deltaTime*2f;每秒移动速度2米

加入你要让图片5秒后消失i=5;i-=Time.deltaTime;if(i<=0)……5秒后……


十五、Awake()函数

void Awake(){};这个函数叫更早函数(名字我起的……没查到叫什么),因为大家知道运行先进行Start()函数里的,但如果用这个函数,在进入Start之前先进入这个函数


十六、技巧

Transform组件用的非常广,回头我在总结,现在都凌晨多了,我先总结一个有关这个技巧,音频组件等有些一样可以用这个技巧

我们用这个组件一般是this点出来,在Update函数里面多用几次的话,内部…效率低,建个这样类型的变量,在Start()函数里就先赋值,以后用这个变量,是一样的,效率快了


十七、音频(给物体加音乐)

1.加音频组件:Component->Audio->Audio Source

2.创建个音频变量public AudioClip m_shootClip;方便在unity界面上拖想要的音乐进来

3.m_audio.PlayOneShot(m_shootClip);这一行代码就是了,m_audio是按十六写的,全部写法是this.audio,括号里加入音频变量



2017-04-03 16:12:04 MacYosef 阅读数 544
  • 老孙的游戏课:第2篇 人机交互

    本课是“老孙游戏课”系列课程的第2篇,该课程重点介绍Unity中的人机交互功能,以游戏实例为背景,深入浅出地讲解键盘操作、鼠标操作、碰撞检测、血条制作等多个游戏中常用的交互变换功能。

    18 人正在学习 去看看 孙博文

参考:游戏蛮牛-手册

碰撞体 (Collider) 组合
在 Unity 中可以进行许多不同的碰撞体 (Collider) 组合。每个游戏都是独一无二的,不同组合可能更适合于不同类型的游戏。如果在游戏中使用物理,则了解不同基本碰撞体 (Collider) 类型、其常见用途以及与其他类型对象的交互方式会十分有帮助。

静态碰撞体 (Static Collider)
这些是未附加刚体 (Rigidbody)、但的确附加了碰撞体 (Collider) 的游戏对象 (GameObject)。这些对象应保持静态,或很少移动。这些十分适用于环境几何结构。它们在刚体 (Rigidbody) 与之碰撞时不会移动。

刚体碰撞体 (Rigidbody Collider)
这些游戏对象 (GameObject) 同时包含刚体 (Rigidbody) 和碰撞体 (Collider)。它们通过脚本编写的力和碰撞,完全受物理引擎影响。它们可以与仅包含碰撞体 (Collider) 的游戏对象 (GameObject) 碰撞。这些很可能是使用物理的游戏中的主要碰撞体 (Collider) 类型。

运动学刚体碰撞体 (Kinematic Rigidbody Collider)
此游戏对象 (GameObject) 包含碰撞体 (Collider) 和标记有“为运动学”(IsKinematic) 的刚体 (Rigidbody)。要移动此游戏对象 (GameObject),请修改其 变换组件 (Component),而不是应用力。它们类似于静态碰撞体 (Static Collider),但是更适合于要经常四处移动碰撞体 (Collider) 的情况。有一些使用此游戏对象 (GameObject) 的其他专业方案。
此对象可以用于通常希望静态碰撞体 (Static Collider) 发送触发器 (Trigger) 事件的情况。因为触发器 (Trigger) 必须附加刚体 (Rigidbody),所以应添加刚体 (Rigidbody),然后启用“为运动学”(IsKinematic)。这可防止对象脱离物理影响,并使您可以在需要时接收触发器 (Trigger) 事件。
运动学刚体 (Kinematic Rigidbody) 可以方便地打开和关闭。这适合于在以下情况中创建布娃娃:通常希望角色跟随在动画之后,然后在发生碰撞时(通过爆炸或所选的任何其他事物进行提示)变为布娃娃。发生这种情况时,只需通过脚本将所有运动学刚体 (Kinematic Rigidbody) 转变为普通刚体 (Rigidbody)。
如果让刚体 (Rigidbody) 静止下来以便在一段时间内不移动,它们会“入睡”。即,它们在物理更新过程中不会进行计算,因为它们不会前往任何位置。如果将运动学刚体 (Kinematic Rigidbody) 从休眠的普通刚体 (Rigidbody) 下边移走,则休眠的刚体 (Rigidbody) 将被唤醒并在物理更新中重新准确计算。因此如果您具有许多要四处移动的静态碰撞体 (Static Collider) 并且让不同对象正确落到其上,请使用运动学刚体碰撞体 (Kinematic Rigidbody Collider)。

标准准则:物理系统不会应用于未添加刚体的对象上。

这里写图片描述

没有更多推荐了,返回首页