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

Unity 3D基于旋转的网格玩家移动边界限制与对齐优化问题

Unity网格系统玩家移动优化:边界限制与无硬编码对齐

核心问题解决思路

1. 网格边界限制实现

不再直接操作世界坐标移动,而是基于当前网格坐标计算目标位置:

  • 将玩家旋转后的前方向量转换为网格坐标系的偏移(利用方向向量的X/Z分量取整,得到网格轴上的±1/0偏移)
  • 计算目标网格坐标后,判断是否在xRange(网格宽度)和yRange(网格高度)的合法范围内(默认网格坐标从0开始)
  • 仅对合法目标位置执行移动逻辑

2. 避免硬编码旋转角度

通过旋转后的方向向量提取网格偏移,替代角度判断:

  • 玩家旋转步长为90度,旋转后的forward向量在网格X/Z轴上的分量必然是±1或0
  • 直接对方向向量的X/Z分量取整,即可得到网格坐标的偏移量,无需硬编码判断0/90/180/270等角度值

修改后的完整Movement脚本

public class Movement : MonoBehaviour
{
    bool Rotating = false;
    bool Moving = false;
    public Rigidbody rb;
    private Quaternion endRotation;
    [SerializeField] private AnimationCurve curve;
    private Quaternion startRotation;
    private Vector3 startPosition;
    public GameObject Grid;
    private Vector3 endPosition;
    public int xRange;
    public int yRange;
    public float posX; // 当前网格X坐标(0到xRange-1)
    public float posY; // 当前网格Y坐标(0到yRange-1)
    public float desiredDuration = 1f;
    private float elapsedTime;
    float percentageComplete = 0f;  

    private void Awake()
    {
        rb = GetComponent<Rigidbody>();
        var grid = Grid.GetComponent<GridCreation>();
        xRange = grid.width;
        yRange = grid.height;
        // 初始化网格坐标(从初始世界坐标转换)
        posX = Mathf.Round(rb.position.x / 10);
        posY = Mathf.Round(rb.position.z / 10);
    }

    public void TurnRight(InputAction.CallbackContext context)
    { 
        if (context.started && !Rotating && !Moving) 
        {
            percentageComplete = 0;
            startRotation = rb.rotation;
            endRotation = startRotation * Quaternion.Euler(0,90,0);
            Rotating = true;
            elapsedTime = 0;
        }
    }

    public void TurnLeft(InputAction.CallbackContext context)
    { 
        if (context.started && !Rotating && !Moving) 
        {
            percentageComplete = 0;
            startRotation = rb.rotation;
            endRotation = startRotation * Quaternion.Euler(0,-90,0);
            Rotating = true;
            elapsedTime = 0;
        }
    }

    // 基于网格坐标的移动逻辑
    public void Forward(InputAction.CallbackContext context)
    { 
        if (context.started && !Rotating && !Moving) 
        {
            // 计算旋转后的前方向量(仅保留X/Z轴分量)
            Vector3 forwardDir = rb.rotation * Vector3.forward;
            forwardDir.y = 0;
            forwardDir.Normalize();

            // 转换为网格坐标偏移(因旋转步长90度,分量必为±1或0)
            int gridOffsetX = Mathf.RoundToInt(forwardDir.x);
            int gridOffsetY = Mathf.RoundToInt(forwardDir.z);

            // 计算目标网格坐标
            float targetPosX = posX + gridOffsetX;
            float targetPosY = posY + gridOffsetY;

            // 边界检查:确保目标在网格范围内
            if (targetPosX >= 0 && targetPosX < xRange && targetPosY >= 0 && targetPosY < yRange)
            {
                percentageComplete = 0;
                startPosition = rb.position;
                // 将网格坐标转换为世界坐标(每格10单位,Y固定为1)
                endPosition = new Vector3(targetPosX * 10, 1, targetPosY * 10);
                Moving = true;
                elapsedTime = 0;

                // 提前更新网格坐标(避免移动中计算误差)
                posX = targetPosX;
                posY = targetPosY;
            }
        }
    }
            
    private void Update() 
    {
        if (Rotating)
        {
            if (percentageComplete < 1) 
            {
                elapsedTime += Time.deltaTime;
                percentageComplete = elapsedTime / desiredDuration;
                rb.rotation = Quaternion.Lerp(startRotation, endRotation, curve.Evaluate(percentageComplete));
            }
                       
            if (percentageComplete > 1) 
            {
                Rotating = false;
            }
        }

        if (Moving)
        {
            if (percentageComplete < 1) 
            {
                elapsedTime += Time.deltaTime;
                percentageComplete = elapsedTime / desiredDuration;
                rb.position = Vector3.Lerp(startPosition, endPosition, curve.Evaluate(percentageComplete));
            }

            if (percentageComplete > 1) 
            {
                Moving = false;
            }
        }
    }
}

关键优化点说明

  1. 初始网格坐标初始化:在Awake中直接从世界坐标转换,避免移动后才计算的误差
  2. 方向向量转网格偏移:利用旋转后的forward向量的归一化分量,自动适配所有90度旋转方向
  3. 提前边界检查:在移动前就判断目标网格坐标是否合法,无效输入直接忽略
  4. 网格坐标提前更新:移动开始时就更新posX/posY,避免移动结束后从世界坐标取整的精度问题

内容的提问来源于stack exchange,提问作者Chris W.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:48:17