前言
在學習瓦片地圖的使用時,我發現無論國內外還是Unity官方的相關教程都比較散,接觸的比較淺,學的我挺難受的,所以就把各個地方看的教程加上我自己的理解,和官方的API手冊,總結出了這個詳解,
0. 瓦片地圖基礎知識
0.1 什么是瓦片地圖
TileMap是Unity5.5a實驗版加入的新功能,就像他的字面意思「瓦片地圖」,
但說起來這個技術并不“新”了,成熟的2D引擎(諸如gamemaker,RpgMaker),都帶有自己自己的tile編輯器的,第三方的編輯工具如Tiled使用起來也是比較方便的,Tile編輯器界的老牌開源編輯器Tiled,很多引擎也提供Tiled匯入的支持,
TileMap 是有助于快速搭建整體關卡的利器,如果沒有它,開發者要么只有用最笨的方法手動逐一搭建,要么自己寫編輯器,要么借助于第三方編輯軟體,而這三種方法其實對初學者都不是很友好,
0.2 瓦片地圖的構成
Tilemap用起來其實就和現實中畫畫一樣,它由以下五個基本部分組成:

- Sprite(精靈):
紋理的容器,
大型紋理圖集可以轉換為精靈圖集(Sprite Sheet), - Tile(瓦片):
包含一個精靈,以及二個屬性,顏色和碰撞體型別,
使用瓦片就像在畫布上畫畫一樣,畫畫時可以設定一些顏色和屬性, - Palette(調色板):
當你在畫布(Canvas)上畫畫時,會需要一個位置來保存繪畫的結果,
類似地,調色板(Palette)的功能就是保存瓦片,將它們繪制到網格上, - Brush(筆刷):
用于將畫好的東西繪制到畫布上,
使用Tilemap時,可以在多個筆刷中任意選擇,繪制出線條、方塊等各種形狀, - Tilemap(瓦片地圖):
類似Photoshop中的圖層,我們可以在Tilemap上畫上Tile,
Tilemap部分其它工具:
- Grid(網格):
用于控制網格屬性的組件,
Tilemap是Grid的子物件,
Grid類似于UI Canvas(UI畫布), - Tilemap渲染器(Tilemap Renderer):
用于控制Tile在Tilemap上的渲染,控制諸如排序、材質和遮罩等,
1. 瓦片地圖的初級使用
1.1 創建流程
- 創建瓦片地圖:
在層級視窗中,依次點擊Create->2D Object->Tilemap 新建Tilemap
這將自動創建兩個GameObject物件,一個是名為Grid的父物件,上面帶有Grid組件,
另一個是名為Tilemap的子物件,它在Grid物件下,帶有Tilemap和Tilemap Renderer組件, - 創建調色板
依次點擊Window->Tile Palette打開調色板視窗,
在調色板視窗中,選擇Create New Palette,設定名字和屬性,然后指定保存位置, - 創建筆刷
向調色板加入新瓦片,拖動精靈到調色板中,會打開一個新視窗,詢問瓦片的保存位置,
你也可以通過精靈圖集拖動多個瓦片, - 創建完成,開始在場景中繪制:
瓦片加入調色板后,就可以用筆刷繪制場景了,
1.2 瓦片繪制工具
TilePalette(瓦片調色板) 中可以找到用來繪制Tile的繪圖工具:

從左至右依次為:
- 選擇工具
用來選擇Tile資源更改屬性, - 移動工具
用于移動選中的Tile資源, - 矩形工具
用于繪制矩形塊,并由選定的Tile資源來填充, - 選取工具
用于選取要繪制的Tile資源,按下左鍵并拖拽可以一次選取多個Tile, - 擦除工具
用于擦除Tile資源, - 填充工具
用于將選定區域填充為選定的Tile資源,
TilePalette面板上有一個Edit,如果選上的話可以編輯在TilePalette面板中的Tile資源,
1.3 瓦片地圖的物理碰撞
在Tilemap中添加碰撞體十分簡單,只要給Tilemap物件加入一個Tilemap Collider 2D組件即可,
可以看到,這個組件自動為該Tilemap上的所有瓦片都加入了碰撞體,
我們可以通過再添加Composite Collider來優化這里的碰撞體,Rigidbody 2D組件會隨著Composite Collider自動添加,
因為平臺不會移動,所以要記得將Rigidbody 2D上的Body Type屬性設定為Static,
最后在Tilemap Collider 2D上勾選Used By Composite,在整個平臺周圍生成一個復合碰撞體,
設定好后的檢視視窗如下所示,

