当前位置:   article > 正文

第二十八章 Unity射线检测

第二十八章 Unity射线检测

本章节我们介绍一下射线。射线就是从一个固定点向一个方向发射出一条直线,在发射过程中需要判断该射线有没有与游戏物体发送碰撞。射线既可以用来检测射击游戏中武器指向目标;又可以判断鼠标是否指向游戏物体。射线的创建方式,一般使用代码来实现。接下来,我们就来创建一个新的“SampleScene3.unity”场景。这里注意的是,射线检测都是以物理系统为基础的,因此只有添加碰撞体组件的游戏物体才能被射线检测到。庆幸的是,在Unity中,创建的Cube或者Sphere,都是自动附带相应的碰撞体组件。

我们创建了三个球体Sphere1,Sphere2,Sphere3,然后我们由Sphere1为起点向X轴负方向(上图左边)发射一条射线。那么这条射线就应该可以检测到Sphere2和Sphere3。

接下来,我们创建一个“RayScript.cs”脚本文件,附加到Sphere1上面,内容如下。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class RayScript : MonoBehaviour
  5. {
  6. // Update is called once per frame
  7. void Update()
  8. {
  9. if (Input.GetKeyDown(KeyCode.A))
  10. {
  11. // 射线的起点(Sphere1的位置)
  12. Vector3 origin = transform.position;
  13. // 射线的方向(X轴负方向)
  14. Vector3 direction = Vector3.left;
  15. // 射线碰撞的目标对象
  16. RaycastHit hitInfo;
  17. // 射线的最大长度
  18. float maxDistance = 100;
  19. // 创建射线,返回是否检测到碰撞对象
  20. bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
  21. // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
  22. if (raycast)
  23. {
  24. // 获取碰撞点坐标
  25. Vector3 point = hitInfo.point;
  26. Debug.Log("碰撞点坐标:" + point);
  27. // 获取碰撞目标的名称
  28. string name = hitInfo.collider.name;
  29. Debug.Log("碰撞对象名称:" + name);
  30. // 获取目标的碰撞体组件
  31. Collider coll = hitInfo.collider;
  32. //获取目标的Transgorm组件
  33. Transform trans = hitInfo.transform;
  34. }
  35. }
  36. }
  37. }

上面的代码非常简单,我们使用四个参数来,通过Physics.Raycast方法创建一条射线,然后使用第三个参数RaycastHit hitInfo就是我们需要的碰撞目标的信息。我们可以通过这个对象获取到射线和目标的碰撞点位置信息,也可以获取目标的游戏对象名称,以及它碰撞体collider组件或者transform变换组件。其实,Physics.Raycast方法还有第五个参数int layerMask,用来指定检测图层,而忽略其他图层。在我们上一章节中,就提到过,碰撞检测可以使用层layer来进行限制。我们可以指定层与层之间的游戏对象发生碰撞,同样这里也适用于射线的碰撞检测。这里我们就不再详细介绍这个参数了。

在游戏开发中,由于射线不可见,所以有时候,我们无法判断射线碰撞的有效性。这个时候,我们可以借助Debug.DrawLine()函数和Debug.DrawRay()来模拟射线。首先介绍DrawLine,

Debug.DrawLine(Vector3 start, Vector3 end, Color color=Color.white, float duration=0.0f, bool depthTest=true);

参数为start 直线的起点,end 直线的终点,color 直线的颜色,duration 直线的持续时间。

depthTest 直线是否被靠近摄像机的对象遮挡。

Debug.DrawRay(Vector3 start, Vector3 dir, Color color=Color.white, float duration=0.0f, bool depthTest=true);

参数为start 射线的起点,dir 射线的方向和长度,color 射线的颜色,duration 射线的持续时间,depthTest 摄像是否被靠近摄像机的对象遮挡。

接下来,我们就使用Debug.DrawRay方法来模拟上面代码案例中的摄像,增加如下代码

  1. // 画一条蓝线来模拟射线
  2. Debug.DrawRay(origin, direction * 100, Color.blue, 100);

接下来,我们重新Play工程,然后按下A键,回到Scene视图(不是Game视图)中查看。

我们可以看到由Sphere1发射出来的一条蓝色的模拟射线了。

