2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Ray detection, as the name suggests, uses a ray to detect whether it hits an object/objects
Radiographic testing consists of two parts: RaysandDetection
Scene rays can be seen in many places in daily life, such asflashlight,ppt laser page turning pen, in theCyclops
A ray consists of a starting point, a direction and a distance, namely: origin
, direction
anddistance
In physics, the distance of a ray is infinite, so a ray in physics has only one starting point and one direction. In the game, the maximum distance of a ray is also limited by the system. Generally, we define a custom distance, such as 1000.0 meters. The following is a description of Ray.
In Unity, Ray is a structure, and the basic members of the structure volume includeorigin
, direction
andGetPoint
. That is, the starting point, direction, and position of a point at a certain distance along the ray. The following is the code for Ray in Unity
using System;
namespace UnityEngine
{
public struct Ray : IFormattable
{
public Ray(Vector3 origin, Vector3 direction);
public Vector3 origin { get; set; } // 起点(向量)
public Vector3 direction { get; set; }// 方向(向量)
public Vector3 GetPoint(float distance);// 沿着射线一定距离的点(向量)
public override string ToString();
public string ToString(string format);
public string ToString(string format, IFormatProvider formatProvider);
}
}
According to the above code, we can see that we can directly use the Ray constructor to construct a ray, for example:
Ray ray = new Ray(Vector3.zero, Vector3.forward); // 射线的起点 + 射线的方向
According to the above code, we can see that the starting point of the ray is the origin of the world coordinates in Unity (0,0,0), and the direction of the ray is the forward direction of the world coordinates.
In reality, our laser pointer and flashlight are visible, but the rays in Unity are invisible. Therefore, if we want to display the rays, we can use the following methods:
Debug.DrawRay()
Display rayLineRenderer
The component draws the rayJust constructing or displaying the rays is meaningless, which is like holding a tool but not using it to do the work.
How to use rays to perform object detection.
Think about it carefully: In daily life, our laser pointer or flashlight can emit a light spot on the wall or illuminate a certain place. This shows that the wall is interactive, in other words, the ray hits the object/detects the object. Use the following API in Unity to determine whether the object is detected.
// 基础版API
public static bool Raycast(Ray ray);
public static bool Raycast(Vector3 origin, Vector3 direction, float maxDistance, int layerMask);
public static bool Raycast(Ray ray, out RaycastHit hitInfo);
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance);
// 常用版API
public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);
public static bool Raycast(Ray ray, float maxDistance);
To use ray tracing, you need to use Unity'sPhysics
Library, which contains static functions related to physics
Seeing so many overloaded functions above, we often don't know which one to use. In fact, only the basic ones are needed. The ones with more parameters actually use different overloaded parameters according to the needs.
First look at the basic version of the API public static bool Raycast(Ray ray);
Based on the return value, we can know whether the ray hits an object. If it hits, it returns true, and if it does not hit, it returns false.
Ray Detection in Another APIpublic static bool Raycast(Ray ray, out RaycastHit hitInfo);
There is one more parameter inhitInfo
The hitInfo parameter is the information structure of the object hit by the ray. This structure is relatively large, which means it contains more information. This is different from the result of the ray detection hitting the object in Unreal Engine (FHitResult
) is similar. From a macroscopic or practical point of view, when a ray hits an object, we can obtain information about the object itself and the point of impact.
Getting the information of an object is easy to understand: such as the name of the object, the object's transfrom component, the object's tag...
Get the information of the hit point, that is, the point where the ray hits the object (hit point): such as the coordinates of the point,Normal(normal), normal plane, the pointVertex Color…
The following areRaycastHit
The information of the structure, the commonly used ones have been commented. There are also many commonly used ones without comments, such aslightmapCoord
These are lightmap coordinates, used for rendering.
namespace UnityEngine
{
public struct RaycastHit
{
public Collider collider { get; } // 碰撞器
public int colliderInstanceID { get; }
public Vector3 point { get; set; } // 击中的点(命中点)
public Vector3 normal { get; set; } // 命中点的法线
public Vector3 barycentricCoordinate { get; set; } // 重心坐标
public float distance { get; set; } // 命中点距离射线起点的距离
public int triangleIndex { get; }
public Vector2 textureCoord { get; }
public Vector2 textureCoord2 { get; }
public Transform transform { get; } // Transform组件
public Rigidbody rigidbody { get; } // 刚体组件
public ArticulationBody articulationBody { get; }
public Vector2 lightmapCoord { get; }
public Vector2 textureCoord1 { get; }
}
}
That is to sayHitInfo
Saves the information about the object we hit, we can do more with this information
In Unity,layerMask
It is an object layer selection mechanism used to control physical collision, raycasting, ray detection, etc.layerMask
, you can specify which layers should be included or excluded from these operations.
Let’s go back to the common ray detection API public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);
middle.
In the above API, maxDistance is self-explanatory, it is the distance of the ray, and the system also has a maximum value set toMathf.Infinity
. There is also a parameter layerMask in this APi, which is the layer mask.
To distinguish game objects in Unity, we can add tags to distinguish game objects. However, tags are more troublesome, and we need to enter the tag name manually, which is easy to make mistakes. At the same time, because it is a string, the speed is slower when it is calculated at the bottom layer of Unity. In Unity, there is a concept of layers.
The layerMask in Unity contains 32 layers, some of which are already used by the system, such asPlayer
layer, UI
Layer. There are still many layers that are not used by the system. We can add layers and then add child labor layers to game objects to classify them.
How do I add a layer? Click Layer on the Insepector panel of any object, then clickAddLayerThat's it, then manually specify the layer for the objects whose levels need to be modified.
How to use after manually adding a Layer.
existpublic static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);
middlelayereMask
We understand that it makes aint
However, we cannot fill in the numbers directly. The filling rules use shift operations.
How to fill in layerMask:
Get the Layer Mask value:
1 << LayerMask.NameToLayer("LayerName")
. This returns an integer value representing the layerMask of the layer.Combining multiple layers:
|
.For example,layerMask = LayerMask.GetMask("Layer1", "Layer2")
A layerMask will be created, including "Layer1" and "Layer2".Exclusion Layer:
^
to exclude specific layers. For example,layerMask = ~LayerMask.GetMask("ExcludeLayer")
。Check Layer:
layerMask.value & (1 << gameObject.layer)
If the result is not 0, it means the object is in the layerMask.// 示例代码
// 创建一个包含Layer1和Layer2的layerMask
int layerMask = LayerMask.GetMask("Layer1", "Layer2");
// 排除Layer3
layerMask = ~LayerMask.GetMask("Layer3");
// 使用layerMask进行射线检测
RaycastHit hit;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
// 处理射线击中的对象
}
The following is the code using the hitInfo parameter without and with hitInfo
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayCast : MonoBehaviour
{
/
// 构建一条射线 : 射线产生的起始点 + 射线的方向.
private Ray ray1 = new Ray(Vector3.zero, Vector3.forward);
// 射线距离
private float rayDistance = 100.0f;
// 击中的判定结果
bool hitResult = false;
// 射线击中的物体
private RaycastHit hitInfo;
void Start()
{
// 不含有hitInfo的函数
bool result1 = Physics.Raycast(Vector3.zero + new Vector3(0,0,10), Vector3.forward, 1000.0f, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
if (result1)
{
Debug.Log("射线击中物体");
}
// 含有hitInfo的函数
hitResult = Physics.Raycast(ray1, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
if (hitResult == true)
{
print(hitInfo.collider.name);
print(hitInfo.transform);
print(hitInfo.point);
}
}
}
Use ray detection to realize a rotating laser pen. When encountering an object, the ray length decreases. It is necessary to draw the ray
Skills needed: Ray Detection + Rotation + LineRender
Following is the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateRay : MonoBehaviour
{
// LinerRender组件
private LineRenderer lineRenderer = null;
// 构建一条射线 : 射线产生的起始点 + 射线的方向.
private Ray ray = new Ray(Vector3.zero, Vector3.forward);
// 射线距离
private float rayDistance = 1000.0f;
// 击中的判定结果
bool hitResult = false;
// 射线击中的物体
private RaycastHit hitInfo;
void Start()
{
// 添加线条绘制组件
lineRenderer = this.gameObject.AddComponent<LineRenderer>();
InitLineRenderer(lineRenderer);
// 设置射线的起点和方向
ray.origin = this.transform.position;
ray.direction = this.transform.forward;
}
// Update is called once per frame
void Update()
{
// 重新设置射线的位置
ray.origin = this.transform.position;
ray.direction = this.transform.forward;
// 旋转游戏对象 -- 每秒旋转60°
Quaternion quaternion = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);
this.transform.rotation *= quaternion;
// 判断击中的物体
hitResult = Physics.Raycast(ray, out hitInfo, rayDistance, 1 << LayerMask.NameToLayer("Default"), QueryTriggerInteraction.UseGlobal);
if (hitResult == true)
{
print(hitInfo.collider.name);
}
// 显示并更新射线
UpdateLineRendererByRay(lineRenderer, ray, hitResult,hitInfo, rayDistance);
}
/// <summary>
/// 初始化线条渲染组件
/// </summary>
void InitLineRenderer(LineRenderer lineRenderer)
{
// lineRenderer = this.gameObject.AddComponent<LineRenderer>();
lineRenderer.positionCount = 2;
lineRenderer.startWidth = 0.2f;
lineRenderer.endWidth = 0.2f;
lineRenderer.startColor = Color.red;
lineRenderer.endColor = Color.green;
}
/// <summary>
/// 击中物体的时候修改射线的长度
/// </summary>
/// <param name="lineRenderer">lineRenderer组件</param>
/// <param name="ray">射线</param>
/// <param name="hitResult">是否命中物体</param>
/// <param name="hitInfo">命中物体信息</param>
/// <param name="rayDistance">射线距离</param>
void UpdateLineRendererByRay(LineRenderer lineRenderer,Ray ray, bool hitResult, RaycastHit hitInfo, float rayDistance)
{
if (lineRenderer == null || lineRenderer.positionCount < 2)
{
Debug.Log("LineRender组件不可以使用");
return;
}
// 修改起点位置
lineRenderer.SetPosition(0, ray.origin);
// 修改终点位置
if (hitResult == true)
{
lineRenderer.SetPosition(1, hitInfo.point);
}
else
{
lineRenderer.SetPosition(1, ray.GetPoint(rayDistance));
}
}
}
Optimized code
using UnityEngine;
public class RotateRay : MonoBehaviour
{
private LineRenderer lineRenderer;
private Ray ray;
private float rayDistance = 1000.0f;
private RaycastHit hitInfo;
void Start()
{
lineRenderer = this.gameObject.AddComponent<LineRenderer>();
InitLineRenderer(lineRenderer);
ray = new Ray(Vector3.zero, Vector3.forward);
}
void Update()
{
UpdateRayPosition();
RotateObject();
PerformRaycast();
UpdateLineRenderer();
}
void InitLineRenderer(LineRenderer lineRenderer)
{
lineRenderer.positionCount = 2;
lineRenderer.startWidth = 0.2f;
lineRenderer.endWidth = 0.2f;
lineRenderer.startColor = Color.red;
lineRenderer.endColor = Color.green;
}
void UpdateRayPosition()
{
ray.origin = this.transform.position;
ray.direction = this.transform.forward;
}
void RotateObject()
{
Quaternion rotation = Quaternion.AngleAxis(60f * Time.deltaTime, this.transform.up);
this.transform.rotation *= rotation;
}
void PerformRaycast()
{
int layerMask = 1 << LayerMask.NameToLayer("Default");
hitInfo = new RaycastHit(); // 初始化hitInfo,避免未击中时的错误
Physics.Raycast(ray, out hitInfo, rayDistance, layerMask, QueryTriggerInteraction.UseGlobal);
}
void UpdateLineRenderer()
{
if (lineRenderer == null || lineRenderer.positionCount < 2)
{
Debug.LogError("LineRenderer component is not available or not properly initialized.");
return;
}
lineRenderer.SetPosition(0, ray.origin);
lineRenderer.SetPosition(1, hitInfo.collider != null ? hitInfo.point : ray.GetPoint(rayDistance));
}
}
The effect diagram is as follows
using UnityEngine;
public class CameraRay: MonoBehaviour
{
// 射线距离
private float rayDistance = 1000.0f;
// 特效Prefab--外部可以自定义特效
public GameObject effectPrefab;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// 从摄像机发出一条射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hitInfo;
// 如果射线击中了指定层级的物体
if (Physics.Raycast(ray, out hitInfo, rayDistance, LayerMask.GetMask("Wall")))
{
// 生成特效 --格局法线来计算特效位置
GameObject effectObject = Instantiate(effectPrefab, hitInfo.point, Quaternion.LookRotation(hitInfo.normal));
// 销毁特效,参数为延迟时间
Destroy(effectObject, EffectPrefab.GetComponent<ParticleSystem>().main.duration);
}
}
}
}