Tiled结合Unity实现瓦片地图

2020年01月16日 14:50 0 点赞 1 评论 更新于 2025-11-21 21:30
Tiled结合Unity实现瓦片地图

前段时间,因公司需求需要制作瓦片地图。虽然Cocos与瓦片地图的结合案例众多,但Unity方面的案例却寥寥无几。制作瓦片地图离不开Tiled Map Editor(下载地址),关于该工具的教程有很多,并且它自带示例,因此本文不再赘述如何拼接地图。下面先展示美术人员拼接的地图,效果十分出色,这里截取了一部分。

工具准备与导入

Tiled2Unity介绍

拼图并非程序员的专长,但将地图导入Unity则是一项技术活。尽管很多技术专家喜欢自己编写代码,但在如今追求快速推出游戏的时代,有现成的工具不用实在可惜。本文将介绍Tiled2Unity,它是一款完全免费的软件,若您想付费支持,官方也提供了相应链接。其官网有详细教程,建议您仔细阅读。打开该软件后,界面如下(此处应插入软件界面截图)。

版本声明

在此声明,本文使用的Unity版本是Unity 5.5.2,Tiled版本是0.18.2,Tiled2Unity版本是1.0.10.3,操作系统为mac。若因版本或系统不同导致结果存在差异,请您自行解决。

在Unity中导入Tiled2Unity

有两种导入方法:

  1. 方法一:先打开Unity工程,再打开Tiled2Unity,找到并点击“Help -> Import Unity Package to Project”,操作界面如下(此处应插入操作界面截图)。之后,Unity会弹出导入资源框(此处应插入导入资源框截图),点击“Import”即可完成导入。
  2. 方法二:使用Unity的“Import Package”方法。在mac系统中,右键点击Tiled2Unity的app,选择“显示包内容”,然后找到“Contents/Resources/Tield2Unity.unitypackage”并导入。

导入完成后,您会在工程目录下看到Tiled2Unity文件目录(此处应插入文件目录截图)。

绑定Tiled Map Editor和Tiled2Unity

在Tiled2Unity界面上有一个黄色区域,上面显示了几行内容(此处应插入黄色区域内容截图)。上面明确指示,将“open -a /Applications/Tiled2UnityMac.app --args %mapfile”这个命令行编辑到Tiled Map Editor中,即可使用Tiled Map Editor打开Tiled2Unity。具体编辑方法如下:打开Tiled Map Editor,找到相应位置(此处应插入相应位置截图),点击旁边的下拉三角,选择“编辑命令”,在弹出的对话框中粘贴上述命令(此处应插入粘贴命令对话框截图)。“Export”是该命令的名称,只需复制那串字符串,无需添加额外路径。网上有些教程添加各种路径,其实并未理解该命令行的含义。点击“ok”保存后,点击小齿轮,Tiled2Unity会自动打开,并将当前编辑的地图信息自动导入。需要注意的是,如果不通过Tiled Map Editor而是直接打开Tiled2Unity,将不会有编辑的地图信息。

设置Tiled2Unity并导出地图

通过Tiled Map Editor的小齿轮打开Tiled2Unity后,需要进行以下两项设置:

  1. Pixels Per Unit:其解释为“Set to same 'Pixels Per Unit' value for Unity sprites in your project”,即设置为与Unity项目中sprites的“Pixels Per Unit”相同的值。在Unity中,图片设置为sprite格式时会有该选项,默认值为100,即100个像素为一个单位。但我们实际关注的是Tiled中每个瓦片的单位,例如制作的地图每个瓦片是128 * 64的等角(交错)格式地图,斜45度效果,那么最小单位应为64的一半,即32。这样,整张地图的长和宽用这个最小单位计算才不会出现小数个单位。因此,将“Pixels Per Unit”设置为32。
  2. Export To:这是地图导出后存放的位置,官方说明如下(此处应插入说明截图),意思是定位到包含Tiled2Unity.export.txt文件的目录。还记得之前导入到Unity的Tiled2Unity包吗?其中就有这个文件(此处应插入文件位置截图)。定位到该文件所在目录后,导出的地图的prefab将存放在上面的“Prefabs”文件夹下。