這樣我們的Tilemap部分就完成了,
1.4 注意事項
1.4.1 瓦片匹配問題
- 發生情景:
在創建Tilemap及一個palette后,我們想把已有的美術資源(Jungle_Tileset.png)做成瓦片,在對圖片進行切片、拖入Palette后、進行繪制地圖時會發現,瓦片并不與Scene場景內的unit單元格匹配,
例如下圖:

-
問題原因:
原因在于我們對圖片進行切片時,Pixels Per Unit 數值的問題, -
問題分析:
它的含義是每個unit單元格所能容納該圖片的多少個像素,上圖依次為Pixels Per Unit為不同值時每個瓦片與Scene場景下一個Unit的比例,拿Pixels Per Unit=43來說,其含義是每個unit只夠裝下43個像素,而我們的美術圖片(左)像素為1024x1024,算下來每個瓦片有128像素,我們卻只給每個Unit43像素,所以從最右邊圖片我們可以看出大概9個unit才可以放下一個瓦片, -
如何解決:
對每張將要被做成瓦片的美術資源進行Pixels Per Unit的計算,如本例最合理的數值為 1024/8 = 128非瓦片的Sprite直接拖拽進入Scene進行縮放操作即可,
1.4.2 Tiles的選擇問題
- 發生情景:
在匯入2d-extras包后,我們可以在Project內右鍵添加各類Tiles(見下圖),假如我們要做一個帶有Animation的瓦片,是否直接可以直接使用AnimationTile型別呢?答案是肯定的,但完美的做法是按需使用,

-
問題原因:
AnimatedTile的RuleTile的功能存在差異
-
AnimatedTile:
- 瓦片數目:對單塊瓦片進行操作
- 影片速度:MinimumSpeed ~ MaximumSpeed
- 起始時間:Start Time
-
RuleTile
(前提是設定瓦片output型別為Animation):
- 瓦片數目:多片,且能同時實作規則瓦片功能
- 影片速度:Speed
- GameObject:同時實作PrefabBrush功能
-
-
總結:
對于帶有Animation的瓦片,我們應該按需選擇Tile的型別,
例如:不同啟用時間的地刺,我們應當選擇AnimatedTile進而調節StartTime引數;
例如:較長的瀑布,我們應該選用RuleTile,既能實作Animation效果又能方便畫多個瓦片;
例如:蠟燭,我們想實作燃燒影片不一致,就應當采用AnimatedTile進而調節兩個Speed引數等等,
但總的來說,RuleTile集成了幾類extra瓦片的基礎功能,較為常用,
1.4.3 快捷鍵
- 旋轉繪制中的瓦片的方向:
[ ] 鍵 - 洗掉當前繪制的瓦片:
按住shift+左鍵點擊
2. 瓦片地圖 的 API
2.1 編輯器中的屬性
2.1.1 TileMap
tilemap組件是一個存盤和處理瓦片資源以便創建2D關卡系統,
此組件將所需資訊放置在組件的瓦片上傳輸到其他相關組件,
例如Tilemap Render和Tilemap Colider2D,
- Animation Frame Rate:
unity播放圖塊的速率,(如果將其設定為2,則unity以2倍速度播放Tile影片) - Color:
使用選定的顏色在此Tilemap上為Tile著色,設定為白色時,unity渲染圖塊時不著色, - Tile Anchor:
Tile在此Tilemap上的錨定偏移,調整位置值以偏移圖塊 在關聯網格上的像元位置, - Orientation:
Tilemap上Tile的方向,如果您需要在特定平面上(特別是在3D視圖中)定向圖塊,使用此項, - Position:
顯示當前方向矩陣的位置偏移, - Rotation:
當前方向矩陣的旋轉, - Scale:
當前方向矩陣的比例,

