如何让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
相关产品推荐
相关产品推荐

