Unity网格移动:如何按2D方向优雅计算含障碍物的目标Cell?
优雅实现Unity网格移动的目标单元格计算
这个需求在网格类游戏里太常见了,咱们可以用逐格探测的方式来实现,逻辑清晰还不容易出错,完美贴合你要的"碰到障碍物就停在前面"的需求。
核心思路
沿着指定的移动方向,从玩家当前单元格开始,逐个检查下一个单元格:
- 如果下一个单元格在地图范围内且不是障碍物,就把它设为新的目标
- 一旦碰到障碍物或者超出地图边界,就停止探测,返回最后一个可行的单元格
完整代码实现
首先,咱们需要先能获取玩家当前所在的网格坐标(这个是基础),然后实现GetTargetCell方法:
// 先实现获取玩家当前单元格坐标的辅助方法 private (int x, int y) GetPlayerCurrentCellCoordinates() { // 替换成你的玩家Transform引用 Vector3 playerPos = playerTransform.position; // 因为咱们的单元格位置是(x, 0, y),所以取x和z的整数部分对应网格坐标 int gridX = Mathf.RoundToInt(playerPos.x); int gridY = Mathf.RoundToInt(playerPos.z); // 确保坐标不会超出地图范围,避免数组越界 gridX = Mathf.Clamp(gridX, 0, MAP_SIZE - 1); gridY = Mathf.Clamp(gridY, 0, MAP_SIZE - 1); return (gridX, gridY); } // 你需要的GetTargetCell方法 public Cell GetTargetCell(Vector2 movementDirection) { // 获取玩家当前的网格坐标 (int currentX, int currentY) = GetPlayerCurrentCellCoordinates(); // 把Vector2方向转换成网格坐标的增量(x和y轴的步长) int xDelta = 0; int yDelta = 0; switch (movementDirection) { case Vector2 dir when dir == Vector2.up: yDelta = 1; break; case Vector2 dir when dir == Vector2.down: yDelta = -1; break; case Vector2 dir when dir == Vector2.left: xDelta = -1; break; case Vector2 dir when dir == Vector2.right: xDelta = 1; break; } // 初始目标就是玩家当前的单元格 int targetX = currentX; int targetY = currentY; // 开始逐格探测 while (true) { // 计算下一个单元格的坐标 int nextX = targetX + xDelta; int nextY = targetY + yDelta; // 检查是否超出地图边界 if (nextX < 0 || nextX >= MAP_SIZE || nextY < 0 || nextY >= MAP_SIZE) { break; } // 获取下一个单元格 Cell nextCell = mapCells[nextX, nextY]; // 如果是障碍物,停止前进 if (nextCell.IsObstacle) { break; } // 下一个单元格可行,更新目标坐标 targetX = nextX; targetY = nextY; } // 返回最终的目标单元格 return mapCells[targetX, targetY]; }
为什么这个方式优雅?
- 逻辑直观:逐格检查完全符合玩家对网格移动的预期,不会出现穿墙或者跳过障碍物的bug
- 边界安全:自动处理地图边缘的情况,不会触发数组越界异常
- 扩展性强:如果以后要支持斜向移动(比如左上/右下),只需要在switch里加对应的方向判断,给xDelta和yDelta赋值就行
- 低耦合:方法只依赖必要的参数和地图数据,和其他系统的关联很少,维护起来很方便
额外提示
如果你的网格单元格大小不是1x1,那在转换玩家世界坐标到网格坐标的时候,需要除以单元格的尺寸,比如:
float cellSize = 2f; // 假设每个单元格是2单位大小 int gridX = Mathf.RoundToInt(playerPos.x / cellSize); int gridY = Mathf.RoundToInt(playerPos.z / cellSize);
内容的提问来源于stack exchange,提问作者Question3r




