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

如何在Unity中用C#实现2D俯视玩家的优先方向移动?

2D俯视瓦片式移动实现(带最近按键优先级)

核心需求

  • 仅支持X、Y轴直线移动,不允许斜向移动
  • 基于瓦片(Tile-Based)的网格移动逻辑
  • 优先响应最近按下的按键:例如按住W向上移动时,按下A则切换为向左移动,松开A后自动恢复向上移动

现有代码问题

当前提供的代码仅能在单轴范围内实现简单的按键响应,无法处理多轴按键切换时的优先级判定,且Vertical方向的移动逻辑被错误嵌套在Horizontal的else分支中,导致移动逻辑异常。

解决方案:基于按键时间戳的优先级判定

核心思路是记录每个方向按键的按下时间戳,在每次移动节点(玩家到达目标瓦片位置)时,筛选出当前所有处于按下状态的按键,选择时间戳最新的那个作为当前移动方向,以此实现"最近按键优先"的逻辑。

修改后的完整代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float walkSpeed = 5f;
    public float runSpeed = 10f;
    private float currentSpeed;

    public Transform movePoint;
    public LayerMask Barrier;

    private bool isRun;
    // 存储按键与对应的按下时间戳
    private Dictionary<KeyCode, float> keyPressTimestamps = new Dictionary<KeyCode, float>();
    // 定义方向与按键的映射
    private Dictionary<KeyCode, Vector2> keyDirectionMap = new Dictionary<KeyCode, Vector2>()
    {
        { KeyCode.W, Vector2.up },
        { KeyCode.S, Vector2.down },
        { KeyCode.A, Vector2.left },
        { KeyCode.D, Vector2.right }
    };

    void Start()
    {
        movePoint.parent = null;
        currentSpeed = walkSpeed;
    }

    void Update()
    {
        // 处理奔跑状态切换
        isRun = Input.GetKey(KeyCode.LeftShift);
        currentSpeed = isRun ? runSpeed : walkSpeed;

        // 移动玩家到目标点
        transform.position = Vector3.MoveTowards(transform.position, movePoint.position, currentSpeed * Time.deltaTime);

        // 仅当玩家到达当前目标点时,才判定下一次移动方向
        if (Vector3.Distance(transform.position, movePoint.position) <= 0.05f)
        {
            // 更新按键时间戳(按下/抬起事件)
            UpdateKeyTimestamps();

            // 获取当前优先级最高的移动方向
            Vector2? targetDirection = GetLatestPressedDirection();

            if (targetDirection.HasValue)
            {
                Vector3 targetPos = movePoint.position + new Vector3(targetDirection.Value.x, targetDirection.Value.y, 0f);
                // 检测目标位置是否有障碍
                if (!Physics2D.OverlapCircle(targetPos, 0.2f, Barrier))
                {
                    movePoint.position = targetPos;
                }
            }
        }
    }

    /// <summary>
    /// 更新所有方向按键的时间戳:按下时记录时间,抬起时移除记录
    /// </summary>
    private void UpdateKeyTimestamps()
    {
        foreach (var key in keyDirectionMap.Keys)
        {
            if (Input.GetKeyDown(key))
            {
                if (keyPressTimestamps.ContainsKey(key))
                    keyPressTimestamps[key] = Time.time;
                else
                    keyPressTimestamps.Add(key, Time.time);
            }
            else if (Input.GetKeyUp(key))
            {
                if (keyPressTimestamps.ContainsKey(key))
                    keyPressTimestamps.Remove(key);
            }
        }
    }

    /// <summary>
    /// 从当前按下的按键中,找到时间戳最新的方向
    /// </summary>
    /// <returns>优先级最高的移动方向,无按键按下时返回null</returns>
    private Vector2? GetLatestPressedDirection()
    {
        if (keyPressTimestamps.Count == 0)
            return null;

        // 找到时间戳最大的按键
        KeyCode latestKey = KeyCode.None;
        float latestTime = -1f;
        foreach (var pair in keyPressTimestamps)
        {
            if (pair.Value > latestTime)
            {
                latestTime = pair.Value;
                latestKey = pair.Key;
            }
        }

        return keyDirectionMap[latestKey];
    }
}

关键优化点说明

  1. 按键时间戳管理:通过keyPressTimestamps字典记录每个方向按键的按下时间,UpdateKeyTimestamps方法专门处理按键的按下/抬起事件,确保时间戳准确更新。
  2. 优先级判定逻辑:GetLatestPressedDirection方法筛选出当前按下按键中时间戳最新的那个,直接对应到移动方向,完美实现"最近按键优先"的需求。
  3. 逻辑结构修复:将移动方向判定统一放在玩家到达目标点的分支中,避免了原代码中Vertical方向逻辑被错误嵌套的问题,保证移动逻辑的正确性。
  4. 可扩展性:通过keyDirectionMap统一管理按键与方向的映射,后续修改按键配置或添加新方向时只需修改该字典即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:55:00