2.1.2 Tilemap Renderer
-
Material:
定義用于渲染精靈紋理的材質, -
Sort Order:
設定所選瓦片地圖上的瓦片排序方向, -
Mode:
設定渲染模式,- Chunk :
渲染器按位置對瓦片進行分組,并將瓦片的精靈一起批處理以進行渲染,可獲得最佳渲染性能 - Individual:
渲染器單獨渲染每個瓦片,同時還會考慮他們的位置和排序順序,
此模式使瓦片上的精靈能夠與場景中的其他渲染器或與自定義排序軸進行互動
- Chunk :
-
Detect Chunk Culling Bounds:
確定渲染器如何檢測用于剔除瓦片地圖塊的邊界,
這些邊界可擴展瓦片地圖塊的邊界,以確保在剔除程序中不會裁剪過大的精靈,- Auto:渲染器會自動檢查瓦片使用的精靈,以確定要使用的擴展剔除邊界
- Manual:用于擴展邊界與剔除瓦片地圖塊的值是手動設定的,而不使用Editor的自動檢測功能
-
Chunk Culling Bounds:
輸入剔除邊界的擴展值, -
Sorting Layer:
設定瓦片地圖的排序圖層(sorting layer),
從下拉框中選擇現有的排序圖層,或創建新的排序圖層, -
Order in Layer:
設定瓦片地圖在其排序圖層中的渲染優先級,首先渲染編號較低的圖層,
編號較高的圖層疊加在前者之上, -
Mask Interaction:
設定地圖渲染器在于精靈互動時的行為方式,- None:
瓦片地圖渲染器不與場景中的任何精靈遮罩互動 - Visible Inside Mask:
瓦片地圖在精靈遮罩覆寫瓦片地圖的地方是可見的,而在遮罩外部不可見 - Visible Outside Mask:
瓦片地圖在精靈遮罩外部是不可見的,而在遮罩內部不可見,
精靈遮罩會隱藏其要覆寫的精靈部分
- None:

