一篇文章教会你用Unity制作网格地图生成组件
admin
2023-05-02 02:10:53
目录

    前言

    如果你玩过三国志这种类型的战旗游戏或者模拟城市、部落冲突、海岛奇兵这种模拟经营类的游戏,那么你对网格地图一定不会陌生。在这些游戏中,所有地图场景中的物体都是基于整齐的网格来记录位置等信息。如下图:

    如果你还是感知不到什么是网格地图。俄罗斯方块或者贪吃蛇你一定不会陌生,物体的存在是依托于规整的网格地图而存在的。

    还是一如既往,本篇文章为零基础小白文,如果你是小萌新,并且对网格地图感兴趣的话,可以学习本片文章,然后尝试创建自己的游戏吧!

    本文章的最终显示效果为:

    1,创建组建出网格的基本单元

    我们知道网格是由一个个格子组成的,所以第一步需要先创建出一个基本的模板:

    创建一个脚本命名为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().material.color=color;
        }
        //委托绑定模板点击事件
        private void OnMouseDown()
        {
            OnClick?.Invoke();
        }
    
    }
    

    编写好脚本后,创建模板预制体,本案例就使用一个简单的方块来作为演示案例,为了保证可以区分每一个方格,大小缩放到0.9,这样两个方格之间就会有空隙来分割不同的网格块。

    创建好方格后,将Grid脚本挂在到物体上,并设置相关的初始参数,具体如图:

    2,编辑网格创建脚本

    接下来我们就需要封装一个网格创建的脚本,创建一个脚本命名为GridMeshCreate,然后编写该脚本,为了实现创建网格地图的功能,我们需要获取到一些基本信息:

    完成上面的信息的定义后,我们就可以编写脚本来实现网格创建的功能了,但是在此之前我们要思考一个问题,我们的创建的每一个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 gridEvent;
    
        /// 
        /// 基于挂载组件的初始数据创建网格
        /// 
        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);
    
                }
            }
        }
    
        /// 
        /// 重载,基于传入宽高数据来创建网格
        /// 
        /// 
        /// 
        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);
                }
            }
        }
    
        /// 
        /// 根据位置创建一个基本的Grid物体
        /// 
        /// x轴坐标
        /// y轴坐标
        public void CreateGrid(int row,int column)
        {
            GameObject go = GameObject.Instantiate(gridPre, parentTran);
            Grid grid = go.GetComponent();
    
            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);
        }
    
        /// 
        /// 删除网格地图,并清除缓存数据
        /// 
        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);
        }
    }
    

    关于上面的脚本,有下面的两个关键点:

    关于网格的创建,在脚本中,我们写了一个重载的方法public void CreateMesh(int height,int widght),传入了网格宽和高,来方便通过后期通过脚本灵活的修改网格的宽和高(注意这里的宽和高指的是x轴与y轴格子的个数)

    而对于Grid逻辑对外暴露的实现,是利用在创建预制体时,为其添加一个委托事件。这样就可以在我们其他脚本创建时写入逻辑方法,而不需要对于这个封装好的网格地图创建类进行修改,而关于委托的一些知识,可以查看我之前的文章:

    关于委托的文章:

    C# 委托基础与入门

    3,地图生成案例

    在我们封装好网格创建的脚本后,就可以通过该脚本来做一个简单的网格地图来演示其用法

    创建脚本命名为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();
        }
    
        /// 
        /// 创建grid时执行的方法,通过委托传入
        /// 
        /// 
        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脚本挂载到场景中的物体上,并根据注释进行相关的赋值。如图:

    完成脚本挂载后点击运行,进入游戏后,点击空格键就会创建一张地图,在地图中会有随机的障碍物,以红色来标识障碍物,不可被点击,而白色区域点击后颜色变为蓝色,具体效果如图所示:

    总结

    这里只是介绍了一个简单的案例,如果你觉得有用的话,可以尝试基于GridMeshCreate脚本创建自己的网格地图生成方法,来做出自己想要的效果!

    到此这篇关于Unity制作网格地图生成组件的文章就介绍到这了,更多相关Unity网格地图生成组件内容请搜索趣讯吧以前的文章或继续浏览下面的相关文章希望大家以后多多支持趣讯吧!

    相关内容

    热门资讯

    demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
    世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
    苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
    长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
    猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
    应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
    脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
    世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
    阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
    demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
    苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
    北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
    阿西吧是什么意思 阿西吧相当于... 即使你没有受到过任何外语培训,你也懂四国语言。汉语:你好英语:Shit韩语:阿西吧(아,씨발! )日...
    长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
    应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
    猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
    脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
    世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...