Unity2D游戏项目导出地图数据

需求背景

最近着手了一个使用Unity构建的2D手机游戏项目,项目服务端需要设计简单的AI,AI的基础是寻路,寻路依赖地图数据。

问题分析

Unity的3d开发环境中,原生自带了Navigation的组件,该组件可以快速的在Unity3D项目中生成NavMesh数据,并且同时支持Unity内置的寻路功能。但是对于Unity2D项目并没有类似的功能,所以得要自己想办法、造轮子。

需求也很明确:

1、能导出0/1方格地图数据提供给A*寻路,0表示区域可行走,1表示区域有障碍;(文末会提到为何不沿用NavMesh数据格式)

2、对于地图的变更,仅修改参数,不用修改代码,一键操作生成需要的地图数据文件;

3、能够兼容一些需求变更,比如击碎物,机关门之类;

着手解决

在Unity的AssetStore中搜索A*会出现很多相关的内容,下面经过实际操作并且推荐的是:

A* Pathfinding Project Pro

(写这篇博文时该插件售价100刀…)好在它的官网上有免费版本,最后经过使用后发现免费版本已经可以满足需求。

具体对样例操作步骤可以参考如下(演示环境是Unity_Windows64bit_5.3.5f1)。

一、Unity的下载、2D项目创建、导入我们下载的免费插件(详细步骤略),最终在Assets目录下多出目录:

unity_map_step1

 

二、接着我们选取里面自带的2D样例,双击打开Scene:

unity_map_step2

三、在Hierarchy标签页下选取A*这个GameObject,在Inspector这个标签页下会出现Astar Path (Script):

unity_map_step3

需要注意的是,若嵌套在自己的项目,例如你的项目地图信息都是挂载在Level(不要纠结于名字,Level只是官方的教程中经常使用来挂载的地图信息的GameObject名)下面,那么需要给Level增加一个Astar Path的脚本。或者你也可以参考样例中做法,新建一个名字为A*的GameObject,把脚本挂到A*下面。

第一次使用需要点击Add New Graph->Grid Graph来增加一个基于Grid的地图数据(上面的样例已经是添加好的)。下面说一下脚本里需要关心的配置:

Width(nodes) – 宽方向有多少节点(样例中的x轴方向);

Depth(nodes) – 高方向有多少节点(样例中的y轴方向);

node size – 节点的大小,默认是1m,用来调节精细度的主要指标;

Aspect Ratio – 节点的伸缩放,配合Isometric Angle使用,比较适合于Isometric Game配置,一般正交镜头2D项目默认1即可;

Center、Rotation – 用来标记在Scene中显示地图信息的位置,需要和项目地图在同一平行面(样例中就是与x,y轴的面平行,z轴可以随意调整);

Connections – 默认8即可,表示方格的8个连接方向;

Use 2D Physics – 勾选上;

Collider Type – 我在实际项目中选择了Sphere,因为默认的Ray出现了“意想不到的缝隙”,导致的原因应该是地图拼接的不太好,具体未查;

Diameter – 碰撞检测求的直径,若场景不太规则,可以把默认值1调小;

Mask – 设置成和地图的layer一样,默认是Default;

四、点击一下Astar Path脚本最下方的“Scan”按钮,出现红色的网格图,红点代表不可行走,红线代表可以行走

unity_map_step4

五、地图数据的烘焙已经做完,接下来取出我们需要的数据:

导出的原理就是 – 既然红色的网格图插件都画出来了,那么它肯定以自定义的数据结构持有该地图数据,所以我们可以从自定义结构体中找到地图数据,然后将数据处理成需要的格式。所幸官网文档比较全(赞),里面对于自定义结构体的文档比较清晰,可以节约不少人力。

下面是导出的C#代码,可以将其放在AstarPathfindingEditor\Editor目录下。

//file:exportMap.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using UnityEngine.SceneManagement;
using Pathfinding;

public class ExportMap
{
    [MenuItem("Tools/Export Map")]
    static void Export()
    {
        //新建地图文件路径
        string tmpPath =  Application.dataPath + "/" + SceneManager.GetActiveScene().name + ".txt";
        StreamWriter tmpStreamWriter = new StreamWriter(tmpPath);

        AstarData data = AstarPath.active.astarData;
        GridGraph grid = data.gridGraph;

        //文件第一行:录入文件说明 x列有多少格子,y行有多少格子,每个格子的长x,每个格子的高y
        tmpStreamWriter.WriteLine(grid.width + " " + grid.depth + " " + grid.nodeSize + " " + grid.nodeSize);
        //文件后续行:录入节点信息
        string linecontent = "";
        for (int i = 0; i < grid.CountNodes(); i++)
        {
            GridNode node = grid.nodes[i];
            if ((i + 1) % grid.width == 0)
            {
                linecontent += (node.Walkable ? "0" : "1") + " ";
                tmpStreamWriter.WriteLine(linecontent);
                linecontent = "";
            }
            else
            {
                linecontent += (node.Walkable ? "0" : "1") + " ";
            }
        }

        tmpStreamWriter.WriteLine();
        tmpStreamWriter.Flush();
        tmpStreamWriter.Close();
    }
}
六、点击Tools->Export Map生成地图文件

unity_map_step6

进入项目文件夹会发现Assets目录下会出现2D.txt,该文件就是我们需要的地图数据。

关于NavMesh

NavMesh的核心数据结构在Unity中是NavMeshTriangulation,其包含3大数据块:

areas – 区块数组,数组长度等于indices的个数;

indices – 连接图,表示那些顶点组成了一个区块;

vertices – 顶点信息,核心下标索引与x,y,z的坐标;

这是一个很明显连接图,用来做AI寻路是没有问题的,但是一旦涉及到了障碍物打碎之类的需求感觉有点棘手 – 障碍物打碎会打乱原来的区块与顶点,这些在unity中可以动态烘焙生成新的连接图,但是非Unity服务端很难处理(得要研究一系列关于NavMesh相关的算法)。若是0/1方格地图,理想情况下我把障碍物的坐标点从1->0就完成了修改,这是另一个重要的原因选择了0/1方格地图。

至此地图数据导出的工作就完成了,后续你有100种方法把地图数据文件生成/移动到服务端目录,也有至少10种方法读取需要的数据然后使用,这里就不累述了。

(全文结束)


转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment

Your email address will not be published.