2.2 編程用API
https://docs.unity.cn/cn/current/ScriptReference/Tilemaps.Tilemap.html
2.2.1 Tilemap
常用變數
| 變數名稱 | 作用 |
|---|---|
| Grid layoutGrid | 獲取與此Tilemap關聯的網格, |
| BoundsInt localBounds | 以本地空間大小回傳 Tilemap 的邊界, |
| BoundsInt cellBounds | 以單元格大小回傳 Tilemap 的邊界, |
| Vector3Int origin | Tilemap 的原點(以單元格位置為單位), |
| Vector3Int size | Tilemap 的大小(以單元格為單位), |
| Vector3 tileAnchor | Tilemap 的錨點, |
| orientation | Tilemap 的排列方向, |
| bool enabled | 啟用的 Behaviour 可更新,禁用的 Behaviour 不可更新, |
| Vector3 cellGap | 布局中各個單元格之間的間隙大小, |
| cellLayout | 單元格的布局, |
| cellSize | 布局中每個單元格的大小, |
| cellSwizzle | 布局的單元格重排, |
| hideFlags | 該物件應該隱藏、隨場景一起保存還是由用戶修改? |
| Color color | Tilemap 層的顏色 |
常用方法
添加瓦片
-
SetTile
在Tilemap中指定的單元格添加瓦片,
void SetTile (Vector3Int position, Tilemaps.TileBase tile); -
SetTiles
在Tilemap中指定的一組單元格添加一組瓦片,
void SetTiles(Vector3Int[] positionArray, TileBase[] tileArray); -
SetTilesBlock
在Tilemap中指定的一個范圍塊內添加一組瓦片,
void SetTilesBlock(BoundsInt position, TileBase[] tileArray);比SetTiles更有效率,是批處理
-
BoxFill
在瓦片地圖上使用給定瓦片進行框填,
從給定坐標開始,然后從開始到結束(含)填充邊界,
void BoxFill (Vector3Int position, Tilemaps.TileBase tile, int startX, int startY, int endX, int endY);引數名 含義 position 瓦片在 Tilemap 上的位置, tile 要放置的 Tile, startX 填充范圍的 X 坐標下限, startY 填充范圍的 Y 坐標下限, endX 填充范圍的 X 坐標上限, endY 填充范圍的 Y 坐標上限, -
FloodFill
從給定坐標開始,在瓦片地圖上使用要放置的給定瓦片進行灌填(油漆桶),
void FloodFill (Vector3Int position, Tilemaps.TileBase tile);BoxFill是范圍內填充
FloodFill是剩下范圍全部填充 -
InsertCells
在Tilemap中指定位置插入很多瓦片
void InsertCells (Vector3Int position, Vector3Int insertCells);
引數:- position 單元格坐標
- Vector3Int insertCells 插入瓦片引數
- numColumns:插入列數
- numRows:插入行數
- numLayers:插入層數
查詢瓦片
- GetTile
獲取指定單元格的瓦片物件,
Tilemaps.TileBase GetTile(Vector3Int position); - GetSprite
獲取指定單元格的瓦片使用的精靈物件,
Sprite GetSprite(Vector3Int position); - ContainsTile
瓦片地圖中是否存在該瓦片
bool ContainsTile(TileBase tileAsse); - HasTile
瓦片地圖的指定位置是否存在該瓦片
bool HasTile(Vector3Int position); - GetCellCenterLocal
獲取瓦片地圖邏輯中心的本地坐標,
Vector3 GetCellCenterLocal (Vector3Int position); - GetCellCenterWorld
獲取瓦片地圖邏輯中心的世界坐標,
Vector3 GetCellCenterWorld (Vector3Int position); - GetColliderType
根據給定的瓦片地圖中某個單元格的 XYZ 坐標,獲取瓦片的碰撞體型別,
Tilemaps.Tile.ColliderType GetColliderType (Vector3Int position); - GetColor
根據給定的瓦片地圖中某個單元格的 XYZ 坐標,獲取瓦片的顏色,
Color GetColor (Vector3Int position); - GetTileFlags
獲取給定位置處的 Tile 的 TileFlags,
Tilemaps.TileFlags GetTileFlags (Vector3Int position); - GetUsedTilesCount
獲取 Tilemap 中使用的不同 tiles 的總數,
int GetUsedTilesCount ();
修改瓦片
- SetColliderType
根據給定的瓦片地圖中某個單元格的 XYZ 坐標,設定瓦片的碰撞體型別,
void SetColliderType (Vector3Int position, Tilemaps.Tile.ColliderType colliderType); - SetColor
根據給定的瓦片地圖中某個單元格的 XYZ 坐標,設定瓦片的顏色,
void SetColor (Vector3Int position, Color color); - SetTileFlags
將 TileFlags 設定到給定位置處的 Tile 上,
void SetTileFlags (Vector3Int position, Tilemaps.TileFlags flags);
更新瓦片
- RefreshTile
更新指定位置的瓦片,
void RefreshTile(Vector3Int position); - RefreshAllTiles
更新瓦片地圖中的所有瓦片,
void RefreshAllTiles (); - SwapTile
將所有指定型別的瓦片置換為新的瓦片物件并重繪
void SwapTile(Tilemaps.TileBase changeTile, Tilemaps.TileBase newTile);- changeTile:舊瓦片
- newTile:新瓦片
洗掉瓦片
- ClearAllTiles
清除瓦片地圖內所有瓦片,
void ClearAllTiles(); - DeleteCells
洗掉指定范圍內的瓦片
void DeleteCells(Vector3Int position, Vector3Int deleteCells);
Vector3Int deleteCells:- 列數
- 行數
- 層級
坐標轉換
- CellToLocal
將單元格位置轉換為本地空間位置,
Vector3 CellToLocal(Vector3Int cellPosition); - CellToWorld
將單元格位置轉換為世界空間位置,
Vector3 CellToWorld (Vector3Int cellPosition); - LocalToCell
將本地位置轉換為單元格位置,
Vector3Int LocalToCell (Vector3 localPosition); - WorldToCell
將世界位置轉換為單元格位置,
Vector3Int WorldToCell (Vector3 worldPosition); - GetBoundsLocal
回傳該位置的單元格的本地邊界,
Bounds GetBoundsLocal (Vector3Int cellPosition); - GetLayoutCellCenter
獲取 Grid 的設定布局的單元格默認中心坐標(世界坐標),
Vector3 GetLayoutCellCenter ();
注意:
所有坐標轉換方法都是依賴于tilemap實體的父實體(GridLayout)
需要如下方法才能使用:
GridLayout gridLayout = transform.parent.GetComponent<GridLayout>();
Vector3Int cellPosition = gridLayout.LocalToCell(transform.localPosition);
transform.localPosition = gridLayout.CellToLocal(cellPosition);
2.2.2 Tile
Tilemap 中默認瓦片的類,
該類從 TileBase 繼承,代表將放置在 Tilemap 中的默認瓦片,它實作 TileBase.GetTileData,以在瓦片地圖中簡單渲染 Sprite,
常用變數
| 變數名 | 作用 |
|---|---|
| color | Tile 的 顏色, |
| flags | Tile 的 TileFlags, |
| gameObject | Tile 的 GameObject, |
| sprite | 要在 Tile 處渲染的 Sprite, |
| transform | Tile 的 Transform matrix, |
注意:
transform是矩陣,不是一般游戲物件的transform組件
以上五個屬性合一塊一封裝就是TileData了
常用方法
- GetTileData
檢索 Tile 的瓦片渲染資料,
void GetTileData (Vector3Int position, Tilemaps.ITilemap tilemap, ref Tilemaps.TileData tileData); - RefreshTile
瓦片重繪時呼叫
void RefreshTile (Vector3Int position, Tilemaps.ITilemap tilemap);
注意:
以上倆方法都是靜態方法,不能通過實體呼叫,要通過類名呼叫
2.2.3 編程案例
使用程式動態生成使用三種瓦片的瓦片地圖
瓦片種類有三種:

