赞
踩
如果你玩过三国志这种类型的战旗游戏或者模拟城市、部落冲突、海岛奇兵这种模拟经营类的游戏,那么你对网格地图一定不会陌生。在这些游戏中,所有地图场景中的物体都是基于整齐的网格来记录位置等信息。如下图:
如果你还是感知不到什么是网格地图。俄罗斯方块或者贪吃蛇你一定不会陌生,物体的存在是依托于规整的网格地图而存在的。
还是一如既往,本篇文章为零基础小白文,如果你是小萌新,并且对网格地图感兴趣的话,可以学习本片文章,然后尝试创建自己的游戏吧!
本文章的最终显示效果为:
我们知道网格是由一个个格子组成的,所以第一步需要先创建出一个基本的模板:
创建一个脚本命名为Grid
,并定义一些我们需要修改的属性,由于本案例我想要创建一个有障碍物的地图,用来作为A*寻路的地图。所以需要下面的信息:
编写模板脚本:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class Grid : MonoBehaviour { public float gridWidght; public float girdHeight; public bool isHinder; public Color color; public Action OnClick; //当网格地图比较大时,每帧更新模板颜色比较消耗性能,可以修改为通过事件触发 void Update() { gameObject.GetComponent<MeshRenderer>().material.color=color; } //委托绑定模板点击事件 private void OnMouseDown() { OnClick?.Invoke(); } }
编写好脚本后,创建模板预制体,本案例就使用一个简单的方块来作为演示案例,为了保证可以区分每一个方格,大小缩放到0.9
,这样两个方格之间就会有空隙来分割不同的网格块。
创建好方格后,将Grid
脚本挂在到物体上,并设置相关的初始参数,具体如图:
接下来我们就需要封装一个网格创建的脚本,创建一个脚本命名为GridMeshCreate
,然后编写该脚本,为了实现创建网格地图的功能,我们需要获取到一些基本信息:
x
轴Grid
预制体的个数y
轴Grid
预制体的个数Grid
的位置:通过一个初始点,然后通过Grid
的长宽计算完成上面的信息的定义后,我们就可以编写脚本来实现网格创建的功能了,但是在此之前我们要思考一个问题,我们的创建的每一个Grid
的会完全一摸一样吗。答案肯定是不会。比如说,在一些模拟经营的游戏中,一个物体可能会对周围的环境造成一些影响,为了标识其影响范围,就需要通过不同颜色的网格来表示,如图所示:
在上面的图片中可以看出,我们需要对于不同区块的网格进行不同的信息展示,这就需要我们在网格创建时传入对应的处理逻辑。具体的代码结构为:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class GridMeshCreate : MonoBehaviour { [Serializable] public class MeshRange { public int widght; public int height; } //网格的宽高范围 public MeshRange meshRange; //生成网格起始点 public Vector3 startPos; //网格生成的父物体 public Transform parentTran; //模板预制体 public GameObject gridPre; private Grid[,] m_grids; public Grid[,] MeshGridData { get { return m_grids; } } //注册模板事件 public Action<Grid> gridEvent; /// <summary> /// 基于挂载组件的初始数据创建网格 /// </summary> public void CreateMesh() { if (meshRange.widght == 0 || meshRange.height == 0) { return; } ClearMesh(); m_grids = new Grid[meshRange.widght, meshRange.height]; for (int i = 0; i < meshRange.widght; i++) { for (int j = 0; j < meshRange.height; j++) { CreateGrid(i, j); } } } /// <summary> /// 重载,基于传入宽高数据来创建网格 /// </summary> /// <param name="height"></param> /// <param name="widght"></param> public void CreateMesh(int height,int widght) { if (widght == 0 || height == 0) { return; } ClearMesh(); m_grids = new Grid[widght, height]; for (int i = 0; i < widght; i++) { for (int j = 0; j < height; j++) { CreateGrid(i, j); } } } /// <summary> /// 根据位置创建一个基本的Grid物体 /// </summary> /// <param name="row">x轴坐标</param> /// <param name="column">y轴坐标</param> public void CreateGrid(int row,int column) { GameObject go = GameObject.Instantiate(gridPre, parentTran); Grid grid = go.GetComponent<Grid>(); float posX = startPos.x + grid.gridWidght * row; float posZ = startPos.z + grid.girdHeight * column; go.transform.position = new Vector3(posX, startPos.y, posZ); m_grids[row, column] = grid; gridEvent?.Invoke(grid); } /// <summary> /// 删除网格地图,并清除缓存数据 /// </summary> public void ClearMesh() { if (m_grids == null || m_grids.Length == 0) { return; } foreach (Grid grid in m_grids) { if (grid.gameObject != null) { Destroy(grid.gameObject); } } Array.Clear(m_grids, 0, m_grids.Length); } }
关于上面的脚本,有下面的两个关键点:
Grid
逻辑的方法关于网格的创建,在脚本中,我们写了一个重载的方法public void CreateMesh(int height,int widght)
,传入了网格宽和高,来方便通过后期通过脚本灵活的修改网格的宽和高(注意这里的宽和高指的是x
轴与y
轴格子的个数)
而对于Grid
逻辑对外暴露的实现,是利用在创建预制体时,为其添加一个委托事件。这样就可以在我们其他脚本创建时写入逻辑方法,而不需要对于这个封装好的网格地图创建类进行修改,而关于委托的一些知识,可以查看我之前的文章:
关于委托的文章:
在我们封装好网格创建的脚本后,就可以通过该脚本来做一个简单的网格地图来演示其用法
创建脚本命名为MainRun
,并进行编辑:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MainRun : MonoBehaviour { //获取网格创建脚本 public GridMeshCreate gridMeshCreate; //控制网格元素grid是障碍的概率 [Range(0,1)] public float probability; private void Update() { if (Input.GetKeyDown(KeyCode.Space)) { Run(); } } private void Run() { gridMeshCreate.gridEvent = GridEvent; gridMeshCreate.CreateMesh(); } /// <summary> /// 创建grid时执行的方法,通过委托传入 /// </summary> /// <param name="grid"></param> private void GridEvent(Grid grid) { //概率随机决定该元素是否为障碍 float f = Random.Range(0, 1.0f); Debug.Log(f.ToString()); grid.color = f <= probability ? Color.red : Color.white; grid.isHinder = f <= probability; //模板元素点击事件 grid.OnClick = () => { if (!grid.isHinder) grid.color = Color.blue; }; } }
可以看到,在Run
方法中是对于我们网格创建框架的一个调用,而在GridEvent(Grid grid)
中我们就可以写入我们的逻辑,并通过修改Grid
脚本中的代码来辅助完成我们需要的效果,比如本案例中在Grid
写入了一个点击事件,就可以在创建时通过委托定义该事件。
注意:
- 在脚本里面用到了
Random.Range(0, 1.0f)
来生成一个概率,注意不要写成Random.Range(0, 1)
,因为这样输出的结果只能为整数,即只能输出零
在编写完成脚本后,就可以将GridMeshCreate
脚本挂载到场景中的物体上,并根据注释进行相关的赋值。如图:
完成脚本挂载后点击运行,进入游戏后,点击空格键就会创建一张地图,在地图中会有随机的障碍物,以红色来标识障碍物,不可被点击,而白色区域点击后颜色变为蓝色,具体效果如图所示:
在使用网格地图创建的代码时,也许你发现了一个不方便的地方,就是里面的一个变量类型是类名Grid
,这样就会有局限性,我的预制体脚本只能挂载Grid
,这显然是不方便的,因此后续我对其进行了一些修改,将Grid
替换为GameObject
修改后的脚本为:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class GridMeshCreate : MonoBehaviour { [Serializable] public class MeshRange { public int horizontal; public int vertical; } [Header("网格地图范围")] public MeshRange meshRange; [Header("网格地图起始点")] private Vector3 startPos; [Header("创建地图网格父节点")] public Transform parentTran; [Header("网格地图模板预制体")] public GameObject gridPre; [Header("网格地图模板大小")] public Vector2 scale; private GameObject[,] m_grids; public GameObject[,] grids { get { return m_grids; } } //注册模板事件 public Action<GameObject, int, int> gridEvent; /// <summary> /// 基于挂载组件的初始数据创建网格 /// </summary> public void CreateMesh() { if (meshRange.horizontal == 0 || meshRange.vertical == 0) { return; } ClearMesh(); m_grids = new GameObject[meshRange.horizontal, meshRange.vertical]; for (int i = 0; i < meshRange.horizontal; i++) { for (int j = 0; j < meshRange.vertical; j++) { CreateGrid(i, j); } } } /// <summary> /// 重载,基于传入宽高数据来创建网格 /// </summary> /// <param name="height"></param> /// <param name="widght"></param> public void CreateMesh(int height, int widght) { if (widght == 0 || height == 0) { return; } ClearMesh(); m_grids = new GameObject[widght, height]; for (int i = 0; i < widght; i++) { for (int j = 0; j < height; j++) { CreateGrid(i, j); } } } /// <summary> /// 根据位置创建一个基本的Grid物体 /// </summary> /// <param name="row">x轴坐标</param> /// <param name="column">y轴坐标</param> public void CreateGrid(int row, int column) { GameObject go = GameObject.Instantiate(gridPre, parentTran); //T grid = go.GetComponent<T>(); float posX = startPos.x + scale.x * row; float posZ = startPos.z + scale.y * column; go.transform.position = new Vector3(posX, startPos.y, posZ); m_grids[row, column] = go; gridEvent?.Invoke(go, row, column); } /// <summary> /// 删除网格地图,并清除缓存数据 /// </summary> public void ClearMesh() { if (m_grids == null || m_grids.Length == 0) { return; } foreach (GameObject go in m_grids) { if (go != null) { Destroy(go); } } Array.Clear(m_grids, 0, m_grids.Length); } }
这里只是介绍了一个简单的案例,如果你觉得有用的话,可以尝试基于GridMeshCreate
脚本创建自己的网格地图生成方法,来做出自己想要的效果!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。