如何在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]; } }
关键优化点说明
- 按键时间戳管理:通过
keyPressTimestamps字典记录每个方向按键的按下时间,UpdateKeyTimestamps方法专门处理按键的按下/抬起事件,确保时间戳准确更新。 - 优先级判定逻辑:
GetLatestPressedDirection方法筛选出当前按下按键中时间戳最新的那个,直接对应到移动方向,完美实现"最近按键优先"的需求。 - 逻辑结构修复:将移动方向判定统一放在玩家到达目标点的分支中,避免了原代码中Vertical方向逻辑被错误嵌套的问题,保证移动逻辑的正确性。
- 可扩展性:通过
keyDirectionMap统一管理按键与方向的映射,后续修改按键配置或添加新方向时只需修改该字典即可。
内容的提问来源于stack exchange,提问作者keisasuga
相关产品推荐
相关产品推荐

