一篇文章教会你用Unity制作网格地图生成组件

目录
  • 前言
  • 1,创建组建出网格的基本单元
  • 2,编辑网格创建脚本
  • 3,地图生成案例
  • 总结

前言

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

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

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

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

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<MeshRenderer>().material.color=color;
    }
    //委托绑定模板点击事件
    private void OnMouseDown()
    {
        OnClick?.Invoke();
    }

}

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

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

2,编辑网格创建脚本

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

关于委托的文章:

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();
    }

    /// <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脚本挂载到场景中的物体上,并根据注释进行相关的赋值。如图:

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

总结

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

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

(0)

相关推荐

  • Unity3D选择本地图片并加载

    本文实例为大家分享了Unity3D选择本地图片并加载的具体代码,供大家参考,具体内容如下 ①找到System.Windows.Forms.dll:在unity的安装目录中找到它,如 E:\ProgramFiles(x86)\Unity\Editor\Data\Mono\lib\mono\2.0 ②设置.NET 2.0集:Untiy默认是.NET 2.0 Subset.在Edit->Project Settings->Player->OtherSettings中修改 ③任意打开一项目,新建

  • Unity制作小地图和方向导航

    一.unity方向导航制作 设计要求是方向导航随着鼠标旋转转换方向,效果图如下: 具体的实现方法主要有两个步骤,分别为UI设计和脚本编写.我的设计思路是这个控件分为两层,第一层为东西南北指示层,第二层为图标指示层,这里我的图标采用圆形图标,方向指示这里采用控制图标旋转的方式实现,层级关系如下: 首先创建父节点1,然后在父节点下创建子节点2,3:最后调整好位置. 第二步脚本编写,脚本如下: using UnityEngine; using System.Collections; using Uni

  • 一篇文章教会你用Unity制作网格地图生成组件

    目录 前言 1,创建组建出网格的基本单元 2,编辑网格创建脚本 3,地图生成案例 总结 前言 如果你玩过三国志这种类型的战旗游戏或者模拟城市.部落冲突.海岛奇兵这种模拟经营类的游戏,那么你对网格地图一定不会陌生.在这些游戏中,所有地图场景中的物体都是基于整齐的网格来记录位置等信息.如下图: 如果你还是感知不到什么是网格地图.俄罗斯方块或者贪吃蛇你一定不会陌生,物体的存在是依托于规整的网格地图而存在的. 还是一如既往,本篇文章为零基础小白文,如果你是小萌新,并且对网格地图感兴趣的话,可以学习本片文

  • 一篇文章教会你使用java爬取想要的资源

    目录 说明 方法摘要 常用的Element节点方法 实战:爬取B站番剧 Maven 代码 说明 简介: 你还在为想要的资源而获取不到而烦劳吗?你还在为你不会python而爬取不到资源而烦劳吗?没关系,看完我这一篇文章你就会学会用java爬取资源,从此不会因此而烦劳,下面我会以爬取京东物品来进行实战演示!!! 方法摘要 方法 方法说明 adoptNode(Node source) 试图把另一文档中的节点采用到此文档. createAttribute(String name) 创建指定名称的Attr

  • 一篇文章带你使用Typescript封装一个Vue组件(简单易懂)

    一.搭建项目以及初始化配置 vue create ts_vue_btn 这里使用了vue CLI3自定义选择的服务,我选择了ts.stylus等工具.然后创建完项目之后,进入项目.使用快捷命令code .进入Vs code编辑器(如果没有code .,需要将编辑器的bin文件目录地址放到环境变量的path中).然后,我进入编辑器之后,进入设置工作区,随便设置一个参数,这里比如推荐设置字号,点下.这里是为了生成.vscode文件夹,里面有个json文件. 我们在开发项目的时候,项目文件夹内的文件很

  • 一篇文章教会你PYcharm的用法

    目录 一.界面介绍 二.设置中文(无需汉化包) 三.常用快捷键 一些常用设置: 四.Python 标识符和关键字 1.标识符 2. 关键字 五.行和缩进 六.Python 引号 七.Python注释 1.单行注释 2.多行注释 八.Python空行 九.输入和输出 1.print 输出 2.input 输入 十.多行语句 总结 一.界面介绍 文件导航区域 能够 浏览/定位/打开 项目文件 文件编辑区域 能够 编辑 当前打开的文件 控制台区域 能够: 输出程序执行内容 跟踪调试代码的执行 1.右上

  • 一篇文章教会你部署vue项目到docker

    目录 第一步 第二步,生成镜像 第三步,生成(启动)一个容器 总结 让我在5分钟你教不会你把项目部署到docker,前提是你得服务器装有docker,这里方便装宝塔,大佬当我没说. 大致分为3步: 第一是弄一个nginx的配置文件然后构建镜像的时候把它放进去取代原来的配置和写Dockerfile生成镜像的一些配置 第二是生成镜像 第三是生成启动容器,ok开始,顺便抛砖引玉 第一步 server { listen 80; server_name localhost; # docker服务宿主机的i

  • 一篇文章教会你使用gs_restore导入数据

    目录 背景信息 命令格式 示例 总结 背景信息 gs_restore是openGauss提供的针对gs_dump导出数据的导入工具.通过此工具可将由gs_dump生成的导出文件进行导入. gs_restore工具由操作系统用户omm执行. 主要功能包含: 导入到数据库 如果连接参数中指定了数据库,则数据将被导入到指定的数据库中.其中,并行导入必须指定连接的密码.导入时生成列会自动更新,并像普通列一样保存. 导入到脚本文件 如果未指定导入数据库,则创建包含重建数据库所必须的SQL语句脚本并写入到文

  • 一篇文章教你学会使用Python绘制甘特图

    目录 优点 局限 一日一书 用来制作甘特图的专业工具也不少,常见的有:Microsoft Office Project.GanttProject.WARCHART XGantt.jQuery.Gantt.Excel等,网络上也有一些优质工具支持在线绘制甘特图. 可是这种现成的工具,往往也存在一些弊端,让编程人员不知所措.比如说,花里胡哨的UI,让人目不暇接,不知点哪个才好: 比如说,有些基于浏览器的图表需要掌握HTML.JS等编程语言,只会点Python的我直接被劝退: 再比如,进来就是注册.登

  • 一篇文章入门Python生态系统(Python新手入门指导)

    译者按:原文写于2011年末,虽然文中关于Python 3的一些说法可以说已经不成立了,但是作为一篇面向从其他语言转型到Python的程序员来说,本文对Python的生态系统还是做了较为全面的介绍.文中提到了一些第三方库,但是Python社区中强大的第三方库并不止这些,欢迎各位Pytonistas补充. •原文链接:http://mirnazim.org/writings/python-ecosystem-introduction/ •译文链接:http://codingpy.com/artic

  • 一篇文章就能了解Rxjava

    前言: 第一次接触RxJava是在前不久,一个新Android项目的启动,在评估时选择了RxJava.RxJava是一个基于事件订阅的异步执行的一个类库.听起来有点复杂,其实是要你使用过一次,就会大概明白它是怎么回事了!为是什么一个Android项目启动会联系到RxJava呢?因为在RxJava使用起来得到广泛的认可,又是基于Java语言的.自然会有善于组织和总结的开发者联想到Android!没错,RxAndroid就这样在RxJava的基础上,针对Android开发的一个库.今天我们主要是来讲

  • php下删除一篇文章生成的多个静态页面

    复制代码 代码如下: //– 删除一篇文章生成的多个静态页面 //– 生成的文章名为 5.html 5_2.html 5_3.html /*------------------ */ function delStaticHtml ($article_id) { global $db; $sql = "SELECT `post_time` FROM `@__article` WHERE `article_id` = '{$article_id}'"; $art = $db->get

随机推荐