找回密码
 立即注册
查看: 426|回复: 1

[笔记] 初学者关于Unity的tilemap随机生成[1]

[复制链接]
发表于 2022-8-2 17:37 | 显示全部楼层 |阅读模式
这个系列用于记载一个初学者自学Unity的时候写出的一些脑瘫代码
首先了解一下Unity的TileMap
想要在脚本中引用TileMap对象,我们首先需要using UnityEngine.Tilemaps,像这样
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
然后我们要想随机化生成一个TileMap,必然要有一个函数向TileMap中添加Tile对象,那么我们便找到了SetTile()方法


可以看到,SetTile方法有两个参数 position 和 tile,其中position代表了物块在TileMap中的位置,tile则代表了物块的类型。、
那么思路很清晰了,首先随机化出一个二维数组,然后解析二维数组并向空TileMap中添加物块。
1.随机化二维数组

我们要做的是一个2D的横板像素游戏,因此我选择先生成地面,首先定义一个int类型的量,表示当前的地面坐标高度,然后定义两个量worldHeight和worldWidth,既世界的高和宽
int groundHeight = 10;
public int worldHeight = 100;
public int worldWidth = 100;
然后定义一个二维数组tileType和一个一维数组groundPos
int[,] tileType;
int[] groundPos
为什么要是10呢,因为我不可能让世界的生成从第0格开始。然后我使用rand随机数来不断改变这个值,并给二维数组赋值,同时在groundPos中记录下每列的地面高度(原因之后再说)
        for(int i = 0; i < worldWidth; i++)
        {
            groundHeight = Mathf.Max(groundHeight + Random.Range(-3, 3), 5);
            groundPos = groundHeight + 1;
            tileType[i, groundHeight] = 1;
            for(int j = 0; j < groundHeight; j++)
            {
                tileType[i, j] = 2;
            }
        }
可以看到,我用了一个Mathf.Max()函数来确定groundHeight的大小,为什么呢,因为游戏中地面不可能只有一层,那么我们就要让地面位于至少5高度的位置,这样地层厚度至少为4,就不会显得单薄。然后我又套了一个for循环,让所有的地面之下全都为纯色的土壤。
下一步我称之为美化地图,因为我们会发现,地面总是会出现这种情况:一块平整的地面上突然出现一个小坑,且只有一块,显得非常奇怪;也有可能是突出一块,同样很奇怪,而且不美观,那么我们就要想办法解决这个问题。
怎么办呢,首先我们可以访问这个二维数组,那么就代表着我们遍历一遍二维数组,寻找所有的两边是物块且中间没有物块的,或者是两边没有物块,中间有物块的情况,把中间的地面高度改为和两边相同,便可以达到平整地面的效果
        for(int i = 1; i < worldWidth - 1; i++)
        {
            if(tileType[i - 1, groundPos - 1] == 1 && tileType[i + 1, groundPos - 1] == 1 && tileType[i, groundPos - 1] == 0)
            {
                tileType[i, groundPos[i - 1] - 1] = 1;
                groundPos = groundPos[i - 1];
            }
        }
        for(int i = 1; i < worldWidth - 1; i++)
        {
            if(tileType[i - 1, groundPos - 1] == 0 && tileType[i + 1, groundPos - 1] == 0 && tileType[i, groundPos - 1] == 1)
            {
                for(int j = groundPos[i - 1]; j < groundPos; j++)
                {
                    tileType[i, j] = 0;
                }
                tileType[i, groundPos[i - 1] - 1] = 1;
                groundPos = groundPos[i - 1];
            }
        }
然后我们把地面高度之下的所有物块设定为土壤
        for(int i = 0; i < worldWidth; i++)
        {
            for(int j = 0; j < groundPos - 1; j++)
            {
                tileType[i, j] = 2;
            }
        }
最后一步,长草,这也是我之前存下地面位置的原因,虽然草和地面是两个TileMap里面的,但是我们可以用一个二维数组来存储,最后设定Tile时把草设置在第二个TileMap里面即可。
当然也不能每块地都长草,所以我们加上rand
        for(int i = 0; i < worldWidth; i++)
        {
            if(Random.Range(0, 5) == 1) tileType[i, groundPos] = 3;
        }
至此,用于存放物块信息的二维数组就设定好了
2.设定TileMap

