Unity使用Isometric Z As Y Tilemap建立2.5D地圖(三)如何用代碼建立Tilemap
- 建立Tilemap Palette
-
- 1.一些需要了解的事情
-
- 1.1 UnityEditor代碼存放位置
- 1.2 使用MenuItem添加UnityEditor菜單
- 2.使用代碼建立Palette
- 3.使用代碼在場景中建立Tilemap
- 4.使用代碼在場景Tilemap各層上畫地圖
上一篇文檔介紹了Unity在使用Isometric Z As Y Tilemap時如何正确處理圖檔遮擋順序(《Unity使用Isometric Z As Y Tilemap建立2.5D地圖(二)如何按照正确遮擋順序渲染圖檔》)。本篇文章将講解如何使用C#代碼方式建立Tilemap、Palette,進行地圖繪制。
建立Tilemap Palette
1.一些需要了解的事情
通常場景地圖都是提前在Unity編輯器中提前建立好的,也有根據需要在遊戲運作時動态建立的,本例子是在UnityEditor中建立地圖内容。
1.1 UnityEditor代碼存放位置
Unity為了避免不必要的包含,Unity3D的運作時類和編輯器類是存儲在不同的Assemblies中(UnityEngine和UnityEditor),我們将要編寫的編輯器代碼通常是需要放在相應工程下的 Assets\Scripts\Editor 目錄中。
1.2 使用MenuItem添加UnityEditor菜單
MenuItem屬性允許你添加菜單項到主菜單和檢視面闆上下文菜單,該屬性可以把任意靜态函數變為一個可通過菜單調用的指令,僅靜态函數能使用MenuItem屬性。
[MenuItem("Tools/Create Palettee")]
static void CreatePalettee()
{
//to do someting
}
在 Assets\Scripts\Editor 目錄中建立一個C#類,通過上面代碼可以在Unity編輯器的 Tools 菜單中增加一個 Create Palettee的菜單,點選菜單後會執行CreatePalettee()方法。
2.使用代碼建立Palette
首先在 Assets 目錄下建立需要存放Palette的目錄,例子中是放在Assets/Palette目錄下。
if (!AssetDatabase.IsValidFolder("Assets/Palette"))
{
AssetDatabase.CreateFolder("Assets", "Palette");
}
if (dir.Exists)
{
/*
周遊Resources/images下子目錄,可以用不同目錄存放不同層的圖檔
比如ground目錄下放ground層圖檔、build目錄放build圖檔等等
*/
foreach (DirectoryInfo subDir in dir.GetDirectories())
{
string subPath = "Assets/Palette/" + subDir.Name;
if (!AssetDatabase.IsValidFolder(subPath))
{
AssetDatabase.CreateFolder("Assets/Palette", "" + subDir.Name);
}
//建立Palette,相關參數含義請參考前面文章
GameObject palette = CreateNewPalette("Assets/Palette", subDir.Name, GridLayout.CellLayout.IsometricZAsY,
GridPalette.CellSizing.Manual, new Vector3(72f, 36f, 1f), GridLayout.CellSwizzle.XYZ);
//準備向Palette中寫入地圖塊對象
Tilemap layer1 = palette.GetComponentInChildren<Tilemap>(true);
//擷取目錄下所有圖檔
Sprite[] sprites = Resources.LoadAll<Sprite>("images/" + subDir.Name);
int x = 0;
int y = 0;
for (int i = 0; i < sprites.Length; i++)
{
/*
下面這行代碼不是必要的,下面會解釋。
*/
Vector2 offset = TextureManager.GetSpriteOffset(sprites[i].name);
//在Palette裡建立Tile
Tile tile = Tile.CreateInstance<Tile>();
switch(subDir.Name)
{
case "ground":
tile.colliderType = Tile.ColliderType.None;
break;
case "build":
tile.colliderType = Tile.ColliderType.Grid;
break;
case "airbuild":
tile.colliderType = Tile.ColliderType.None;
break;
}
tile.color = Color.white;
tile.sprite = sprites[i];
tile.flags = TileFlags.LockColor;
tile.name = sprites[i].name;
/*
下面5行代碼不是必要的,用處是設定Palette中Tile預設的transform。
此處用來将我讀取的圖檔資源偏移寫入Palette的Tile中。
*/
Matrix4x4 matrix = tile.transform;
matrix.m03 = sprites[i].rect.width/2 - offset.x;//x
matrix.m13 = offset.y - sprites[i].rect.height;//y
matrix.m23 = (offset.y - sprites[i].rect.height) * 2;//z
tile.transform = matrix;
//生成Tile asset
string tilePath = AssetDatabase.GenerateUniqueAssetPath(subPath + "/" + tile.name + ".asset");
AssetDatabase.CreateAsset(tile, tilePath);
//将Tile寫入Pallette
layer1.SetTile(new Vector3Int(x, y, 0), tile);
//此處将40個圖檔放在一行,可根據需要調整
y = y + 1;
if(y >= 40)
{
x = x + 1;
y = 0;
}
}
}
AssetDatabase.SaveAssets();
}
上面代碼中,Vector2 offset = TextureManager.GetSpriteOffset(sprites[i].name);以及 Matrix4x4相關5行代碼不是必要的,但我認為很多時候是很有用的。
由于我所用資源的圖檔大小不規則,是以導入資源時需要将圖檔顯示偏移位置加入到Palette的圖檔中,這樣繪制Tilemap地圖時才能顯示在正确的位置。大家可以根據自己圖檔需要去掉或調整相關代碼。
Matrix4x4用于矩陣轉換,可以用來完成縮放、旋轉等作用。這裡我們隻用到其中3個屬性,m03對應坐标X值,m13對應坐标Y值,m23對應坐标Z值。
代碼中Tile.colliderType相關設定,是用來設定Tile的碰撞類型:
屬性名 | 作用描述 |
---|---|
Tile.ColliderType.None | 無碰撞 |
Tile.ColliderType.Grid | 用網格的outline作為碰撞檢測形狀 |
Tile.ColliderType.Sprite | 用Sprite outline作為碰撞檢測形狀 |
CreateNewPalette方法定義如下:
private static GameObject CreateNewPalette(string folderPath, string name, GridLayout.CellLayout layout, GridPalette.CellSizing cellSizing, Vector3 cellSize, GridLayout.CellSwizzle swizzle)
{
GameObject temporaryGO = new GameObject(name);
Grid grid = temporaryGO.AddComponent<Grid>();
grid.cellSize = cellSize;
grid.cellGap = cellSize;
grid.cellLayout = layout;
grid.cellSwizzle = swizzle;
CreateNewLayer(temporaryGO, "Layer1", layout);
string path = AssetDatabase.GenerateUniqueAssetPath(folderPath + "/" + name + ".prefab");
UnityEngine.Object prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(temporaryGO, path, InteractionMode.AutomatedAction);
GridPalette palette = CreateGridPalette(cellSizing);
AssetDatabase.AddObjectToAsset(palette, prefab);
PrefabUtility.ApplyPrefabInstance(temporaryGO, InteractionMode.AutomatedAction);
AssetDatabase.Refresh();
GameObject.DestroyImmediate(temporaryGO);
return AssetDatabase.LoadAssetAtPath<GameObject>(path);
}
CreateNewLayer方法定義如下:
private static GameObject CreateNewLayer(GameObject paletteGO, string name, GridLayout.CellLayout layout)
{
GameObject newLayerGO = new GameObject(name);
var tilemap = newLayerGO.AddComponent<Tilemap>();
//Sprite Anchor需要根據實際情況調整
tilemap.tileAnchor = new Vector3(0, 0, 0);
var renderer = newLayerGO.AddComponent<TilemapRenderer>();
newLayerGO.transform.parent = paletteGO.transform;
newLayerGO.layer = paletteGO.layer;
//預設設定
switch (layout)
{
case GridLayout.CellLayout.Hexagon:
{
tilemap.tileAnchor = Vector3.zero;
break;
}
case GridLayout.CellLayout.Isometric:
case GridLayout.CellLayout.IsometricZAsY:
{
renderer.sortOrder = TilemapRenderer.SortOrder.TopRight;
break;
}
}
return newLayerGO;
}
GridPalette 方法定義如下:
private static GridPalette CreateGridPalette(GridPalette.CellSizing cellSizing)
{
var palette = GridPalette.CreateInstance<GridPalette>();
palette.name = "Palette Settings";
palette.cellSizing = cellSizing;
return palette;
}
3.使用代碼在場景中建立Tilemap
首先在場景中建立Tilemap對象,主要代碼如下:
//在場景中建立Tilemap Grid,這裡命名為TilemapRoot,可根據需要調整。
GameObject tilemap = new GameObject("TilemapRoot");
Grid grid = tilemap.AddComponent<Grid>();
grid.cellSize = new Vector3(72, 36, 1);
grid.cellLayout = GridLayout.CellLayout.IsometricZAsY;
grid.cellSwizzle = GridLayout.CellSwizzle.XYZ;
在Tilemap對象下根據需要建立多層Tilemap Layer。我這裡建立了地面層、建築層、空中層、碰撞層,可根據實際情況調整。主要代碼如下:
//建立ground tilemap
Tilemap groundLayer = CreateTilemapLayer("ground", tilemap, Vector3.zero,"Ground",0);
Tilemap buildLayer = CreateTilemapLayer("build", tilemap, Vector3.zero, "Build", 1);
Tilemap airbuildLayer = CreateTilemapLayer("airbuild", tilemap, Vector3.zero, "Build", 1);
Tilemap colliderLayer = CreateTilemapLayer("collider", tilemap, Vector3.zero, "Ground", 0);
//為碰撞層添加TilemapCollider2D元件,否則碰撞無法觸發。
TilemapCollider2D collider = colliderLayer.gameObject.AddComponent<TilemapCollider2D>();
collider.usedByComposite = true;
//此為碰撞形狀偏移,可以使用預設沒有偏移,或者根據需要設定。
collider.offset = new Vector2(-2, 26);
CreateTilemapLayer類定義如下:
private static Tilemap CreateTilemapLayer(string name,GameObject parent, Vector3 anchor,string sortingLayerName,int sortOrderInLayer)
{
GameObject layer = new GameObject(name);
Tilemap layerTilemap = layer.AddComponent<Tilemap>();
layerTilemap.tileAnchor = anchor;
TilemapRenderer tilemapRenderer = layer.AddComponent<TilemapRenderer>();
tilemapRenderer.sortOrder = TilemapRenderer.SortOrder.TopRight;
tilemapRenderer.mode = TilemapRenderer.Mode.Individual;
tilemapRenderer.sortingLayerName = sortingLayerName;
tilemapRenderer.sortingOrder = sortOrderInLayer;
layer.transform.parent = parent.transform;
return layerTilemap;
}
4.使用代碼在場景Tilemap各層上畫地圖
使用上一章定義的Tilemap Layer上畫地圖了,主要涉及代碼如下:
//可以直接通過加載之前建立的Palette Tile資源畫圖。舉例如下:
Tile tile = AssetDatabase.LoadAssetAtPath<Tile>("Assets/Palette/ground/0.asset");
if (tile!=null)
{
tile = GameObject.Instantiate(tile);
//準備要畫圖的網格位置
Vector3Int position = new Vector3Int(x,y,1);
//下面4行代碼不是必要的,可以根據需要動态調整場景中圖檔顯示的x、y、z值。
Matrix4x4 pos = tile.transform;
pos.m13 = pos.m13 + offsetHeight[i-1, j, k];
pos.m23 = pos.m23 + (offsetHeight[i - 1, j, k] - 1) * 2;
tile.transform = pos;
//在對應Tilemap Layer圖層中畫圖
tilemapLayer.SetTile(position, tile);
}
上面Matrix4x4 pos相關代碼不是必要的,但根據我的圖檔資源情況是很有用的,可以根據實際需要調整對應網格圖檔顯示的X、Y、Z值。根據前面的文章可以知道,這對于正确顯示地圖圖檔和調整圖檔遮擋關系是很有必要的。
好了,有了以上主要代碼,大家就可以根據自己需要通過代碼自動建立Tilemap Palette調色闆和地圖的。代碼中有不對的地方請務必指正。