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

发表于2018-05-07
评论0 4.2k浏览
继续介绍随机地形生成算法系列,在上篇教程中,我们已经可以生成一个还看得过去的随机地形了。下面我们用gizmos画出了密密麻麻的正方体来展示整个地图。

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

每个点有两种状态:0和1,分别代表道路和障碍,我们把这四个点当成一个二进制数字0000。
当它们全部是1的时候,也就是1111=15。如图:

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



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

下面我们来定义它们的数据结构,新建一个脚本命名为MeshGenerator:
public class Node  
{  
    public Vector3 position;  
    public int vertexIndex = -1;<span style="white-space:pre;"> </span>//顶点索引,我们以后将会用到  
    public Node(Vector3 _pos)<span style="white-space:pre;">    </span>//节点的构造函数,需要传入它的位置  
    {  
        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方法:
oid 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!正是我们想要的效果~!
来自:https://blog.csdn.net/qq_17758883/article/details/78181769


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论