Unity中基于Raycast的碰撞检测偏移问题求助
Unity 3D角色Raycast碰撞检测偏移&穿模问题解决
我在Unity开发3D游戏时,用Raycast搭建的碰撞检测系统出现偏移:调整playerSize参数后,角色无法贴合测试立方体,还会出现单侧部分穿入的情况。原代码如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { [SerializeField] private float movementSpeed = 7f; [SerializeField] private float rotationSpeed = 10f; [SerializeField] private float playerSize = .7f; [SerializeField] private GameInput gameInput; private bool isWalking; private void Update() { Vector2 inputVector = gameInput.GetMovementVectorNormalized(); Vector3 moveDir = new Vector3 (inputVector.x, 0f, inputVector.y).normalized; bool canMove = !Physics.Raycast(transform.position, moveDir, playerSize); if (canMove) { transform.position += moveDir * movementSpeed * Time.deltaTime; } isWalking = moveDir != Vector3.ze transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotationSpeed); } }
问题原因
- 单点Raycast的局限性:Raycast是从角色原点发射的单射线,只能检测直线上的单点碰撞,无法模拟角色的实际体积,斜向移动或碰撞体边缘不规则时极易出现检测偏移。
- 未覆盖移动步长:代码仅检测
playerSize长度内的碰撞,但角色每帧实际移动距离为movementSpeed * Time.deltaTime,若该距离大于剩余碰撞间隙,会直接穿模。 - 语法错误:
isWalking = moveDir != Vector3.ze拼写不完整,应为Vector3.zero,会导致编译失败。
解决方案
- 改用SphereCast模拟体积碰撞:SphereCast发射球形检测体,更贴合角色体积,避免单点检测的偏移问题。
- 扩展检测范围:将检测长度设为
角色半径 + 当前帧移动步长,覆盖完整移动路径,防止穿模。 - 修正语法错误:补全
Vector3.zero拼写。
修改后的代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Player : MonoBehaviour { [SerializeField] private float movementSpeed = 7f; [SerializeField] private float rotationSpeed = 10f; [SerializeField] private float playerRadius = .7f; // 改为radius更贴合SphereCast语义 [SerializeField] private GameInput gameInput; [SerializeField] private CapsuleCollider playerCollider; // 引用角色碰撞体 private bool isWalking; private void Update() { Vector2 inputVector = gameInput.GetMovementVectorNormalized(); Vector3 moveDir = new Vector3(inputVector.x, 0f, inputVector.y).normalized; float moveDistance = movementSpeed * Time.deltaTime; // 从碰撞体中心发射SphereCast,检测范围包含移动步长 bool canMove = !Physics.SphereCast(playerCollider.bounds.center, playerRadius, moveDir, moveDistance + playerRadius); if (canMove) { transform.position += moveDir * moveDistance; } isWalking = moveDir != Vector3.zero; transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotationSpeed); } }
额外优化建议
- 若未使用CapsuleCollider,可通过
transform.position + Vector3.up * playerHeight/2设置检测起点(假设角色中心在半高处),避免从模型底部发射检测。 - 给碰撞体添加
PhysicsMaterial,将Dynamic Friction和Static Friction设为0,减少摩擦导致的移动卡顿。
内容的提问来源于stack exchange,提问作者romchyk1219
相关产品推荐
相关产品推荐