其他设置保持默认即可,若有特殊需求可自行调整。完成上述设置后,点击“Preview Map”可预览在Tiled中编辑的地图,若无误,点击“Big AssExport Button”即可导出地图,左侧的Debug窗口会显示编译信息。

在Unity中使用Tiled地图文件

Tiled Map Editor中的对象

Tiled编辑器中唯一值得程序员关注的部分是对象层,因为只有这部分能参与编程。Tiled中的图层有三种(此处应插入图层类型截图),点击新建图层时会弹出这三种选项。图块层和图片层主要由美术人员负责,仅用于显示;而对象层是程序员重点关注的部分。如果想通过代码操纵某个图层,一定要将其定义为对象层。例如,可将所有的关卡点和建筑点设置为对象层,其他如地表、水面、道路、花草等设置为图块层。切换到对象显示模式后,可看到设置为对象的瓦片列表(此处应插入对象列表截图)。若未定义名称和类型,显示如下(此处应插入未定义名称和类型的对象截图)。选择某个对象后,左侧的属性如下(此处应插入对象属性截图),此时除了xy坐标和宽高外,似乎没有其他用处,先别着急,后续会进行处理。

对象类型编辑器

上述内容均为默认设置,我们需要通过对象类型编辑器来编辑对象,以获取程序所需的变量。首先找到对象类型编辑器(此处应插入对象类型编辑器位置截图),选择后会弹出对象编辑器(此处应插入对象编辑器截图)。默认情况下,编辑器为空,这里已添加了一些内容。左侧的“类型”可用于为对象设置类型,每个对象都应有一个类型,相同类型的对象具有相同的属性。例如,定义了一个关卡类型“Level”,若设置了类型的颜色,在编辑器中该类型的对象将显示相同颜色,便于区分对象类型,但这并非关键设置。每个类型对应后面的属性,可根据需求进行定义。例如,关卡需要记录id以及4个临近关卡点的id,因此定义了5个变量,并将每个属性的默认值设置为0。

设置每个对象的自定义属性

回到主界面,选择一个对象,在“类型”行输入刚设置的类型,神奇的事情就会发生:下面的自定义属性将立即显示在对象编辑器中为该类型定义的属性(此处应插入自定义属性显示截图)。例如,输入“Level”类型后,会出现5条属性,可自定义这些属性的值。如这里关卡号为37,相邻关卡点只有一个,其id为30。“名称”可手动添加,上面的每条数据在未来代码中都可访问,尤其是类型和自定义属性,是我们关注的重点。

Unity内置的自定义属性

Tiled编辑器支持几种与Unity相关的自定义属性,如下所示:

  • unity:tag
  • unity:sortingLayerName
  • unity:sortingOrder
  • unity:layer(在Unity中,“layer”指“物理”层)

例如,在Unity中设置好tag、layer或sortingLayerName后,在Tiled编辑器中设置相同的tag、layer或sortingLayerName,导出后,这些对象将自动设置为Unity中对应的tag、layer、sortingLayerName。下面以“sortingLayerName”为例进行说明: 首先,在Unity的“Tags & Layers”中的“Sorting Layers”里添加一个名为“Background”的层(此处应插入添加层截图),注意层的顺序,将“Default”层拖到“Background”层下方,确保“Background”背景层始终在最下面。然后,在Tiled编辑器的图块层和对象层都添加“unity:sortingLayerName”,并设置为“Background”(此处应插入设置截图)。需要注意的是,图块层也可添加自定义属性。导出后,图块层将自动设置为“Background”层,由于生成的角色默认在“Default”层,这样可保证角色始终显示在地图上方(此处应插入导出后图层显示截图和角色在地图上方显示截图)。

tag和layer的设置和使用方法类似,不再赘述。

导出时加载数据

导入到Unity后,每个对象都携带了在Tiled编辑器中设置的属性(除自定义数据外),并作为TileObject脚本组件绑定到对象上(此处应插入组件绑定截图)。对照前面的内容,可看到属性的对应关系,因此可在TileObject中获取一些基础属性。

那么,如何在代码中获取自定义数据呢?首先,查看Tiled2Unity在Unity中导入的包,找到ICustomTiledImporter文件(此处应插入文件位置截图),代码如下:

namespace Tiled2Unity
{
public interface ICustomTiledImporter
{
// A game object within the prefab has some custom properites assigned through Tiled that are not consumed by Tiled2Unity
// This callback gives customized importers a chance to react to such properites.
void HandleCustomProperties(GameObject gameObject, IDictionary<string, string> customProperties);

// Called just before the prefab is saved to the asset database
// A last chance opporunity to modify it through script
void CustomizePrefab(GameObject prefab);
}
}

// Examples
/*
[Tiled2Unity.CustomTiledImporter]
class CustomImporterAddComponent : Tiled2Unity.ICustomTiledImporter
{
public void HandleCustomProperties(UnityEngine.GameObject gameObject,
IDictionary<string, string> props)
{
// Simply add a component to our GameObject
if (props.ContainsKey("AddComp"))
{
gameObject.AddComponent(props["AddComp"]);
}
}
public void CustomizePrefab(GameObject prefab)
{
// Do nothing
}
}
*/

这个类是负责导出自定义数据的加载类,官方在注释中提供了示例供参考。它包含两个接口:“HandleCustomProperties”用于处理自定义数据,传递两个参数,“gameObject”表示每个对象,“props”是自定义属性组成的字典;“CustomizePrefab”用于对导出的地图prefab进行操作,传递的“prefab”是地图prefab。

在工程目录下创建一个“Editor”文件夹,并在其中新建一个脚本“CustomImporter_StrategyTiles”,代码如下:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Tiled2Unity;

[Tiled2Unity.CustomTiledImporter]
public class CustomImporter_StrategyTiles : Tiled2Unity.ICustomTiledImporter
{
public void HandleCustomProperties(GameObject gameObject, IDictionary<string, string> customProperties)
{
TileObject obj = gameObject.GetComponent<TileObject>();
if (obj != null)
{
// Add the terrain tile game object
if (obj.TmxType == "Level")
{
BoxCollider2D box = gameObject.AddComponent<BoxCollider2D>();
box.offset = new Vector2(2, 1.3f);
box.size = new Vector2(2, 1);
TileData tile = gameObject.AddComponent<TileData>();
tile.Id = int.Parse(customProperties["Object_ID"]);
for (int i = 0; i < 4; ++i)
{
string strId = "Neighbor_ID_" + (i + 1);
if (customProperties.ContainsKey(strId))
{
tile.neighbor[i] = int.Parse(customProperties[strId]);
}
}
}
}
}

public void CustomizePrefab(GameObject prefab)
{
// Do nothing
}
}

上述代码继承了ICustomTiledImporter接口,用于获取自定义属性。首先获取对象的TileObject基础属性,然后判断其类型是否为“Level”,若是,则进行两件事:一是添加BoxCollider2D碰撞体,二是添加脚本TileData来存储自定义数据并绑定到对象上。自定义数据以字典形式存储,通过Tiled编辑器中的key获取value,并保存到TileData的数据中。TileData类的定义如下:

public class TileData : MonoBehaviour
{
public int Id = 0;
public int[] neighbor = new int[4];
}

至此,所有数据都已获取,后续的编程工作可根据需求自行完成。需要注意的是,这段代码在Tiled2Unity导出地图到Unity时调用,所有数据在导入时就已加载完成,与游戏运行无关。

此外,点击地图prefab,可看到它绑定了一个TiledMap脚本(此处应插入脚本绑定截图),从中可以获取层数“NumLayers”、横向格子数“NumTilesWide”、纵向格子数“NumTilesHigh”、每个格子的宽“TileWidth”、高“TileHeight”、导出缩放值“ExportScale”、地图像素宽“MapWidthInPixels”和高“MapHeightInPixels”。如果前面设计的最小单位是32,建议使用“MapWidthInPixels”和“MapHeightInPixels”计算横向和纵向所需的最小单位数量,而不是直接使用“NumTilesWide”和“NumTilesHigh”。

以上只是基础内容,要实现与地图的交互,还需考虑摄像机的Size和位置,防止地图边缘露出,特别是在地图可拖动缩放的情况下。