代碼如下:
瓦片地圖生成:
using UnityEngine;
using UnityEngine.Tilemaps;
public class MapSystem : MonoBehaviour
{
public Vector2Int mapSize;//瓦片地圖大小
public float desertRate = 0.2f;//沙漠地塊所占比例
public float gobiRate = 0.4f;//戈壁地塊所占比例
public float oasisRate = 0.4f;//綠洲地塊所占比例
public int mixingAreaSize = 2;//交接混合地塊寬度
private LandBlock[][] landBlocks;
private Tilemap tilemap;
void Awake()=> tilemap = gameObject.GetComponent<Tilemap>();
void Start() => initMap();
public void initMap()//初始化地圖
{
tilemap.ClearAllTiles();//先清空瓦片地圖中的所有瓦片
tilemap.size = new Vector3Int(mapSize.x,mapSize.y,0);//設定瓦片地圖大小
landBlocks = new LandBlock[mapSize.y][];//準備資料結構與瓦片地圖的瓦片系結
GridLayout gridLayout = transform.parent.GetComponentInParent<GridLayout>();//獲取瓦片地圖的父物件,網格布局
//求三種地塊(瓦片)所占的列數
int desertCol = (int) (mapSize.x * desertRate);
int gobiCol = (int)(mapSize.x * gobiRate);
int oasisCol = mapSize.x - desertCol - gobiCol;
//開始生成瓦片地圖
for (int i = 0;i<mapSize.y;i++)
{
landBlocks[i] = new LandBlock[mapSize.x];
for (int j = 0;j<mapSize.x;j++)
{
// 初始化瓦片
Tile tile = ScriptableObject.CreateInstance<Tile>();
// 初始化瓦片單元格地址(邏輯地址)
Vector3Int index = new Vector3Int(i, j, 0);
// 將瓦片添加到瓦片地圖中
tilemap.SetTile(index, tile);
// 獲取該瓦片的世界坐標
Vector3 position = gridLayout.CellToWorld(index);
// 根據當前列數生成地塊(地塊資料與瓦片系結)
LandBlock landBlock;
if(j < desertCol - mixingAreaSize)
landBlock = new LandBlock(index, position, LandBlock.BlockType.Desert, tile);
else if(j < desertCol)
if(Random.Range(0f, 1f) < 0.5)
landBlock = new LandBlock(index, position, LandBlock.BlockType.Desert, tile);
else
landBlock = new LandBlock(index, position, LandBlock.BlockType.Gobi, tile);
else if(j < desertCol + gobiCol - mixingAreaSize)
landBlock = new LandBlock(index, position, LandBlock.BlockType.Gobi, tile);
else if(j < desertCol + gobiCol)
if (Random.Range(0f, 1f) < 0.5)
landBlock = new LandBlock(index, position, LandBlock.BlockType.Gobi, tile);
else
landBlock = new LandBlock(index, position, LandBlock.BlockType.Oasis, tile);
else
landBlock = new LandBlock(index, position, LandBlock.BlockType.Oasis, tile);
landBlocks[i][j] = landBlock;
}
}
// 更新瓦片地圖的所有瓦片,地圖初始化完成
tilemap.RefreshAllTiles();
}
地塊資料類:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class LandBlock
{
private static readonly List<Sprite> spriteList = new List<Sprite>(Resources.LoadAll<Sprite>("img/Entity/LandBlocks"));
public enum BlockType { Desert, Gobi, Oasis };// 地塊型別: 沙漠,戈壁,綠洲
public Vector3Int index; // 邏輯坐標
public Vector3 position; // 位置
public Tile tile;
private BlockType type;
public BlockType Type {get => type;}
public LandBlock(Vector3Int index, Vector3 position, BlockType type, Tile tile)
{
this.index = index;
this.position = position;
this.type = type;
this.tile = tile;
setSprite();
}
private void setSprite()//更改瓦片精靈
{
string spriteName = type.ToString() + "Block";
tile.sprite = spriteList.Find(p => p.name == spriteName);
}
}
注意:
Tile 瓦片的實體化不能使用new,會報錯,
需要使用 ScriptableObject.CreateInstance 才行,例如:
Tile tile = ScriptableObject.CreateInstance();
效果如下:

這樣就可以程式化生成地圖了
3. 2d-extras中的高級瓦片
https://github.com/Unity-Technologies/2d-extras
為了提高開發速度,Unity創建了名為2d-extras的GitHub代碼庫,這里面的工具都是基于Tilemap制作,有很多實用的瓦片和筆刷,下面介紹幾個2d-extras中的實用工具,
3.1 規則瓦片(Rule Tile)
2d-extras中最重要的工具之一就是規則瓦片(Rule Tile),

不用這個工具的話,我們每次在Tilemap上繪制時,都要從調色板選取特定瓦片再進行繪制,
如果你想要繪制相鄰瓦片,例如一個拐角,你需要從調色板上一次次選取不同瓦片進行繪制,這種方法效率不高,但有了規則瓦片,你可以為相鄰瓦片設定一組規則,它將自動選擇最合適的瓦片進行繪制,
規則瓦片屬性:
規則圖塊屬性包括:
- 默認精靈
- 默認游戲物件:
可以保留為空白 - 默認碰撞器:
指定了瓦片的碰撞檢測模式,有兩種:- 精靈模式:按精靈形狀進行碰撞檢測
- 網格模式:按瓦片地圖的單元網格形狀進行碰撞檢測
您添加的每個磁貼都有其他屬性:
- Rule :
這決定了規則的型別,選項是固定(默認行為)、鏡像(跨 X 或 Y 軸)或旋轉,鏡像和旋轉是指 3x3 網格的配置,本節稍后會詳細介紹,

上圖的兩個 Rotated 規則完成了 8 個 設定瓦片規則的作業,
- 游戲物件
- 碰撞器:
更改特定圖塊的碰撞器以覆寫默認碰撞器, - 輸出:
這決定了瓷磚如何放置到場景中,
您可以從Single 、Random或Animation 中進行選擇,- Single :單個瓦片(普通瓦片),將使用選定的瓦片,
- Random:隨機瓦片,將在一組定義的瓦片之間進行選擇
- Animation :影片瓦片,將播放指定圖塊的影片,
- 3x3 網格:
用于設定規則,規則可以根據周圍瓦片的屬性來決定中心瓦片的屬性
比如說地塊的拼接和轉交等功能 - 瓦片選擇:
在該規則下使用的瓦片
3.2 預制件筆刷
在2d-extras中,還加入了預制件筆刷(Prefab Brush),它能用預制件而不是瓦片來繪制畫面,預制件可以是3D物件、粒子效果或是影片物件,要想創建自己的預制件筆刷,只要在專案視窗點擊Create -> Prefab Brush即可,然后選取剛創建的預制件筆刷,指定想要畫出的預制件,如果你添加了不止一個預制件,它會在這些預制件中隨機選取進行繪制,
4. 相關參考
https://zhuanlan.zhihu.com/p/34714804
https://www.cnblogs.com/SouthBegonia/p/11592554.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/325847.html
標籤:其他
下一篇:6.C語言 二維陣列
