Unity地牢爬行游戏网格移动碰撞转向后失效问题求助
转向后碰撞检测失效的修复方案
问题根源
当前碰撞检测的射线偏移和方向使用世界空间坐标系(Vector3.forward/Vector3.right),玩家转向后,这些方向并未跟随玩家的局部坐标系同步更新,导致碰撞检测逻辑与实际玩家朝向脱节,进而出现穿墙现象。
修复步骤
- 更新射线偏移的坐标系:将射线原点的偏移计算从世界空间改为玩家局部空间,确保射线始终基于玩家当前朝向生成。
- 同步移动方向的坐标系:调用碰撞检测时传入玩家局部方向,而非世界方向。
- 优化碰撞检测的方向判断:基于玩家局部坐标系判断移动方向,确保射线发射逻辑匹配当前朝向。
修改后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public bool smoothTransition = true; public float transitionSpeed = 5f; public float transitionRotationSpeed = 500f; public float rayLength = 1f; Vector3 targetGridPos; Vector3 prevTargetGridPos; Vector3 targetRotation; float rayOffsetX = 0.7f; float rayOffsetY = 0.7f; float rayOffsetZ = 0.7f; Vector3 xOffset; Vector3 yOffset; Vector3 zOffset; Vector3 zAxisOriginA; Vector3 zAxisOriginB; Vector3 xAxisOriginA; Vector3 xAxisOriginB; private void Start() { targetGridPos = Vector3Int.RoundToInt(transform.position); } private void Update() { yOffset = transform.position + Vector3.up * rayOffsetY; // 改用玩家局部方向计算偏移,适配转向 zOffset = transform.forward * rayOffsetZ; xOffset = transform.right * rayOffsetX; zAxisOriginA = yOffset + xOffset; // 玩家右侧上方检测点 zAxisOriginB = yOffset - xOffset; // 玩家左侧上方检测点 xAxisOriginA = yOffset + zOffset; // 玩家前方上方检测点 xAxisOriginB = yOffset - zOffset; // 玩家后方上方检测点 } private void FixedUpdate() { MovePlayer(); } void MovePlayer() { prevTargetGridPos = targetGridPos; Vector3 targetPosition = targetGridPos; // 处理旋转角度循环 if (targetRotation.y > 270f && targetRotation.y < 361f) targetRotation.y = 0f; if (targetRotation.y < 0f) targetRotation.y = 270f; if (!smoothTransition) { transform.position = targetPosition; transform.rotation = Quaternion.Euler(targetRotation); } else { transform.position = Vector3.MoveTowards(transform.position, targetPosition, Time.deltaTime * transitionSpeed); transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(targetRotation), Time.deltaTime * transitionRotationSpeed); } } public void RotateLeft() { if (AtRest) targetRotation -= Vector3.up * 90f; } public void RotateRight() { if (AtRest) targetRotation += Vector3.up * 90f; } public void MoveForward() { if (CanMove(transform.forward)) { if (AtRest) targetGridPos += transform.forward; } } public void MoveBackward() { if (CanMove(transform.back)) { if (AtRest) targetGridPos -= transform.forward; } } public void MoveLeft() { if (CanMove(transform.left)) { if (AtRest) targetGridPos -= transform.right; } } public void MoveRight() { if (CanMove(transform.right)) { if (AtRest) targetGridPos += transform.right; } } bool CanMove(Vector3 direction) { direction = direction.normalized; // 前后移动时,用左右检测点发射射线 if (Mathf.Abs(Vector3.Dot(direction, transform.forward)) > 0.9f) { if (Physics.Raycast(zAxisOriginA, direction, rayLength)) return false; if (Physics.Raycast(zAxisOriginB, direction, rayLength)) return false; } // 左右移动时,用前后检测点发射射线 else if (Mathf.Abs(Vector3.Dot(direction, transform.right)) > 0.9f) { if (Physics.Raycast(xAxisOriginA, direction, rayLength)) return false; if (Physics.Raycast(xAxisOriginB, direction, rayLength)) return false; } return true; } bool AtRest { get { return (Vector3.Distance(transform.position, targetGridPos) < 0.05f) && (Vector3.Distance(transform.eulerAngles, targetRotation) < 0.05f); } } }
关键修改说明
- 射线偏移计算:
zOffset和xOffset改用transform.forward和transform.right,确保检测点始终位于玩家当前的前后左右方向。 - 移动方向传递:
MoveForward等方法调用CanMove时传入玩家局部方向,而非固定世界方向。 - 碰撞检测逻辑:通过点积判断移动方向与玩家局部坐标系的关系,确保射线发射位置匹配移动需求。
内容的提问来源于stack exchange,提问作者Nush
相关产品推荐
相关产品推荐

