1. 程式人生 > >遊戲中的隨機地形生成演算法(三)

遊戲中的隨機地形生成演算法(三)

在上篇教程中,我們已經把Map中的每個點都實實在在的畫了出來,四個點形成一個正方形。其中,正方形的每條邊都有一箇中點。

那麼,這節課我們來把該連在一起的點連起來,繪製我們的mesh。

    public class Square
    {
        public ControlNode topLeft, topRight, bottomLeft, bottomRight;      //一個矩形擁有四個控制節點  
        public Node centerTop, centerLeft, centerRight, centerBottom;       //還有四個節點
        
        public int configuration;

        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;

            if (topLeft.isActive)
                configuration += 8;
            if (topRight.isActive)
                configuration += 4;
            if (bottomRight.isActive)
                configuration += 2;
            if (bottomLeft.isActive)
                configuration += 1;
        }
    }

首先完善一下我們的Square類,增加了一個int變數configuration,我們用它來記錄當前矩形控制節點的啟用情況,

如果左上啟用:對應1000=8,

如果右上啟用:對應0100=4;

如果左下啟用:對應0010=2;

如果右下啟用:對應0001=1;

然後我們定義一個分形函式TriangulateSquare,顧名思義,用來把一個Square分割成三角形。

void TriangulateSquare(Square square)
    {
        switch (square.configuration)
        {
            //1 point
            case 1:
                MeshFromPoints(square.centerBottom, square.bottomLeft, square.centerLeft);
                break;
            case 2:
                MeshFromPoints(square.centerRight, square.bottomRight, square.centerBottom);
                break;
            case 4:
                MeshFromPoints(square.centerTop, square.topRight, square.centerRight);
                break;
            case 8:
                MeshFromPoints(square.topLeft, square.centerTop, square.centerLeft);
                break;

            //2 points
            case 3:
                MeshFromPoints(square.centerRight, square.bottomRight, square.bottomLeft, square.centerLeft);
                break;
            case 6:
                MeshFromPoints(square.centerTop, square.topRight, square.bottomRight, square.centerBottom);
                break;
            case 9:
                MeshFromPoints(square.topLeft, square.centerTop, square.centerBottom, square.bottomLeft);
                break;
            case 12:
                MeshFromPoints(square.topLeft, square.topRight, square.centerRight, square.centerLeft);
                break;
            case 5:
                MeshFromPoints(square.centerTop, square.topRight, square.centerRight, square.centerBottom, square.bottomLeft, square.centerLeft);
                break;
            case 10:
                MeshFromPoints(square.topLeft, square.centerTop, square.centerRight, square.bottomRight, square.centerBottom, square.centerLeft);
                break;

            // 3 point:
            case 7:
                MeshFromPoints(square.centerTop, square.topRight, square.bottomRight, square.bottomLeft, square.centerLeft);
                break;
            case 11:
                MeshFromPoints(square.topLeft, square.centerTop, square.centerRight, square.bottomRight, square.bottomLeft);
                break;
            case 13:
                MeshFromPoints(square.topLeft, square.topRight, square.centerRight, square.centerBottom, square.bottomLeft);
                break;
            case 14:
                MeshFromPoints(square.topLeft, square.topRight, square.bottomRight, square.centerBottom, square.centerLeft);
                break;

            // 4 point:
            case 15:
                MeshFromPoints(square.topLeft, square.topRight, square.bottomRight, square.bottomLeft);
                break;
        }
        
    }
在這裡,我們使用到了剛剛定義的configuration來分辨16種情形,在每種情形中,我們又使用了MeshFromPoints這個方法來構建mesh。
    void MeshFromPoints(params Node[] nodes)
    {
        AssignVerticals(nodes);//在建立三角形之前,先儲存一下它的頂點資訊

        if (nodes.Length>=3)
        {
            CreateTriangle(nodes[0], nodes[1], nodes[2]);
        }
        if (nodes.Length >= 4)
        {
            CreateTriangle(nodes[0], nodes[2], nodes[3]);
        }
        if (nodes.Length >= 5)
        {
            CreateTriangle(nodes[0], nodes[3], nodes[4]);
        }
        if (nodes.Length >= 6)//如果有6個點,那我們需要建立4個三角形
        {
            CreateTriangle(nodes[0], nodes[4], nodes[5]);
        }
    }

    void AssignVerticals(Node[] points)
    {
        for (int i = 0; i < points.Length;i++ )
        {
            if (points[i].vertexIndex==-1)
            {
                points[i].vertexIndex = verticals.Count;
                verticals.Add(points[i].position);
            }
        }
    }

    void CreateTriangle(Node a, Node b, Node c)
    {
        triangles.Add(a.vertexIndex);
        triangles.Add(b.vertexIndex);
        triangles.Add(c.vertexIndex);
    }

你也許已經注意到了,我們還沒有定義verticals和triangles,把它們定義為類的欄位:

List<Vector3> verticals;
    List<int> triangles;

就快完成了!現在給我們的遊戲物體掛上Mesh Filter和Mesh Renderer元件,同時建立一個黑色的Material材質球,掛載到Mesh Renderer元件裡的Materail陣列中。

接著我們把MeshGenerator中的OnGizmosDraw函式註釋掉,已經不需要它啦~

同時修改以下程式碼:

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

        verticals=new List<Vector3>();
        triangles=new List<int>();

        for (int x = 0; x < squareGrid.squares.GetLength(0); x++)
        {
            for (int y = 0; y < squareGrid.squares.GetLength(1); y++)
            {
                TriangulateSquare(squareGrid.squares[x, y]);
            }
        }

        Mesh mesh=new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        mesh.vertices=verticals.ToArray();
        mesh.triangles=triangles.ToArray();
        mesh.RecalculateNormals();
    }

我們使用程式碼手動定義了一個mesh元件。好,到現在已經完成了,我們來執行看下,希望不會出錯:

喔噢!大功告成,簡直不能再棒了!