在学习瓦片地图的使用时,我发现无论国内外还是Unity官方的相关教程都比较散,接触的比较浅,学的我挺难受的,所以就把各个地方看的教程加上我自己的理解,和官方的API手册,总结出了这个详解。
TileMap是Unity5.5a实验版加入的新功能,就像他的字面意思「瓦片地图」。
但说起来这个技术并不“新”了,成熟的2D引擎(诸如gamemaker,RpgMaker),都带有自己自己的tile编辑器的,第三方的编辑工具如Tiled使用起来也是比较方便的,Tile编辑器界的老牌开源编辑器Tiled,很多引擎也提供Tiled导入的支持。
TileMap 是有助于快速搭建整体关卡的利器。如果没有它,开发者要么只有用最笨的方法手动逐一搭建,要么自己写编辑器,要么借助于第三方编辑软件,而这三种方法其实对初学者都不是很友好。
Tilemap用起来其实就和现实中画画一样,它由以下五个基本部分组成:
Tilemap部分其它工具:
TilePalette(瓦片调色板) 中可以找到用来绘制Tile的绘图工具:
从左至右依次为:
TilePalette面板上有一个Edit,如果选上的话可以编辑在TilePalette面板中的Tile资源。
在Tilemap中添加碰撞体十分简单,只要给Tilemap对象加入一个Tilemap Collider 2D组件即可。
可以看到,这个组件自动为该Tilemap上的所有瓦片都加入了碰撞体。
我们可以通过再添加Composite Collider来优化这里的碰撞体,Rigidbody 2D组件会随着Composite Collider自动添加。
因为平台不会移动,所以要记得将Rigidbody 2D上的Body Type属性设置为Static。
最后在Tilemap Collider 2D上勾选Used By Composite,在整个平台周围生成一个复合碰撞体。
设置好后的检视窗口如下所示。
这样我们的Tilemap部分就完成了。
问题原因:
原因在于我们对图片进行切片时,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进行缩放操作即可。
问题原因:
AnimatedTile的RuleTile的功能存在差异
AnimatedTile:
RuleTile
(前提是设置瓦片output类型为Animation):
总结:
对于带有Animation的瓦片,我们应该按需选择Tile的类型。
例如:不同启用时间的地刺,我们应当选择AnimatedTile进而调节StartTime参数;
例如:较长的瀑布,我们应该选用RuleTile,既能实现Animation效果又能方便画多个瓦片;
例如:蜡烛,我们想实现燃烧动画不一致,就应当采用AnimatedTile进而调节两个Speed参数等等。
但总的来说,RuleTile集成了几类extra瓦片的基础功能,较为常用。
tilemap组件是一个存储和处理瓦片资源以便创建2D关卡系统。
此组件将所需信息放置在组件的瓦片上传输到其他相关组件。
例如Tilemap Render和Tilemap Colider2D。
Material:
定义用于渲染精灵纹理的材质。
Sort Order:
设置所选瓦片地图上的瓦片排序方向。
Mode:
设置渲染模式。
Detect Chunk Culling Bounds:
确定渲染器如何检测用于剔除瓦片地图块的边界。
这些边界可扩展瓦片地图块的边界,以确保在剔除过程中不会裁剪过大的精灵。
Chunk Culling Bounds:
输入剔除边界的扩展值。
Sorting Layer:
设置瓦片地图的排序图层(sorting layer)。
从下拉框中选择现有的排序图层,或创建新的排序图层。
Order in Layer:
设置瓦片地图在其排序图层中的渲染优先级。首先渲染编号较低的图层。
编号较高的图层叠加在前者之上。
Mask Interaction:
设置地图渲染器在于精灵交互时的行为方式。
https://docs.unity.cn/cn/current/ScriptReference/Tilemaps.Tilemap.html
变量名称 | 作用 |
---|---|
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);
参数:
Tilemaps.TileBase GetTile(Vector3Int position);
Sprite GetSprite(Vector3Int position);
bool ContainsTile(TileBase tileAsse);
bool HasTile(Vector3Int position);
Vector3 GetCellCenterLocal (Vector3Int position);
Vector3 GetCellCenterWorld (Vector3Int position);
Tilemaps.Tile.ColliderType GetColliderType (Vector3Int position);
Color GetColor (Vector3Int position);
Tilemaps.TileFlags GetTileFlags (Vector3Int position);
int GetUsedTilesCount ();
void SetColliderType (Vector3Int position, Tilemaps.Tile.ColliderType colliderType);
void SetColor (Vector3Int position, Color color);
void SetTileFlags (Vector3Int position, Tilemaps.TileFlags flags);
void RefreshTile(Vector3Int position);
void RefreshAllTiles ();
void SwapTile(Tilemaps.TileBase changeTile, Tilemaps.TileBase newTile);
void ClearAllTiles();
void DeleteCells(Vector3Int position, Vector3Int deleteCells);
Vector3 CellToLocal(Vector3Int cellPosition);
Vector3 CellToWorld (Vector3Int cellPosition);
Vector3Int LocalToCell (Vector3 localPosition);
Vector3Int WorldToCell (Vector3 worldPosition);
Bounds GetBoundsLocal (Vector3Int cellPosition);
Vector3 GetLayoutCellCenter ();
注意:
所有坐标转换方法都是依赖于tilemap实例的父实例(GridLayout)
需要如下方法才能使用:
GridLayout gridLayout = transform.parent.GetComponent();
Vector3Int cellPosition = gridLayout.LocalToCell(transform.localPosition);
transform.localPosition = gridLayout.CellToLocal(cellPosition);
Tilemap 中默认瓦片的类。
该类从 TileBase 继承,代表将放置在 Tilemap 中的默认瓦片。它实现 TileBase.GetTileData,以在瓦片地图中简单渲染 Sprite。
变量名 | 作用 |
---|---|
color | Tile 的 颜色。 |
flags | Tile 的 TileFlags。 |
gameObject | Tile 的 GameObject。 |
sprite | 要在 Tile 处渲染的 Sprite。 |
transform | Tile 的 Transform matrix。 |
注意:
transform是矩阵,不是一般游戏对象的transform组件
以上五个属性合一块一封装就是TileData了
void GetTileData (Vector3Int position, Tilemaps.ITilemap tilemap, ref Tilemaps.TileData tileData);
void RefreshTile (Vector3Int position, Tilemaps.ITilemap tilemap);
注意:
以上俩方法都是静态方法,不能通过实例调用,要通过类名调用
使用程序动态生成使用三种瓦片的瓦片地图
代码如下:
瓦片地图生成:
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();
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();//获取瓦片地图的父对象,网格布局
//求三种地块(瓦片)所占的列数
int desertCol = (int) (mapSize.x * desertRate);
int gobiCol = (int)(mapSize.x * gobiRate);
int oasisCol = mapSize.x - desertCol - gobiCol;
//开始生成瓦片地图
for (int i = 0;i();
// 初始化瓦片单元格地址(逻辑地址)
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 spriteList = new List(Resources.LoadAll("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();
效果如下:
这样就可以程序化生成地图了
https://github.com/Unity-Technologies/2d-extras
为了提高开发速度,Unity创建了名为2d-extras的GitHub代码库,这里面的工具都是基于Tilemap制作,有很多实用的瓦片和笔刷,下面介绍几个2d-extras中的实用工具。
2d-extras中最重要的工具之一就是规则瓦片(Rule Tile)。
不用这个工具的话,我们每次在Tilemap上绘制时,都要从调色板选取特定瓦片再进行绘制。
如果你想要绘制相邻瓦片,例如一个拐角,你需要从调色板上一次次选取不同瓦片进行绘制。这种方法效率不高。但有了规则瓦片,你可以为相邻瓦片设置一组规则,它将自动选择最合适的瓦片进行绘制。
规则瓦片属性:
规则图块属性包括:
您添加的每个磁贴都有其他属性:
上图的两个 Rotated 规则完成了 8 个 设定瓦片规则的工作。
在2d-extras中,还加入了预制件笔刷(Prefab Brush)。它能用预制件而不是瓦片来绘制画面。预制件可以是3D对象、粒子效果或是动画对象。要想创建自己的预制件笔刷,只要在项目窗口点击Create -> Prefab Brush即可。然后选取刚创建的预制件笔刷,指定想要画出的预制件。如果你添加了不止一个预制件,它会在这些预制件中随机选取进行绘制。
https://zhuanlan.zhihu.com/p/34714804
https://www.cnblogs.com/SouthBegonia/p/11592554.html