Dictionary.ContainsKey处理Transform.Forward与硬编码Vector3的差异问题
问题背景
我正在搭建回合制游戏的网格系统,网格用Dictionary<Vector3, Node>存储,Node类管理单位占用状态等逻辑。敌方单位会根据玩家位置生成投射物,投射物实例化后固定沿transform.forward方向移动:下一个目标Node可用就移动,否则销毁并攻击玩家。
判断移动可行性的代码
挂载在投射物的“Linear Movement”组件中:
grid = GridManager.instance.grid; // 从单例缓存网格引用 // 其他代码 public bool NextNodeIsWalkable { get { if (!grid.ContainsKey(moveTarget)) return false; return grid[moveTarget].isWalkable; } }
问题核心:moveTarget计算逻辑
private Vector3 moveTarget { get { return Vector3.right * transform.position.x + Vector3.forward * transform.position.z + moveDirection; } }
moveDirection在OnEnable中被赋值为moveDirection = transform.forward。
异常现象
- 投射物沿垂直方向(Z轴)移动时,逻辑完全正常;
- 沿水平方向(X轴)移动时,偶尔会出现
grid.ContainsKey(moveTarget)返回false(实际对应Node存在),导致投射物错误爆炸; - 交换玩家与敌人位置,反转投射物水平移动方向后,有时
ContainsKey又能正常匹配; - 将
moveDirection改为硬编码的轴向向量(如Vector3.right),无论哪种情况都能正常匹配,即便此时transform.forward的数值和硬编码值看起来完全一致。
问题根源:浮点数精度误差导致的键匹配失败
transform.forward的浮点偏差:
Unity中transform.forward是通过Transform旋转计算得到的向量,即便视觉上是严格的轴向(比如X轴方向的(1,0,0)),实际计算时可能因为浮点数精度问题,出现(1.0000001, 0, 0)或(0.9999999, 0, 0)这类微小偏差。Vector3作为字典键的严格匹配逻辑:
Dictionary<Vector3, Node>中,Vector3的相等判断是逐浮点值严格匹配的。当moveDirection带有精度偏差时,计算出的moveTarget会和网格中存储的Node坐标(比如(2,0,1))产生微小差异,导致ContainsKey无法找到对应键。垂直移动无问题的原因:
垂直移动时,transform.forward对应Z轴方向,旋转计算时Z轴的精度偏差更容易被抵消,或者网格Z轴坐标都是整数,叠加后偏差未影响最终的整数坐标匹配;而水平方向的X轴旋转计算更容易出现浮点偏差。硬编码向量正常的原因:
Unity内置的Vector3.right/Vector3.forward等是精确的轴向向量(比如Vector3.right就是严格的(1,0,0)),没有浮点偏差,因此计算出的moveTarget和网格键完全一致,不会匹配失败。
解决方案
方案1:对moveTarget进行取整处理
因为网格是整数单元格,计算目标位置后将坐标四舍五入到整数:
private Vector3 moveTarget { get { Vector3 rawTarget = Vector3.right * transform.position.x + Vector3.forward * transform.position.z + moveDirection; // 仅保留整数坐标,匹配网格的Node位置 return new Vector3(Mathf.Round(rawTarget.x), rawTarget.y, Mathf.Round(rawTarget.z)); } }
方案2:初始化时将moveDirection转为精确轴向向量
在OnEnable中,把transform.forward转换为无偏差的轴向向量:
void OnEnable() { // 取整后归一化,得到精确的轴向方向 Vector3 roundedForward = new Vector3( Mathf.Round(transform.forward.x), 0, // 忽略Y轴,适配2D网格 Mathf.Round(transform.forward.z) ); moveDirection = Vector3.Normalize(roundedForward); }
方案3:改用整数类型作为网格键
推荐将Dictionary<Vector3, Node>改为Dictionary<Vector2Int, Node>(忽略Y轴,适配2D网格),彻底规避浮点精度问题:
- 存储网格时,用
Vector2Int记录Node的坐标; - 计算目标位置时,将投射物的
transform.position转为Vector2Int,再加上方向对应的Vector2Int(如Vector2Int.right)。
内容的提问来源于stack exchange,提问作者HicorySauce

