Unity makes a grid map generation component

foreword

If you have played battle flag games of the Three Kingdoms type or simulation management games such as Sim City, Clash of Clans, and Boom Beach, then you must be familiar with grid maps. In these games, all objects in the map scene are based on a neat grid to record information such as location. As shown below:
I found a picture online

If you still can't perceive what a grid map is. You must not be unfamiliar with Tetris or Snake. The existence of objects depends on a regular grid map.

As always, this article has zero basic content. If you are new to Xiaomeng and are interested in grid maps, you can study this article and try to create your own game!

The final display effect of this article is:
insert image description here

1. Create the basic units that make up the grid

We know that grids are made up of grids, so the first step is to create a basic template:

Create a script named Gridand define some properties that we need to modify. Because of this case, I want to create a map with obstacles to use as a map for A* pathfinding. So the following information is required:

  • Template width
  • Template height
  • Template color
  • Whether the template is an obstacle (identified by color)
  • Template click event (template color conversion)

Write the template script:

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

}

After writing the script, create a template prefab. In this case, a simple square is used as a demonstration case. In order to ensure that each square can be distinguished, the size is scaled 0.9so that there will be a gap between the two squares to separate different squares. grid block.

After creating the grid, Gridhang the script on the object and set the relevant initial parameters, as shown in the figure:

insert image description here

2. Edit the grid creation script

Next, we need to encapsulate a grid creation script, create a script named GridMeshCreate, and then write the script. In order to realize the function of creating a grid map, we need to obtain some basic information:

  • The width of the created grid: the number of xaxis Gridprefabs
  • Create Grid Height: Number of yAxis GridPrefabs
  • Create a position in the grid Grid: pass an initial point, then pass Gridthe length and width calculation

After completing the definition of the above information, we can write scripts to implement the function of grid creation, but before that we have to think about a question, will each of our creations Gridbe exactly the same. The answer is definitely not. For example, in some business simulation games, an object may have some influence on the surrounding environment. In order to identify its influence range, it needs to be represented by grids of different colors, as shown in the figure:

Please add image descriptionAs can be seen in the above picture, we need to display different information for the grids of different blocks, which requires us to pass in the corresponding processing logic when the grid is created. The specific code structure is:

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

There are two key points about the script above:

  • Create a grid
  • Methods for exposing processing Gridlogic to the outside world

Regarding the creation of the grid, in the script, we wrote an overloaded method public void CreateMesh(int height,int widght), passing in the width and height of the grid, to facilitate the flexible modification of the width and height of the grid through the script later (note the width and height here Refers to the number of xaxes and yaxis grids)

For Gridthe implementation of logic exposure, it is used to add a delegate event to the prefab when it is created. In this way, logic methods can be written when our other scripts are created, without the need to modify this encapsulated grid map creation class, and for some knowledge about delegation, you can check my previous article:

Articles about delegation:

3. Map generation case

After we encapsulate the script for grid creation, we can use the script to make a simple grid map to demonstrate its usage

Create a script named MainRunand edit it:

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

    }
}

It can be seen that in the Runmethod is a call to our grid creation framework, and in GridEvent(Grid grid)it we can write our logic, and modify Gridthe code in the script to assist in completing the effect we need, such as in this case in GridWrite a click event, you can define the event through the delegate at creation time.

Notice:

  • It is used in the script Random.Range(0, 1.0f)to generate a probability, be careful not to write Random.Range(0, 1)it, because the output result can only be an integer, that is, only zero can be output

After writing the script, you can GridMeshCreatemount the script to the objects in the scene, and perform related assignments according to the comments. As shown in the figure:
insert image description here

After the script is mounted, click to run. After entering the game, click the space bar to create a map. There will be random obstacles in the map. The obstacles are marked in red and cannot be clicked. The color of the white area changes after clicking. It is blue, and the specific effect is as shown in the figure:
insert image description here

Replenish

When using the code created by the grid map, maybe you found an inconvenience, that is, one of the variable types in it is the class name Grid, so there will be limitations. My prefab script can only be mounted Grid, which is obviously Inconvenient, so I made some modifications to it later and Gridreplaced it with the GameObjectmodified script:

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

}

Summarize

Here is just a simple case, if you find it useful, you can try to GridMeshCreatecreate your own grid map generation method based on script to make the effect you want!

Guess you like

Origin blog.csdn.net/xinzhilinger/article/details/119302097