You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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

火山引擎 最新活动