由上图我们可知,射线不仅穿过了黄球Sphere2,还穿过了绿球Sphere3了。如何能得到绿色Sphere3呢?这个就需要借助Physics.RaycastAll函数。这个函数与Physics.Raycast函数的使用是相似的,但是返回的结果是不一样的。该函数的返回值是RaycastHit数组。接下来,我们重新修改一下代码,如下所示

  1. // Update is called once per frame
  2. void Update()
  3. {
  4. if (Input.GetKeyDown(KeyCode.A))
  5. {
  6. // 射线的起点(Sphere1的位置)
  7. Vector3 origin = transform.position;
  8. // 射线的方向(X轴负方向)
  9. Vector3 direction = Vector3.left;
  10. // 射线碰撞的目标对象
  11. //RaycastHit hitInfo;
  12. // 射线的最大长度
  13. float maxDistance = 100;
  14. // 创建射线,返回是否检测到碰撞对象
  15. //bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
  16. RaycastHit[] hitInfos = Physics.RaycastAll(origin, direction, maxDistance);
  17. // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
  18. //if (raycast)
  19. foreach(RaycastHit hitInfo in hitInfos)
  20. {
  21. // 获取碰撞点坐标
  22. Vector3 point = hitInfo.point;
  23. Debug.Log("碰撞点坐标:" + point);
  24. // 获取碰撞目标的名称
  25. string name = hitInfo.collider.name;
  26. Debug.Log("碰撞对象名称:" + name);
  27. // 获取目标的碰撞体组件
  28. Collider coll = hitInfo.collider;
  29. //获取目标的Transgorm组件
  30. Transform trans = hitInfo.transform;
  31. }
  32. // 画一条蓝线来模拟射线
  33. Debug.DrawRay(origin, direction * 100, Color.blue, 100);
  34. }
  35. }

我们重新Play工程,控制台输出截图如下

在实际游戏开发中,我们有时候需要检测一定范围内是否发生碰撞。比如说,我们要检测周围100米内是否存在某些游戏对象,如果存在,就向其主动发起攻击。此时,我们使用一条射线就无法完成这样的要求。Unity为我们提供了丰富的不同形状的射线检测。这里,我们可以使用Physics.OverlapSphere创建球体碰撞检测,或者使用Physics.OverlapBox创建立方体检测。他们返回的是Collider[]数组,例如我们使用射线检测附近100米内的所有物体

Collider[] colliders = Physics.OverlapSphere(transform.postion, 100.0f);

foreach(Collider collider in colliders){ …… }

最后,我们在介绍一下摄像的另一种使用方式,就是鼠标点击选中场景中的游戏对象。它的原理非常简单,就是由相机位置向鼠标点击位置发射一条射线,然后进行碰撞检测。接下来,我们就来创建一个“RayClickScript.cs”脚本,将其附加到相机上面。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class RayClickScript : MonoBehaviour
  5. {
  6. // Update is called once per frame
  7. void Update()
  8. {
  9. // 鼠标左键按下
  10. if (Input.GetMouseButtonDown(0))
  11. {
  12. // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
  13. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  14. // 声明一个射线碰撞信息类
  15. RaycastHit hit;
  16. // 进行碰撞检测
  17. bool res = Physics.Raycast(ray, out hit);
  18. // 如果产生了碰撞
  19. if(res){
  20. Debug.Log("碰撞点:" + hit.point);
  21. Debug.Log("碰撞目标:" + hit.transform.name);
  22. }
  23. }
  24. }
  25. }

然后我们Play工程,使用鼠标点击绿球Sphere3,

接下来,我们做一个有趣事情。我们点击Plane平面上一点,然后让绿球Sphere3移动到那一点。如何来完成这件有趣的事情呢?代码改动如下

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. public class RayClickScript : MonoBehaviour
  5. {
  6. // 绿球Sphere3
  7. private GameObject sphere3;
  8. // 是否移动
  9. private bool isMove = false;
  10. // 目标点
  11. private Vector3 target = Vector3.zero;
  12. // Start is called before the first frame update
  13. void Start()
  14. {
  15. // 获取绿球Sphere3
  16. sphere3 = GameObject.Find("Sphere3");
  17. }
  18. // Update is called once per frame
  19. void Update()
  20. {
  21. // 鼠标左键按下
  22. if (Input.GetMouseButtonDown(0))
  23. {
  24. // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
  25. Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
  26. // 声明一个射线碰撞信息类
  27. RaycastHit hit;
  28. // 进行碰撞检测
  29. bool res = Physics.Raycast(ray, out hit);
  30. // 如果产生了碰撞
  31. if (res && hit.transform.name == "Plane")
  32. {
  33. // 目标点(Y轴上保持不变)
  34. isMove = true;
  35. target = new Vector3(hit.point.x, sphere3.transform.position.y, hit.point.z);
  36. //Debug.Log("碰撞点:" + hit.point);
  37. //Debug.Log("碰撞目标:" + hit.transform.name);
  38. }
  39. }
  40. // 如果发生碰撞就让绿球移动到目标点
  41. if (isMove)
  42. {
  43. // 绿球朝向目标点
  44. sphere3.transform.LookAt(target);
  45. // 角色移动到目标点的距离
  46. float distance = Vector3.Distance(target, sphere3.transform.position);
  47. // 没有到达目标点就一直移动下去
  48. if (distance > 0.1f)
  49. {
  50. // 旋转后向前移动即可
  51. sphere3.transform.Translate(transform.forward * 0.2f);
  52. }
  53. else
  54. {
  55. // 移动结束
  56. isMove = false;
  57. }
  58. }
  59. }
  60. }

上面的代码,我们就不解释了,直接Play运行查看效果

本课程涉及的内容已经共享到百度网盘:https://pan.baidu.com/s/1e1jClK3MnN66GlxBmqoJWA?pwd=b2id

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/520639
推荐阅读
相关标签
  

闽ICP备14008679号