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

复刻Mario Party棋盘:如何用偏移量跟踪位置实现棋子正确移动

问题描述

我正在复刻Mario Party棋盘游戏,需要通过射线检测让棋子能移动到任意状态(开启/关闭)的地格上。目前用DOTween实现固定距离移动,棋子能从初始地格左右移动,但返回第一个地格时会直接跳到最外侧地格。后续还会添加更多地格,布局不固定。

我试过加偏移量变量newPos,初始设为Vector3(0,0,0),每次移动后把新位置赋值给它,但棋子会跑到完全偏离目标的位置。如果去掉newPos,移动能正常运行,但没法跟踪当前位置。怎么通过偏移量正确跟踪棋子位置?

附当前代码:

public class PlayerMovement : MonoBehaviour
{
    Vector3 startPos;
    Vector3 newPos;
    private void Start()
    {
        print(transform.position);
        startPos = gameObject.transform.localPosition;
        newPos = new Vector3(0, 0, 0);

    }

    void Update()
    {

        if (Input.GetKey(KeyCode.LeftArrow))
        {
            gameObject.transform.DOLocalMove(startPos + newPos + new Vector3Int(-5, 0 , 0), 1);
            
            print(gameObject.transform.localPosition);
            newPos = gameObject.transform.localPosition;
        }

        if (Input.GetKey(KeyCode.RightArrow))
        {
            gameObject.transform.DOLocalMove(startPos + newPos + new Vector3Int(5, 0, 0), 1);
           
            print(gameObject.transform.localPosition);

            newPos = gameObject.transform.localPosition;
        }
    }
}
解决方案

问题根源

  1. 动画触发过于频繁:Update中每帧都会响应按键触发DOTween动画,导致多个动画叠加,位置计算混乱。
  2. 位置逻辑错误:startPos + newPos是重复叠加——newPos已经是棋子当前的本地位置,再加初始位置startPos会让目标位置偏移到错误区域。
  3. 位置更新时机错误:DOTween动画是异步执行的,按键时立刻取transform.localPosition拿到的是动画开始前的旧位置,不是最终目标位置。

修正方案

方案1:用地格索引跟踪位置(推荐,适配后续可变布局)

既然后续布局不固定,直接跟踪棋子所在的地格索引比偏移量更灵活,步骤如下:

  1. 把所有地格存入一个数组或列表,方便通过索引获取位置。
  2. 用整数变量记录当前所在地格的索引。
  3. 按键时根据索引计算下一个地格的位置,触发动画,同时更新索引。

示例代码:

public class PlayerMovement : MonoBehaviour
{
    // 把场景中的所有地格拖到这个列表里,顺序要和实际布局一致
    public List<Transform> tiles;
    private int currentTileIndex = 0;
    // 标记是否正在移动,防止重复触发动画
    private bool isMoving = false;

    private void Start()
    {
        // 初始位置直接对齐第一个地格
        transform.localPosition = tiles[currentTileIndex].localPosition;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow) && !isMoving && currentTileIndex > 0)
        {
            currentTileIndex--;
            MoveToTarget(tiles[currentTileIndex].localPosition);
        }

        if (Input.GetKeyDown(KeyCode.RightArrow) && !isMoving && currentTileIndex < tiles.Count - 1)
        {
            currentTileIndex++;
            MoveToTarget(tiles[currentTileIndex].localPosition);
        }
    }

    private void MoveToTarget(Vector3 targetPos)
    {
        isMoving = true;
        // 执行移动动画,动画完成后解除移动标记
        transform.DOLocalMove(targetPos, 1f).OnComplete(() => 
        {
            isMoving = false;
        });
    }
}

方案2:修正偏移量跟踪逻辑(适合临时横向布局)

如果暂时用地格间距固定的横向布局,修改位置计算逻辑:

public class PlayerMovement : MonoBehaviour
{
    // 每个地格的间距
    public int tileSpacing = 5;
    // 记录当前的偏移步数(比如0是初始位置,-1是左移1格,+1是右移1格)
    private int currentOffsetStep = 0;
    private Vector3 startPos;
    private bool isMoving = false;

    private void Start()
    {
        startPos = transform.localPosition;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.LeftArrow) && !isMoving)
        {
            currentOffsetStep--;
            Vector3 targetPos = startPos + new Vector3(currentOffsetStep * tileSpacing, 0, 0);
            MoveToTarget(targetPos);
        }

        if (Input.GetKeyDown(KeyCode.RightArrow) && !isMoving)
        {
            currentOffsetStep++;
            Vector3 targetPos = startPos + new Vector3(currentOffsetStep * tileSpacing, 0, 0);
            MoveToTarget(targetPos);
        }
    }

    private void MoveToTarget(Vector3 targetPos)
    {
        isMoving = true;
        transform.DOLocalMove(targetPos, 1f).OnComplete(() => 
        {
            isMoving = false;
        });
    }
}

关键注意点

  • 用Input.GetKeyDown代替Input.GetKey,确保每次按键只触发一次移动,避免动画叠加。
  • 加isMoving标记,防止移动过程中重复触发动画。
  • 用地格索引的方案更适配后续的可变布局(比如环形、分支形棋盘),推荐优先使用。

内容的提问来源于stack exchange,提问作者Jellyfish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 04:16:10