TileMap是一个强大的工具,它允许创建一个瓦片网格,可以在这个网格上渲染瓦片,而不是手动放置单个带有精灵的游戏对象。它内置了许多可能需要手动编写的功能,比如将坐标映射到地图上的特定单元格。使用TileMap的一个更酷的特点是,不需要自己编写编辑器来绘制瓦片!认为会选择避免这样做。但是有没有发现自己处于想要获取TileMap上所有绘制的瓦片的情况?可能会发现这并不像希望的那样明显!
正如提到的,TileMap可以为做很多事情。特别是,发现自己想要获取所有与精灵关联的单元格,以及那个精灵实际上是什么。可以使用内置的方法GetSprite来实现这一点:
C#
var cellPosition = new Vector3Int(x, y, 0);
var sprite = tilemap.GetSprite(cellPosition);
足够简单。但是怎么知道要使用哪个坐标作为x和y呢?仅仅在x和y轴上从0到一个非常高的数字,然后用两个循环,希望这足够好?可以做得更好!TileMap类有一个方便的属性叫做cellBounds。这给一个整数矩形,定义了所有已经被修改的TileMap单元格的边界:
C#
tilemap.cellBounds
如果发现自己在TileMap上做了很多编辑,可能需要擦除某些部分以专注于另一个区域。如果这是常见的,可能会从调用CompressBounds()中受益,以将其缩小到当前分配的单元格:
C#
tilemap.CompressBounds()
有了所有这些,可以探讨如何将它们全部结合起来以获得所需要的!
如果有敏锐的眼光,可能已经意识到想要使用两个循环来遍历TileMap上的cellBounds的x和y轴,以获取感兴趣的单元格。完全正确!但是这里有一些应该小心的事情!
这是一个容易犯的错误,因为通常这样编写循环:
C#
for (int x = 0; x < bounds.max.x; x++) {
for (int y = 0; y < bounds.max.y; y++) {
// do stuff
}
}
但这有什么问题呢?起始值!需要小心在Unity中像2D空间这样工作。没有什么实际上保证瓦片是从TileMap上的零位置开始绘制的,并且只朝正方向移动。事实上,在游戏中,在正y轴上没有瓦片!所以一个简单的改变是确保不要搞砸,使用最小值作为起始点:
C#
for (int x = bounds.min.x; x < bounds.max.x; x++) {
for (int y = bounds.min.y; y < bounds.max.y; y++) {
// do stuff
}
}
就这么简单!
C#
public static class TileMapExtensionMethods {
public static IEnumerable GetAllTiles(this Tilemap tilemap) {
// Note: Optionally call tilemap.CompressBounds() some time prior to this
var bounds = tilemap.cellBounds;
// loop over the bounds (from min to max) on both axes
for (int x = bounds.min.x; x < bounds.max.x; x++) {
for (int y = bounds.min.y; y < bounds.max.y; y++) {
var cellPosition = new Vector3Int(x, y, 0);
// get the sprite and tile object at the specified location
var sprite = tilemap.GetSprite(cellPosition);
var tile = tilemap.GetTile(cellPosition);
// This is a sanity check that I've included to ensure we're only
// looking at populated tiles. You can change this up!
if (tile == null && sprite == null) {
continue;
}
// create a data-transfer-object to yield back
var tileData = new TileData(x, y, sprite, tile);
yield return tileData;
}
}
}
}
public sealed class TileData {
public TileData(int x, int y, Sprite sprite, TileBase tile) {
X = x;
Y = y;
Sprite = sprite;
Tile = tile;
}
public int X { get; }
public int Y { get; }
public Sprite Sprite { get; }
public TileBase Tile { get; }
}