日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从零开始做一个SLG游戏(二):用mesh实现简单的地形

發布時間:2024/8/26 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从零开始做一个SLG游戏(二):用mesh实现简单的地形 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文主要是用mesh實現簡單的地形。暫時先繪制三種地形:高山、平原、水域。

首先要做的是網格的細化:

上一篇已經實現了單個六邊形的繪制,實現方式是將六邊形分割成6個等邊三角形,然后分別繪制。

現在需要將每個三角形再次細化,將一個三角形細化為4個小三角形。

如下圖:
?

細化原理如下圖:
?


在上一篇文章中,封裝了三角形繪制的函數:
?

  • private void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? int count = triangles.Count;
  • ? ?? ?? ?? ?? ? vertices.Add(v1);
  • ? ?? ?? ?? ?? ? triangles.Add(count++);
  • ? ?? ?? ?? ?? ? vertices.Add(v2);
  • ? ?? ?? ?? ?? ? triangles.Add(count++);
  • ? ?? ?? ?? ?? ? vertices.Add(v3);
  • ? ?? ?? ?? ?? ? triangles.Add(count++);
  • ? ?? ???}
  • 復制代碼


    其中v1,v2,v3如圖所示,而v4,v5,v6分別為三條邊的中點。

    所以有:

    Vector3 v4 = Vector3.Lerp(v1, v2, 0.5f);
    Vector3 v5 = Vector3.Lerp(v2, v3, 0.5f);
    Vector3 v6 = Vector3.Lerp(v1, v3, 0.5f);
    所以新的4個三角形分別為:(v1, v4, v6)(v4, v2, v5)(v4, v5, v6)(v3, v6, v5)

    于是,新寫一個遞歸函數用于細化(原函數保留):
    ?

  • ? ?? ?? ?private void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3, int time)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? if (time == 0)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(v1, v2, v3);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? else
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?time--;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v4 = Vector3.Lerp(v1, v2, 0.5f);
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v5 = Vector3.Lerp(v2, v3, 0.5f);
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v6 = Vector3.Lerp(v1, v3, 0.5f);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(v1, v4, v6, time);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(v4, v2, v5, time);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(v4, v5, v6, time);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(v3, v6, v5, time);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ???}
  • ? ?? ???/// <summary>
  • ? ?? ???/// 繪制地形
  • ? ?? ???/// </summary>
  • ? ?? ???public void Draw(HexTerrian type)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? ……
  • ? ?? ?? ?? ?? ? for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v1 = HexMetrics.corners[(int)dir];
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v2 = HexMetrics.corners[(int)dir + 1];
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(center, v1, v2, 2);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? UpdateMesh();
  • ? ?? ???}
  • 復制代碼


    其中time指的是細化次數。
    ?


    但是,如今生成的圖片還是一個六邊形,與原來沒有變化。原因是,沒有對圖片上的點做擾動處理,所以雖然生成的時候是細化了生成的,但拼在一起還是平的。

    現加入擾動處理的函數:
    ?

  • private Vector3 Perturb(Vector3 pos)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? float level = 0.5f;
  • ? ?? ?? ?? ?? ? Vector3 localPos = transform.localPosition + pos;
  • ? ?? ?? ?? ?? ? pos.x += level * (Mathf.PerlinNoise(localPos.x, localPos.z) - 0.5f);
  • ? ?? ?? ?? ?? ? pos.y += level * (Mathf.PerlinNoise(localPos.x + 1f, localPos.z + 1f) - 0.5f);
  • ? ?? ?? ?? ?? ? pos.z += level * (Mathf.PerlinNoise(localPos.x + 2f, localPos.z + 2f) - 0.5f);
  • ? ?? ?? ?? ?? ? return pos;
  • ? ?? ???}
  • 復制代碼


    這里用的是unity自帶的柏林噪聲函數 Mathf.PerlinNoise(float x,float y),這個算法會根據x以及y的值生成一個隨機的函數,固定的x和y,生成的隨機值是固定的。所以暫時先用這個做擾動。

    Mathf.PerlinNoise(float x,float y)得出的是0到1的一個值。

    所以減去0.5。得到的是-0.5到0.5的一個值。

    level是一個擾動的參數,擾動后,單個坐標最多偏移0.25個單位,這樣顯示出來的就是一個有輕微褶皺的地形圖片。

    將在AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3, int time)函數中繪制三角形的部分加入擾動:
    ?

  • ? ?? ?? ?private void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3, int time)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? if (time == 0)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(Perturb(v1), Perturb(v2), Perturb(v3));
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? ………………
  • ? ?? ???}
  • 復制代碼


    運行后,我們得到如下的圖:
    ?


    然后再調一下材質的顏色,本游戲采用的是lowpoly風格,該風格的反射很弱,所以需要把材質上的specular hightlight的鉤去掉,然后再調整一下圖片的顏色:
    ?


    有一點泥土的感覺了,暫時先這么用著吧。

    下一步要解決的問題是,目前只是一個單面的六邊形,將這個六邊形繪制成一個棱柱,表現會更好一些,當然,棱柱的底就不畫了,反正看不到。

    首先寫一個函數用于繪制一個矩形:
    ?

  • /// <summary>
  • ? ?? ???/// v3??v4
  • ? ?? ???///
  • ? ?? ???/// v1??v2
  • ? ?? ???/// </summary>
  • ? ?? ???/// <param name="v1"></param>
  • ? ?? ???/// <param name="v2"></param>
  • ? ?? ???/// <param name="v3"></param>
  • ? ?? ???/// <param name="v4"></param>
  • ? ?? ???private void AddSquare(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? AddTriangle(v1, v3, v2);
  • ? ?? ?? ?? ?? ? AddTriangle(v3, v4, v2);
  • ? ?? ???}
  • 復制代碼


    v1,v2,v3,v4的位置備注所示。

    如果不考慮細化,那么繪制邊上的面的函數就可以如下表示:
    ?

  • /// <summary>
  • ? ?? ???/// 繪制地形
  • ? ?? ???/// </summary>
  • ? ?? ???public void Draw(HexTerrian type)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? ……………………
  • ? ?? ?? ?? ?? ? for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v1 = HexMetrics.corners[(int)dir];
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v2 = HexMetrics.corners[(int)dir + 1];
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(center, v1, v2, 2);
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v3 = v1 + 5f * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v4 = v2 + 5f * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddSquare(v1, v2, v3, v4);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? ……………………
  • ? ?? ???}
  • 復制代碼


    接下來考慮分形的做法,先看下示意圖:
    ?


    然后是代碼:
    ?

  • private void AddEdge(Vector3 v1, Vector3 v2, int time)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? if (time == 0)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?v1 = Perturb(v1);
  • ? ?? ?? ?? ?? ?? ?? ?? ?v2 = Perturb(v2);
  • ? ?? ?? ?? ?? ?? ?? ?? ?float d = 5f;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v3 = v1 + d * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v4 = v2 + d * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddSquare(v1, v2, v3, v4);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? else
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?time--;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v5 = Vector3.Lerp(v1, v2, 0.5f);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddEdge(v1, v5, time);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddEdge(v5, v2, time);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ???}
  • 復制代碼


    因為v3,v4是通過v1,v2計算得來的,所以輸入就只需要v1,v2就行了。

    因為點的擾動是最后計算的,所以會和六邊形的邊重合,手游賬號買賣不會出現錯位。

    效果如下圖:


    接下來是河流和高山地塊。

    先定義一個地形類型的枚舉:
    ?

  • public enum HexTerrian
  • {
  • ? ?? ???Water,
  • ? ?? ???Plain,
  • ? ?? ???Mountain,
  • }
  • 復制代碼


    然后在HexCell類中加入地形類型,用于表示當前地形的類型,同時定義一個材質的數組用于保存不同地形對應的材質,并在繪制函數中加入地形參數:
    ?

  • public class HexCell : MonoBehaviour {
  • ? ?? ???……
  • ? ?? ???public HexTerrian terr;
  • ? ?? ???public Material[] materials;
  • ? ?? ???……
  • ? ?? ???public void Draw(HexTerrian type)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? terrianType = type;
  • ? ?? ?? ?? ?? ? GetComponent<Renderer>().material = materials[(int)type];
  • ? ?? ???……
  • }
  • 復制代碼


    將做好的3個地形材質拖到數組上,記得和枚舉一一對應。

    先處理湖泊地形,湖泊和平原相比,只在于水平面將會比平原低一點:
    ?

  • ………
  • ? ?? ???private readonly float deep = 5;//方塊厚度
  • ? ?? ???private readonly float waterLevel = 2;//水平面離地距離
  • ? ?? ???public int fractalTime = 3;//細化次數
  • ………
  • ? ?? ?? ?? ?? ? for (HexDirection dir = HexDirection.NE; dir <= HexDirection.NW; dir++)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v1 = HexMetrics.corners[(int)dir];
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v2 = HexMetrics.corners[(int)dir + 1];
  • ? ?? ?? ?? ?? ?? ?? ?? ?switch (type)
  • ? ?? ?? ?? ?? ?? ?? ?? ?{
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???case HexTerrian.Water:
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (dir == HexDirection.NE)
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?center -= waterLevel * Vector3.up;
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? v1 -= waterLevel * Vector3.up;
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? v2 -= waterLevel * Vector3.up;
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? break;
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???case HexTerrian.Plain:
  • ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? break;
  • ? ?? ?? ?? ?? ?? ?? ?? ?}
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddTriangle(center, v1, v2, fractalTime);
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddEdge(v1, v2, fractalTime);
  • ? ?? ?? ?? ?? ? }
  • ……
  • 復制代碼


    邊緣六個面繪制的時候也要記得短一些:


    ?

  • private void AddEdge(Vector3 v1, Vector3 v2, int time)
  • ? ?? ???{
  • ? ?? ?? ?? ?? ? if (time == 0)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?v1 = Perturb(v1);
  • ? ?? ?? ?? ?? ?? ?? ?? ?v2 = Perturb(v2);
  • ? ?? ?? ?? ?? ?? ?? ?? ?float d = (terrianType == HexTerrian.Water) ? (deep - waterLevel) : deep;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v3 = v1 + d * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?Vector3 v4 = v2 + d * Vector3.down;
  • ? ?? ?? ?? ?? ?? ?? ?? ?AddSquare(v1, v2, v3, v4);
  • ? ?? ?? ?? ?? ? }
  • ? ?? ?? ?? ?? ? ……
  • ? ?? ???}
  • 復制代碼


    接下來繪制高山,對于高山,先簡單處理一下:將頂點提高一些,然后細化一下。
    ?

  • ……
  • case HexTerrian.Mountain:
  • if (dir == HexDirection.NE)
  • {
  • ? ?? ???center += 5f * Vector3.up;
  • }
  • break;
  • ……
  • 復制代碼


    然后測試一下地形效果,隨機地形在這篇文章就不做了,主要是把所有的地形都顯示出來,所以隨便寫一下吧:

  • ? ?? ?? ?? ?? ? foreach (HexCell c in hexCells)
  • ? ?? ?? ?? ?? ? {
  • ? ?? ?? ?? ?? ?? ?? ?? ?int height = (c.Pos.x + c.Pos.y) % 3;
  • ? ?? ?? ?? ?? ?? ?? ?? ?c.Draw((HexTerrian)height);
  • ? ?? ?? ?? ?? ? }
  • 復制代碼


    于是,得到了封面上的效果圖:
    ?

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的从零开始做一个SLG游戏(二):用mesh实现简单的地形的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。