游戏中的随机地形生成算法(二)

在上篇教程中,我们已经小有成就。我们用gizmos画出了密密麻麻的正方体来展示整个地图。

现在想象一下,我们把每个正方体缩小为一个点,就会出现下面的情况:



每个点有两种状态:0和1,分别代表道路和障碍,我们把这四个点当成一个二进制数字0000。

当它们全部是1的时候,也就是1111=15。如图:




那我们可以得到的信息就是这四个点的组合方式有16种。每一种组合对应一种网格(mesh),而网格是由三角形组成的,下面是几个示例:







可以观察到的是,除了四个顶点,边的中点也用来组成网格。我们在这里定义顶点为控制节点,边的中点为节点。每个控制节点拥有两个节点:上方节点和右方节点。

下面我们来定义它们的数据结构,新建一个脚本命名为MeshGenerator:

    public class Node
    {
        public Vector3 position;
        public int vertexIndex = -1;	//顶点索引,我们以后将会用到

        public Node(Vector3 _pos)	//节点的构造函数,需要传入它的位置
        {
            position = _pos;
        }
    }

public class ControlNode : Node
    {
        public bool isActive;       //控制节点是否激活
        public Node above, right;   //每个控制节点拥有两个节点

        public ControlNode(bool _isActive, Vector3 _pos, int squareSize)
            : base(_pos)            //从父类构造函数传入位置,squareSize为四个控制节点形成的矩形的边长
        {
            isActive = _isActive;
            above = new Node(position + Vector3.forward * squareSize / 2f);
            right = new Node(position + Vector3.right * squareSize / 2f);

        }
    }

    public class Square
    {
        public ControlNode topLeft, topRight, bottomLeft, bottomRight;      //一个矩形拥有四个控制节点  
        public Node centerTop, centerLeft, centerRight, centerBottom;       //还有四个节点

        public Square(ControlNode _topLeft, ControlNode _topRight, ControlNode _bottomLeft, ControlNode _bottomRight)   //我们只需要定义四个控制节点
        {   
            topLeft = _topLeft;        
            topRight = _topRight;
            bottomLeft = _bottomLeft;
            bottomRight = _bottomRight;

            //控制节点包含节点
            centerTop = topLeft.right;
            centerBottom = bottomLeft.right;
            centerLeft = bottomLeft.above;
            centerRight = bottomRight.above;
        }
    }

    public class SquareGrid
    {
        public Square[,] squares;   //代表了整张网格的信息

        public SquareGrid(int[,] map, int squareSize)   //构造函数传入Map数组信息和单个矩形的边长
        {
            int nodeCountX = map.GetLength(0);  //X方向上节点的数量
            int nodeCountY = map.GetLength(1);  //Y方向上节点的数量
            float mapWidth = nodeCountX * squareSize;   //地图的宽
            float mapHeight = nodeCountY * squareSize;  //地图的高

            ControlNode[,] controlNodes=new ControlNode[nodeCountX,nodeCountY]; //用来存储整张地图上的控制节点

            for (int x = 0; x < nodeCountX;x++ )
            {
                for (int y = 0; y < nodeCountY; y++)
                {
                    Vector3 pos=new Vector3(-mapWidth/2f+x*squareSize+squareSize/2,0,-mapHeight/2f+y*squareSize+squareSize/2);
                    controlNodes[x,y]=new ControlNode(map[x,y]==1,pos,squareSize);
                }
            }

            squares=new Square[nodeCountX-1,nodeCountY-1];  //矩形的数量总是比节点的数量少1

            for (int x = 0; x < nodeCountX-1;x++ )
            {
                for (int y = 0; y < nodeCountY-1; y++)
                {
                    squares[x,y]=new Square(controlNodes[x,y+1],controlNodes[x+1,y+1],controlNodes[x,y],controlNodes[x+1,y]);
                }
            }

        }
    }

很好,我们现在基本完成了这节内容,我们已经得到了使用SquareGrid存储的地图信息。

现在要做的是给MeshGenerator类添加一个字段和一个方法:

public SquareGrid squareGrid;


    public void GenerateMesh(int[,] map, int squareSize)
    {
        squareGrid = new SquareGrid(map, squareSize);
    }


接着我们需要在MapGenerator里调用这个方法,对GenerateMap函数作出如下改动:

void GenerateMap()
    {
        map=new int[width,height];
        RandomFillMap();    //随机生成地图

        for (int i = 0; i < 4; i++)
        {
            SmoothMap();
        }

        MeshGenerator meshGen = GetComponent<MeshGenerator>();
        meshGen.GenerateMesh(map, 1);
    }

嗯,你应该注意到了,我们应该把这两个脚本挂在同一物体上,否则是无法直接GetComponent的。

先别急着从这个关闭代码界面,我们还要把OnDrawGizmos函数注释掉,然后在MeshGenerator中重新定义一个OnDrawGizmos方法:


	void OnDrawGizmos() {
		if (squareGrid != null) {
			for (int x = 0; x < squareGrid.squares.GetLength(0); x ++) {
				for (int y = 0; y < squareGrid.squares.GetLength(1); y ++) {

					Gizmos.color = (squareGrid.squares[x,y].topLeft.active)?Color.black:Color.white;
					Gizmos.DrawCube(squareGrid.squares[x,y].topLeft.position, Vector3.one * .4f);

					Gizmos.color = (squareGrid.squares[x,y].topRight.active)?Color.black:Color.white;
					Gizmos.DrawCube(squareGrid.squares[x,y].topRight.position, Vector3.one * .4f);

					Gizmos.color = (squareGrid.squares[x,y].bottomRight.active)?Color.black:Color.white;
					Gizmos.DrawCube(squareGrid.squares[x,y].bottomRight.position, Vector3.one * .4f);

					Gizmos.color = (squareGrid.squares[x,y].bottomLeft.active)?Color.black:Color.white;
					Gizmos.DrawCube(squareGrid.squares[x,y].bottomLeft.position, Vector3.one * .4f);


					Gizmos.color = Color.grey;
					Gizmos.DrawCube(squareGrid.squares[x,y].centreTop.position, Vector3.one * .15f);
					Gizmos.DrawCube(squareGrid.squares[x,y].centreRight.position, Vector3.one * .15f);
					Gizmos.DrawCube(squareGrid.squares[x,y].centreBottom.position, Vector3.one * .15f);
					Gizmos.DrawCube(squareGrid.squares[x,y].centreLeft.position, Vector3.one * .15f);

				}
			}
		}
	}

同样的,这里我们首先判断每个点是否激活,然后确定它们的颜色,再画出它们。

我们来看看效果吧:




看不太清楚,放大看一下:




NICE!!正是我们想要的效果~!

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值