You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Unity中的2D网格系统支持负坐标?

解决Unity 2D网格系统支持负坐标的问题

方案1:坐标偏移适配数组(无需修改核心数据结构)

你之前尝试的偏移思路是可行的,空指针大概率是数组初始化尺寸错误或坐标越界导致的。具体实现步骤:

  • 先获取Tilemap的完整边界:通过Tilemap.cellBounds拿到所有瓦片的最小/最大x、y坐标
  • 计算偏移量:把最小负坐标转为0的偏移值,即offsetX = -tileBounds.xMin,offsetY = -tileBounds.yMin
  • 初始化数组时,尺寸要覆盖整个坐标范围:(tileBounds.xMax - tileBounds.xMin + 1) × (tileBounds.yMax - tileBounds.yMin + 1)
  • 访问数组时,将原始坐标(x,y)转换为数组索引:arrayX = x + offsetX,arrayY = y + offsetY

示例代码:

// 初始化网格前先获取Tilemap边界
Tilemap targetTilemap = GetComponent<Tilemap>();
BoundsInt tileBounds = targetTilemap.cellBounds;

int minX = tileBounds.xMin;
int maxX = tileBounds.xMax;
int minY = tileBounds.yMin;
int maxY = tileBounds.yMax;

// 计算偏移量,将负坐标映射为非负数组索引
int offsetX = -minX;
int offsetY = -minY;

// 初始化对应尺寸的数组
bool[,] collisionGrid = new bool[maxX - minX + 1, maxY - minY + 1];

// 填充碰撞网格数据
for (int x = minX; x <= maxX; x++)
{
    for (int y = minY; y <= maxY; y++)
    {
        int arrayX = x + offsetX;
        int arrayY = y + offsetY;
        // 获取当前瓦片的碰撞状态
        Tile tile = targetTilemap.GetTile<Tile>(new Vector3Int(x, y, 0));
        collisionGrid[arrayX, arrayY] = tile != null && tile.colliderType != Tile.ColliderType.None;
    }
}

// 检查指定坐标是否有碰撞瓦片的方法
public bool HasCollision(int x, int y)
{
    int arrayX = x + offsetX;
    int arrayY = y + offsetY;
    // 先判断索引是否在数组范围内,避免越界空指针
    if (arrayX < 0 || arrayX >= collisionGrid.GetLength(0) 
        || arrayY < 0 || arrayY >= collisionGrid.GetLength(1))
        return false;
    return collisionGrid[arrayX, arrayY];
}

空指针排查要点:

  • 确保数组初始化时的尺寸计算正确,漏掉+1会导致边界坐标越界
  • 访问数组前必须判断索引是否在合法范围内
  • 调用GetTile后要检查返回值是否为null,避免后续访问tile.colliderType时抛出空指针

方案2:改用Dictionary存储网格(直接支持正负坐标)

如果不想处理坐标偏移,可以用Dictionary<Vector2Int, bool>(或自定义瓦片数据类)存储坐标与碰撞状态的映射,无需转换坐标:

示例代码:

Tilemap targetTilemap = GetComponent<Tilemap>();
Dictionary<Vector2Int, bool> collisionDict = new Dictionary<Vector2Int, bool>();

BoundsInt tileBounds = targetTilemap.cellBounds;
for (int x = tileBounds.xMin; x <= tileBounds.xMax; x++)
{
    for (int y = tileBounds.yMin; y <= tileBounds.yMax; y++)
    {
        Vector2Int coord = new Vector2Int(x, y);
        Tile tile = targetTilemap.GetTile<Tile>(new Vector3Int(x, y, 0));
        if (tile != null)
        {
            collisionDict[coord] = tile.colliderType != Tile.ColliderType.None;
        }
    }
}

// 检查碰撞的方法
public bool HasCollision(int x, int y)
{
    return collisionDict.TryGetValue(new Vector2Int(x, y), out bool isCollidable) && isCollidable;
}

这个方案的优势是无需坐标转换,只存储存在瓦片的坐标,节省内存,且TryGetValue不会因无效坐标抛出空指针异常。


方案3:直接调用Tilemap API(无需维护自定义网格)

如果你的需求只是检查指定坐标是否存在碰撞瓦片,完全可以跳过自定义网格,直接用Unity原生API查询,支持任意正负坐标:

public bool HasCollision(Tilemap tilemap, int x, int y)
{
    Vector3Int tileCoord = new Vector3Int(x, y, 0);
    Tile tile = tilemap.GetTile<Tile>(tileCoord);
    return tile != null && tile.colliderType != Tile.ColliderType.None;
}

这种方式最简洁,避免了自定义网格带来的坐标转换、空指针等问题,适合仅需碰撞检查的场景。

内容的提问来源于stack exchange,提问作者Zares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 17:40:32