首先我们引用两个TileMap,一个叫做tiles,另一个叫做otherThings
public Tilemap tiles;
public Tilemap otherThings;
然后再引用三种TileBase
public TileBase dirt_up, dirt_in;
public TileBase grass;
然后我们就可以开始快乐地生成我们的TileMap了!
生成方法也很简单,遍历我们之前设定好的二维数组,把每个数字对应设定为TileMap里面的Tile即可,注意TileMap中Tile的位置是一个Vector3,既空间坐标,但是由于我们做的是2D游戏,z轴设定为0即可。
void initData()
    {
        for(int i = 0; i < worldWidth; i++)
        {
            for(int j = 0; j < worldHeight; j++)
            {
                switch(tileType[i, j])
                {
                    case 1 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_up); break;}
                    case 2 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_in); break;}
                    case 3 : {otherThings.SetTile(new Vector3Int(i, j, 0), grass); break;}
                }
            }
        }
    }
这里我用的是switch,因为日后可能会有更多的物块加入到地图里面,全都用if的话需要全部判断一遍,耗时耗力。
最后注意别忘了在Start()方法中执行之前的方法。
至此一个简单的TileMap生成就做好了。最后我们放一下所有的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class WorldSpwaner : MonoBehaviour
{
    public Tilemap tiles;
    public Tilemap otherThings;
    public TileBase dirt_up, dirt_in;
    public TileBase grass;
    int[,] tileType;
    int[] groundPos;
    string[] otherThingsType;
    public int worldHeight = 100, worldWidth = 100;

    void Start()
    {
        initMapTilesInfo();
        initData();
    }

    public enum TileNum
    {
        none,
        Dirt_up,
        Dirt_in,
        Grass,
        Dirt_left,
        Dirt_right,
        Dirt_left_up,
        dirt_right_up
    }   
    /// <summary>
    /// 根据编号确定Tile的类型
    /// </summary>
    void initData()
    {
        for(int i = 0; i < worldWidth; i++)
        {
            for(int j = 0; j < worldHeight; j++)
            {
                switch(tileType[i, j])
                {
                    case 1 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_up); break;}
                    case 2 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_in); break;}
                    case 3 : {otherThings.SetTile(new Vector3Int(i, j, 0), grass); break;}
                    case 8 : {break;}
                    case 4 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_right); break;}
                    case 5 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_right_up); break;}
                    case 6 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_left); break;}
                    case 7 : {tiles.SetTile(new Vector3Int(i, j, 0), dirt_left_up); break;}
                }
            }
        }
    }
    /// <summary>
    /// 初始化地图Tile的编号
    /// </summary>
    void initMapTilesInfo()
    {
        tileType = new int[worldWidth,worldHeight];
        groundPos = new int[worldWidth];
        for(int i = 0; i < worldWidth; i++)
        {
            groundPos = 0;
            for(int j = 0; j < worldHeight; j++)
            {
                tileType[i, j] = 0;
            }
        }
        int groundHeight = 10;
        //初步生成地图
        for(int i = 0; i < worldWidth; i++)
        {
            groundHeight = Mathf.Max(groundHeight + Random.Range(-3, 3), 5);
            groundPos = groundHeight + 1;
            tileType[i, groundHeight] = 1;
            for(int j = 0; j < groundHeight; j++)
            {
                tileType[i, j] = 2;
            }
        }
        //美化生成的地图
        for(int i = 1; i < worldWidth - 1; i++)
        {
            if(tileType[i - 1, groundPos - 1] == 1 && tileType[i + 1, groundPos - 1] == 1 && tileType[i, groundPos - 1] == 0)
            {
                tileType[i, groundPos[i - 1] - 1] = 1;
                groundPos = groundPos[i - 1];
            }
        }
        for(int i = 1; i < worldWidth - 1; i++)
        {
            if(tileType[i - 1, groundPos - 1] == 0 && tileType[i + 1, groundPos - 1] == 0 && tileType[i, groundPos - 1] == 1)
            {
                for(int j = groundPos[i - 1]; j < groundPos; j++)
                {
                    tileType[i, j] = 0;
                }
                tileType[i, groundPos[i - 1] - 1] = 1;
                groundPos = groundPos[i - 1];
            }
        }
        for(int i = 0; i < worldWidth; i++)
        {
            for(int j = 0; j < groundPos - 1; j++)
            {
                tileType[i, j] = 2;
            }
        }
        //长草
        for(int i = 0; i < worldWidth; i++)
        {
            if(Random.Range(0, 5) == 1) tileType[i, groundPos] = 3;
        }
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2022-8-2 17:39 | 显示全部楼层
这个代码有点问题,不完整,注释相关代码后正常 ,使用方式为在grid游戏对象上绑定这个代码 并把2个tileMap对象拖入代码变量上,再把地图图片资源绑定到tileBase变量上点击运行才可正常生成
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-21 20:47 , Processed in 0.144761 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表