射线检测是 Unity 中一种高效的空间检测技术,广泛应用于射击游戏、视线检测、物体选取等场景。以下是 Unity 射线系统的全面解析。
射线(Ray) 是由一个起点和一个方向定义的无限延伸的直线,用于检测这条线上是否存在碰撞体。
Ray ray = new Ray(origin, direction);
origin
: 射线起点(Vector3)
direction
: 射线方向(Vector3,需标准化)
存储射线命中信息,包含:
collider
: 命中的碰撞体
point
: 命中点的世界坐标
distance
: 从射线原点到命中点的距离
normal
: 命中表面的法线向量
// 最简单形式
if (Physics.Raycast(transform.position, transform.forward)) {
Debug.Log("检测到物体");
}
// 完整参数形式
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask, queryTriggerInteraction)) {
// 使用hit信息
}
参数说明:
origin
: 射线起点
direction
: 射线方向
hit
: 输出参数,存储命中信息
maxDistance
: 射线最大长度(默认无限)
layerMask
: 层级掩码,指定检测哪些层
queryTriggerInteraction
: 如何处理触发器(忽略/检测)
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit)) {
Debug.Log("点击了: " + hit.collider.name);
}
检测沿射线方向的球体体积
if (Physics.SphereCast(origin, radius, direction, out hit, maxDistance)) {
// 处理球形检测结果
}
检测沿射线方向的盒子体积
if (Physics.BoxCast(center, halfExtents, direction, out hit, orientation, maxDistance)) {
// 处理盒形检测结果
}
检测射线路径上的所有物体
RaycastHit[] hits = Physics.RaycastAll(ray, maxDistance);
foreach (var hit in hits) {
// 处理每个命中对象
}
// 按距离排序
Array.Sort(hits, (x, y) => x.distance.CompareTo(y.distance));
避免GC分配,适合频繁调用
RaycastHit[] results = new RaycastHit[10];
int hitCount = Physics.RaycastNonAlloc(ray, results, maxDistance);
for (int i = 0; i < hitCount; i++) {
// 处理结果
}
void Update() {
if (Input.GetButtonDown("Fire1")) {
Ray ray = new Ray(transform.position, transform.forward);
if (Physics.Raycast(ray, out RaycastHit hit, 100f, enemyLayer)) {
Enemy enemy = hit.collider.GetComponent();
if (enemy != null) {
enemy.TakeDamage(10);
}
}
}
}
bool HasLineOfSight(GameObject target) {
Vector3 direction = target.transform.position - transform.position;
if (!Physics.Raycast(transform.position, direction, out RaycastHit hit, direction.magnitude, obstacleLayer)) {
return true;
}
return hit.collider.gameObject == target;
}
bool isGrounded;
float groundCheckDistance = 0.1f;
void CheckGround() {
isGrounded = Physics.Raycast(transform.position, Vector3.down, groundCheckDistance, groundLayer);
}
层级掩码过滤:始终使用 layerMask 减少不必要的检测
int layerMask = 1 << LayerMask.NameToLayer("Enemy");
Physics.Raycast(..., layerMask);
限制检测距离:设置合理的 maxDistance
避免每帧检测:在需要时才进行射线检测
使用 NonAlloc 方法:减少GC分配
缓存射线结果:如果结果不会频繁变化
// 在Scene视图中绘制射线
Debug.DrawRay(start, direction * length, Color.red, duration);
// 在游戏运行时可视化
void OnDrawGizmos() {
Gizmos.color = Color.green;
Gizmos.DrawRay(transform.position, transform.forward * 10f);
}
射线检测不到物体:
确认物体有Collider
检查layerMask设置
确认射线方向正确(建议使用normalized方向)
性能问题:
减少每帧的射线检测数量
使用更简单的碰撞体
精度问题:
对于快速移动物体,考虑使用SphereCast
调整射线起点偏移