You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity地牢爬行游戏网格移动碰撞转向后失效问题求助

转向后碰撞检测失效的修复方案

问题根源

当前碰撞检测的射线偏移和方向使用世界空间坐标系(Vector3.forward/Vector3.right),玩家转向后,这些方向并未跟随玩家的局部坐标系同步更新,导致碰撞检测逻辑与实际玩家朝向脱节,进而出现穿墙现象。

修复步骤

  1. 更新射线偏移的坐标系:将射线原点的偏移计算从世界空间改为玩家局部空间,确保射线始终基于玩家当前朝向生成。
  2. 同步移动方向的坐标系:调用碰撞检测时传入玩家局部方向,而非世界方向。
  3. 优化碰撞检测的方向判断:基于玩家局部坐标系判断移动方向,确保射线发射逻辑匹配当前朝向。

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 20:44:53