赞
踩
本章节我们介绍一下射线。射线就是从一个固定点向一个方向发射出一条直线,在发射过程中需要判断该射线有没有与游戏物体发送碰撞。射线既可以用来检测射击游戏中武器指向目标;又可以判断鼠标是否指向游戏物体。射线的创建方式,一般使用代码来实现。接下来,我们就来创建一个新的“SampleScene3.unity”场景。这里注意的是,射线检测都是以物理系统为基础的,因此只有添加碰撞体组件的游戏物体才能被射线检测到。庆幸的是,在Unity中,创建的Cube或者Sphere,都是自动附带相应的碰撞体组件。
我们创建了三个球体Sphere1,Sphere2,Sphere3,然后我们由Sphere1为起点向X轴负方向(上图左边)发射一条射线。那么这条射线就应该可以检测到Sphere2和Sphere3。
接下来,我们创建一个“RayScript.cs”脚本文件,附加到Sphere1上面,内容如下。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class RayScript : MonoBehaviour
- {
- // Update is called once per frame
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- // 射线的起点(Sphere1的位置)
- Vector3 origin = transform.position;
- // 射线的方向(X轴负方向)
- Vector3 direction = Vector3.left;
- // 射线碰撞的目标对象
- RaycastHit hitInfo;
- // 射线的最大长度
- float maxDistance = 100;
-
- // 创建射线,返回是否检测到碰撞对象
- bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
-
- // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
- if (raycast)
- {
- // 获取碰撞点坐标
- Vector3 point = hitInfo.point;
- Debug.Log("碰撞点坐标:" + point);
-
- // 获取碰撞目标的名称
- string name = hitInfo.collider.name;
- Debug.Log("碰撞对象名称:" + name);
-
- // 获取目标的碰撞体组件
- Collider coll = hitInfo.collider;
-
- //获取目标的Transgorm组件
- Transform trans = hitInfo.transform;
- }
- }
- }
- }
上面的代码非常简单,我们使用四个参数来,通过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方法来模拟上面代码案例中的摄像,增加如下代码
- // 画一条蓝线来模拟射线
- Debug.DrawRay(origin, direction * 100, Color.blue, 100);
接下来,我们重新Play工程,然后按下A键,回到Scene视图(不是Game视图)中查看。
我们可以看到由Sphere1发射出来的一条蓝色的模拟射线了。
由上图我们可知,射线不仅穿过了黄球Sphere2,还穿过了绿球Sphere3了。如何能得到绿色Sphere3呢?这个就需要借助Physics.RaycastAll函数。这个函数与Physics.Raycast函数的使用是相似的,但是返回的结果是不一样的。该函数的返回值是RaycastHit数组。接下来,我们重新修改一下代码,如下所示
- // Update is called once per frame
- void Update()
- {
- if (Input.GetKeyDown(KeyCode.A))
- {
- // 射线的起点(Sphere1的位置)
- Vector3 origin = transform.position;
- // 射线的方向(X轴负方向)
- Vector3 direction = Vector3.left;
- // 射线碰撞的目标对象
- //RaycastHit hitInfo;
- // 射线的最大长度
- float maxDistance = 100;
-
- // 创建射线,返回是否检测到碰撞对象
- //bool raycast = Physics.Raycast(origin, direction, out hitInfo, maxDistance);
- RaycastHit[] hitInfos = Physics.RaycastAll(origin, direction, maxDistance);
-
- // 如果发生碰撞,碰撞信息就被存储到 hitInfo 中
- //if (raycast)
- foreach(RaycastHit hitInfo in hitInfos)
- {
- // 获取碰撞点坐标
- Vector3 point = hitInfo.point;
- Debug.Log("碰撞点坐标:" + point);
-
- // 获取碰撞目标的名称
- string name = hitInfo.collider.name;
- Debug.Log("碰撞对象名称:" + name);
-
- // 获取目标的碰撞体组件
- Collider coll = hitInfo.collider;
-
- //获取目标的Transgorm组件
- Transform trans = hitInfo.transform;
- }
-
- // 画一条蓝线来模拟射线
- Debug.DrawRay(origin, direction * 100, Color.blue, 100);
- }
- }
我们重新Play工程,控制台输出截图如下
在实际游戏开发中,我们有时候需要检测一定范围内是否发生碰撞。比如说,我们要检测周围100米内是否存在某些游戏对象,如果存在,就向其主动发起攻击。此时,我们使用一条射线就无法完成这样的要求。Unity为我们提供了丰富的不同形状的射线检测。这里,我们可以使用Physics.OverlapSphere创建球体碰撞检测,或者使用Physics.OverlapBox创建立方体检测。他们返回的是Collider[]数组,例如我们使用射线检测附近100米内的所有物体
Collider[] colliders = Physics.OverlapSphere(transform.postion, 100.0f);
foreach(Collider collider in colliders){ …… }
最后,我们在介绍一下摄像的另一种使用方式,就是鼠标点击选中场景中的游戏对象。它的原理非常简单,就是由相机位置向鼠标点击位置发射一条射线,然后进行碰撞检测。接下来,我们就来创建一个“RayClickScript.cs”脚本,将其附加到相机上面。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class RayClickScript : MonoBehaviour
- {
- // Update is called once per frame
- void Update()
- {
- // 鼠标左键按下
- if (Input.GetMouseButtonDown(0))
- {
- // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
-
- // 声明一个射线碰撞信息类
- RaycastHit hit;
-
- // 进行碰撞检测
- bool res = Physics.Raycast(ray, out hit);
-
- // 如果产生了碰撞
- if(res){
- Debug.Log("碰撞点:" + hit.point);
- Debug.Log("碰撞目标:" + hit.transform.name);
- }
- }
- }
- }
然后我们Play工程,使用鼠标点击绿球Sphere3,
接下来,我们做一个有趣事情。我们点击Plane平面上一点,然后让绿球Sphere3移动到那一点。如何来完成这件有趣的事情呢?代码改动如下
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class RayClickScript : MonoBehaviour
- {
- // 绿球Sphere3
- private GameObject sphere3;
-
- // 是否移动
- private bool isMove = false;
-
- // 目标点
- private Vector3 target = Vector3.zero;
-
- // Start is called before the first frame update
- void Start()
- {
- // 获取绿球Sphere3
- sphere3 = GameObject.Find("Sphere3");
- }
-
- // Update is called once per frame
- void Update()
- {
- // 鼠标左键按下
- if (Input.GetMouseButtonDown(0))
- {
- // 从相机位置发射一条射线经过屏幕上的鼠标点击位置
- Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
-
- // 声明一个射线碰撞信息类
- RaycastHit hit;
-
- // 进行碰撞检测
- bool res = Physics.Raycast(ray, out hit);
-
- // 如果产生了碰撞
- if (res && hit.transform.name == "Plane")
- {
- // 目标点(Y轴上保持不变)
- isMove = true;
- target = new Vector3(hit.point.x, sphere3.transform.position.y, hit.point.z);
- //Debug.Log("碰撞点:" + hit.point);
- //Debug.Log("碰撞目标:" + hit.transform.name);
- }
- }
-
- // 如果发生碰撞就让绿球移动到目标点
- if (isMove)
- {
- // 绿球朝向目标点
- sphere3.transform.LookAt(target);
-
- // 角色移动到目标点的距离
- float distance = Vector3.Distance(target, sphere3.transform.position);
-
- // 没有到达目标点就一直移动下去
- if (distance > 0.1f)
- {
- // 旋转后向前移动即可
- sphere3.transform.Translate(transform.forward * 0.2f);
- }
- else
- {
- // 移动结束
- isMove = false;
- }
- }
- }
- }
上面的代码,我们就不解释了,直接Play运行查看效果
本课程涉及的内容已经共享到百度网盘:https://pan.baidu.com/s/1e1jClK3MnN66GlxBmqoJWA?pwd=b2id
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。