Unity中如何实现高精度2D网格移动系统
Unity 2D 垂直方向严格网格移动实现方案
针对高精度网格位移需求,核心是从输入、移动、结算全流程规避位置偏移,不要靠实时输入直接拖拽角色,所有位移以固定网格步长为单位,移动过程锁输入、结束强制对齐,彻底消除浮点误差。
核心实现逻辑
- 固定网格步长:单格尺寸统一配置,每次垂直移动的距离严格等于单格边长,从根源避免位移偏差
- 状态锁机制:只有角色完全静止、对齐网格点时才响应新的移动输入,移动过程中忽略其他操作指令
- 强制对齐校验:每次移动结算时把坐标吸附到最近网格点,抵消浮点运算累计的位置误差
- 移动接口统一:用刚体就全程走刚体移动API,不用物理就全程改transform,不要混写避免位置抖动
具体实现步骤
1. 基础组件配置
给移动角色挂载以下组件:
Rigidbody2D:重力缩放设为0,冻结Z轴旋转、X轴位移(仅做垂直移动时锁X轴,后续加水平移动再放开)Collider2D(Box/Circle均可):碰撞体尺寸匹配角色大小,建议刚好占满单格空间- 若不需要物理碰撞,可省略刚体组件,直接操作Transform即可
2. 可直接复用的实现代码
using UnityEngine; public class VerticalGridMover : MonoBehaviour { [Header("移动配置")] [Tooltip("单个网格的边长,建议和Unity单位统一,默认1单位=1格")] public float gridSize = 1f; [Tooltip("格子间移动的速度")] public float moveSpeed = 6f; [Tooltip("障碍检测层,用来阻挡移动")] public LayerMask obstacleMask; private Rigidbody2D _rb; private bool _isMoving; private Vector2 _targetPosition; void Awake() { _rb = GetComponent<Rigidbody2D>(); // 初始位置强制对齐网格,避免出生点偏移 SnapToNearestGrid(); } void Update() { // 非移动状态才检测输入 if (!_isMoving) { // 用GetAxisRaw拿到离散的输入值,只会返回-1/0/1,无渐变延迟 float verticalInput = Input.GetAxisRaw("Vertical"); if (Mathf.Abs(verticalInput) > 0.01f) { Vector2 moveDir = new Vector2(0, Mathf.Sign(verticalInput)); Vector2 nextPos = (Vector2)transform.position + moveDir * gridSize; // 检测目标格子是否有障碍,无遮挡才触发移动 if (!Physics2D.OverlapBox(nextPos, Vector2.one * 0.9f, 0, obstacleMask)) { _targetPosition = nextPos; _isMoving = true; } } } } void FixedUpdate() { if (!_isMoving) return; // 刚体移动保证碰撞检测正常 _rb.MovePosition(Vector2.MoveTowards( _rb.position, _targetPosition, moveSpeed * Time.fixedDeltaTime )); // 到达目标点后结束移动,强制对齐位置消除浮点误差 if (Vector2.Distance(_rb.position, _targetPosition) < 0.001f) { _rb.MovePosition(_targetPosition); _isMoving = false; } } // 坐标吸附到最近网格点的工具方法 void SnapToNearestGrid() { float x = Mathf.Round(transform.position.x / gridSize) * gridSize; float y = Mathf.Round(transform.position.y / gridSize) * gridSize; transform.position = new Vector3(x, y, transform.position.z); } }
3. 精度优化注意事项
- 障碍检测的检测框尺寸设为单格的90%即可,不要用满格大小,避免相邻碰撞体边缘贴合导致的误判
- 不要用
Input.GetAxis做输入检测,它的输入值是渐变的,容易导致移动延迟、重复触发 - 无论用什么方式移动,最后一步的位置强制吸附不能省,浮点运算的累计误差跑几十次移动就会出现明显的网格错位
- 不用物理系统的话,把
FixedUpdate里的刚体移动逻辑挪到Update里,直接改transform.position即可,吸附逻辑完全复用 - 移动动画直接绑定
_isMoving状态触发,不要靠速度值判定动画状态,避免浮点误差导致的动画闪切
后续如果要扩展水平移动,只需要补充水平输入的检测逻辑、放开刚体X轴位移限制即可,整套网格对齐、状态锁的逻辑不需要修改。
内容的提问来源于stack exchange,提问作者Trevil Devil
相关产品推荐
相关产品推荐

