Unity(C#)中如何从中心原点瓦片实现单瓦片转多瓦片房间
Unity Tilemap 从中心原点瓦片生成指定规格房间实现方案
核心逻辑:全程以初始状态的中心单瓦片作为坐标原点做对齐,不需要额外做坐标偏移,先计算目标规格房间的格子覆盖范围,清空范围内拆分残留的零散瓦片后统一填充房间瓦片即可,支持任意宽高的矩形(含正方形)房间生成,比如提到的5x7规格可以直接适配。
实现步骤
- 记录原点坐标
初始场景仅存在1个中心瓦片时,直接遍历Tilemap的已占用格子,拿到这个瓦片的格子坐标存为全局原点_originCellPos,后续所有房间生成操作都以这个坐标为中心基准。 - 计算目标房间的格子覆盖范围
传入目标房间的列数(X方向长度)、行数(Y方向长度)后,先计算半幅尺寸:int halfW = Mathf.FloorToInt(roomWidth / 2f);int halfH = Mathf.FloorToInt(roomHeight / 2f);
再算出区域四个边界的格子坐标:- X轴左边界:原点x坐标 - halfW
- X轴右边界:原点x坐标 + (roomWidth - halfW - 1)
- Y轴下边界:原点y坐标 - halfH
- Y轴上边界:原点y坐标 + (roomHeight - halfH - 1)
这套计算逻辑兼容奇偶尺寸:比如5x7规格,halfW=2、halfH=3,X方向覆盖从原点x-2到x+2共5格,Y方向覆盖从原点y-3到y+3共7格,中心刚好落在初始原点瓦片上,不会出现偏移。
- 清理旧瓦片并填充房间
双重遍历覆盖范围内的所有格子坐标,先清空格子上原有的拆分后零散瓦片,再根据需求放置对应类型的瓦片:如果需要做带墙的房间,判断当前格子是否在区域边缘,边缘放墙体瓦片、内部放地面瓦片即可;如果是纯实心区域直接全放同一种瓦片就行。 - 绑定房间数据
填充完瓦片后,把房间的中心坐标、尺寸、边界范围存到自定义的Room数据结构里,方便后续做碰撞生成、房间交互、怪物刷出之类的逻辑。
核心实现代码
using UnityEngine; using UnityEngine.Tilemaps; public class RoomGenerator : MonoBehaviour { [Header("目标瓦片地图引用")] [SerializeField] private Tilemap targetTilemap; [Header("房间地面瓦片")] [SerializeField] private TileBase floorTile; [Header("房间墙体瓦片")] [SerializeField] private TileBase wallTile; // 初始中心原点的格子坐标 private Vector3Int _originCellPos; private void Awake() { // 初始状态仅存在中心瓦片,遍历拿到原点坐标 foreach (Vector3Int cellPos in targetTilemap.cellBounds.allPositionsWithin) { if (targetTilemap.HasTile(cellPos)) { _originCellPos = cellPos; break; } } } /// <summary> /// 以中心初始瓦片为原点生成指定尺寸的房间 /// </summary> /// <param name="roomWidth">X方向格子数(列数)</param> /// <param name="roomHeight">Y方向格子数(行数)</param> public void GenerateRoom(int roomWidth, int roomHeight) { // 计算边界范围 int halfW = Mathf.FloorToInt(roomWidth / 2f); int halfH = Mathf.FloorToInt(roomHeight / 2f); int minX = _originCellPos.x - halfW; int maxX = _originCellPos.x + (roomWidth - halfW - 1); int minY = _originCellPos.y - halfH; int maxY = _originCellPos.y + (roomHeight - halfH - 1); // 遍历范围填充瓦片 for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { Vector3Int currentCell = new Vector3Int(x, y, _originCellPos.z); // 先清空原有瓦片(拆分后的残留瓦片会被直接覆盖) targetTilemap.SetTile(currentCell, null); // 边缘格放墙体,内部放地面,不需要墙体直接全放floorTile即可 bool isEdgeCell = x == minX || x == maxX || y == minY || y == maxY; targetTilemap.SetTile(currentCell, isEdgeCell ? wallTile : floorTile); } } // 生成房间数据实例,后续可根据需求扩展属性 Room newRoom = new Room { centerCellPos = _originCellPos, width = roomWidth, height = roomHeight, xMin = minX, xMax = maxX, yMin = minY, yMax = maxY }; // 这里可以把newRoom加入全局房间管理列表,供其他逻辑调用 } } /// <summary> /// 房间数据结构 /// </summary> public class Room { public Vector3Int centerCellPos; public int width; public int height; public int xMin, xMax, yMin, yMax; }
适配说明
- 这套逻辑支持任意正整数尺寸的房间生成,1x1就是初始单瓦片状态,5x7、4*6这类奇偶组合的尺寸都能自动对齐中心,不需要手动调整偏移参数。
- 如果拆分逻辑给瓦片挂了自定义组件或者数据,在清空瓦片的时候记得同步销毁对应关联数据,避免残留引用导致空报错。
- 如果需要生成非矩形的特殊形状房间,只要修改遍历时候的瓦片放置规则即可,中心对齐的基准逻辑不需要改动。
内容的提问来源于stack exchange,提问作者Noah
相关产品推荐
相关产品推荐

