從零開始做一個SLG遊戲(三):用unity繪製圖形
本文主要是使用mesh製作一些簡單的模型資源。
一般而言,模型的製作最好還是使用專業的軟體來做,但是製作一些簡單的模型,unity還是可以勝任的。
unity自帶的模型只有立方體,圓柱,球,膠囊,方塊等有限的幾個,所以稍微複雜一些的東西就不好做了,比如最常用到的圓錐,稜柱,稜臺等,十分困難。
所以首先要做的是,將一些常用的基本模型,實現出來。
首先要做的是寫一個模型基類:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))]
- public abstract class BasePoly : MonoBehaviour {
- Mesh mesh;
- private List<Vector3> vertices;
- private List<int> triangles;
- private List<Vector2> uvs;
- private float length = 100f;
- private void Awake()
- {
- mesh = GetComponent<MeshCollider>().sharedMesh = GetComponent<MeshFilter>().mesh = new Mesh();
- mesh.name = "PolyMesh";
- vertices = new List<Vector3>();
- triangles = new List<int>();
- uvs = new List<Vector2>();
- }
-
- // Update is called once per frame
- void Update ()
- {
- DrawMesh();
- }
- private void DrawMesh()
- {
- Clear();
- Draw();
- UpdateMesh();
- }
- protected void AddTriangle(Vector3 v1, Vector3 v2, Vector3 v3)
- {
- AddConer(v1);
- AddConer(v2);
- AddConer(v3);
- }
- protected void AddSquare(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4)
- {
- AddTriangle(v1, v3, v2);
- AddTriangle(v2, v3, v4);
- }
- private void AddConer(Vector3 point)
- {
- int count = vertices.Count;
- vertices.Add(point);
- triangles.Add(count);
- uvs.Add(new Vector2(point.x / (2 * length), point.z / (2 * length)));
- }
- private void UpdateMesh()
- {
- mesh.vertices = vertices.ToArray();
- mesh.triangles = triangles.ToArray();
- mesh.uv = uvs.ToArray();
- mesh.RecalculateNormals();
- mesh.RecalculateBounds();
- }
- private void Clear()
- {
- mesh.Clear();
- vertices.Clear();
- triangles.Clear();
- uvs.Clear();
- }
- public virtual void Draw()
- {
- }
- }
具體實現的原理,在上一篇文章裡大部分都有講過。現在將之封裝了起來。
在子類中,重寫Draw()函式,呼叫下面兩個函式即可繪製各種想要的圖形。
AddTriangle(Vector3v1,Vector3 v2,Vector3 v3);
AddSquare(Vector3 v1,Vector3 v2,Vector3 v3,Vector3 v4);
首先實現一下正多邊形:
正多邊形其實和前面六邊形的製作方式類似,不過我們需要手動計算出正多邊形的各個頂點。
將正n邊形的各個頂點和中心點連線,可以得到n個等腰三角形,而等腰三角形頂角的大小為(360°/n)。
所以,如果知道一個頂點p的座標,同時又知道原點座標(0,0)的話,那下一個頂點p'的座標可以通過p圍繞原點旋轉(360°/n)的角度獲得。
那麼我們複習一下高中的空間幾何知識:
一個點圍繞原點逆時針旋轉θ角的時候,我們可以通過旋轉變換來獲得旋轉後的座標。
旋轉變換的矩陣為:
具體用法為:
對於(x,y)進行旋轉變換後,得到的(x',y')有
x'=x*cosθ-y*sinθ
y'=x*sinθ+y*cosθ
所以寫一個函式來實現旋轉變換:
- Vector3 RotationTranslate(Vector3 pos,float angle)
- {
- Vector3 Pos = pos;
- float[,] transRect = {
- { Mathf.Cos(angle),-Mathf.Sin(angle)},
- { Mathf.Sin(angle),Mathf.Cos(angle)},
- };
- Pos.x = pos.x * transRect[0, 0] + pos.z * transRect[0, 1];
- Pos.z = pos.x * transRect[1, 0] + pos.z * transRect[1, 1];
- return Pos;
- }
angle為旋轉的角度。
正多邊形的各個點都有了,然後通過畫三角的方式,將正多邊形畫出來了。
程式碼如下:
- public class Polygon : BasePoly {
- public int edgeCount = 3;
- private float angle = 0f;
- private List<Vector3> coners = new List<Vector3>();
- public override void Draw()
- {
- angle = 2f * Mathf.PI / count;
- Vector3 pos = new Vector3(1f, 0f, 0f);
- coners.Add(pos);
- for (int i = 0; i < count; i++)
- {
- pos = RotationTranslate(pos);
- coners.Add(pos);
- }
- for (int j = 0; j < count; j++)
- {
- AddTriangle(coners[j], Vector3.zero, coners[j + 1]);
- }
- coners.Clear();
- }
- Vector3 RotationTranslate(Vector3 pos)
- {
- Vector3 Pos = pos;
- float[,] transRect = {
- { Mathf.Cos(angle),-Mathf.Sin(angle)},
- { Mathf.Sin(angle),Mathf.Cos(angle)},
- };
- Pos.x = pos.x * transRect[0, 0] + pos.z * transRect[0, 1];
- Pos.z = pos.x * transRect[1, 0] + pos.z * transRect[1, 1];
- return Pos;
- }
- }
實現後,會發現一個問題,那就是每幀都要繪製一次,會非常消耗功能,所以我們需要加一個函式,用於判斷是否需要再繪製一遍:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer), typeof(MeshCollider))]
- public abstract class BasePoly : MonoBehaviour {
- ……
- private void DrawMesh()
- {
- if (NeedDraw())
- {
- Clear();
- Draw();
- UpdateMesh();
- }
- }
- public virtual bool NeedDraw()
- {
- return true;
- }
- ……
- }
然後再在子類中重寫NeedDraw()函式:
- public class Polygon : BasePoly {
- public int edgeCount = 3;
- private int count = 0;
- private float angle = 0f;
- ……
- public override bool NeedDraw()
- {
- if (edgeCount == count)
- {
- return false;
- }
- else if (edgeCount < 3)
- {
- return false;
- }
- else
- {
- count = edgeCount;
- angle = 2f * Mathf.PI / count;
- return true;
- }
- }
- public override void Draw()
- {
- //angle = 2f * Mathf.PI / count;
- ……
- }
- ……
- }
其他圖形也可以通過類似的方法,一一繪製出來,並通過自己的賦值進行微調。
比較麻煩的一點是,這麼實現,需要先讓工程執行起來,繪製的圖形才能顯示出來。幸運的是,這並不影響搭建場景。
接下來將介紹更多的基本圖形的畫法:
1.正稜錐
稜錐的畫法其實和前文製作六邊形網格的製作方法類似,將六邊形網格的中心點往上移動若干個單位,再加上底面,就是一個六稜錐了。
處理方式和前文的正多邊形相同:按角度一次分割三角形
- List<Vector3> conors = new List<Vector3>();
- public override void Draw()
- {
- Vector3 pos = new Vector3(1f, 0f, 0f);
- conors.Add(pos);
- for (int i = 0; i < count; i++)
- {
- pos = RotationTranslate(pos);
- conors.Add(pos);
- }
- }
和多邊形一樣,先把點加進去,接下來和多邊形不同的是,我們需要畫2個三角形,如下圖:
o為底面的中心點,o1為頂點,和前文一樣,底面中心的座標預設為(0,0,0),即Vector3.zero。
o1作為頂點,暫定高為1。於是o1的座標為(0,1,0)。(0,1,0)也可以表示為Vector3.up。
需要畫的三角形為底面的一部分(o,c<i>,c[i+1])以及側面(o1,c[i+1],c<i>)。
完整程式碼如下:
- List<Vector3> conors = new List<Vector3>();
- public override void Draw()
- {
- Vector3 pos = new Vector3(1f, 0f, 0f);
- conors.Add(pos);
- for (int i = 0; i < count; i++)
- {
- pos = RotationTranslate(pos);
- conors.Add(pos);
- }
- for (int k = 0; k < count; k++)
- {
- AddTriangle(Vector3.zero, conors[k], conors[k + 1]);
- AddTriangle(conors[k], Vector3.up, conors[k + 1]);
- }
- conors.Clear();
- }
2.正稜臺
正稜臺和正稜錐一樣,也是分割成多個方向,單個方向如圖:
需要畫的是(o1,c1[i+1],c1<i>)(o,c<i>,c[i+1])兩個三角形底,以及(c<i>,c[1+1],c1<i>,c1[i+1])這個矩形的側面。
上下兩個三角形的底是相似的,並且對應的邊也是相互平行的。
o點座標為Vector3.zero,即(0,0,0),o1的座標為Vector3.up,即(0,1,0)。
然後定義兩個三角形的縮放比例為zoom的話
通過空間向量的換算,很容易得出:c1<i>=c<i>*zoom+Vector3.up
所以我們定義兩個List用於存放上下底的點,並用一個函式來新增點:
- List<Vector3> conors = new List<Vector3>();//下底的點
- List<Vector3> _conors = new List<Vector3>();//上底的點
- public float zoom = 1f;
- void AddConors(Vector3 pos)
- {
- conors.Add(pos);
- _conors.Add(pos * zoom + Vector3.up);
- }
接下來的就很簡單了,點加好後,把三個面加上去就行了,函式都封裝好了的:
- public override void Draw()
- {
- angle = 2f * Mathf.PI / count;
- Vector3 pos = new Vector3(1f, 0f, 0f);
- AddConors(pos);
- for (int i = 0; i < count; i++)
- {
- pos = RotationTranslate(pos);
- AddConors(pos);
- }
- for (int k = 0; k < count; k++)
- {
- AddTriangle(Vector3.zero, conors[k], conors[k + 1]);
- AddTriangle(_conors[k], Vector3.up, _conors[k + 1]);
- AddSquare(conors[k], conors[k + 1], _conors[k], _conors[k + 1]);
- }
- conors.Clear();
- _conors.Clear();
- }
有了自制的稜臺、稜柱,以及unity自帶的方塊,球,膠囊,圓柱之類的物體,就可以在unity中自己搭建各種需要的場景了。
這樣做出來的場景物體,用於最終成品可能不達標,但是用於演示已經夠了。這最重要的是,暫時不用去學建模軟體了。
相關閱讀:
從零開始做一個SLG遊戲(一):六邊形網格
從零開始做一個SLG遊戲(二):用mesh實現簡單的地形
作者:觀復
專欄地址:https://zhuanlan.zhihu.com/p/44918148
相關文章
- 從零開始做一個SLG遊戲(一):六邊形網格遊戲
- 從零開始做一個SLG遊戲(七):遊戲系統以及配置表遊戲
- 從零開始做一個SLG遊戲(八):配置表載入元件遊戲元件
- 從零開始做一個SLG遊戲(二):用mesh實現簡單的地形遊戲
- 從零開始做一個SLG遊戲(四):UI系統之主介面搭建遊戲UI
- 從零開始做一個SLG遊戲(六):UI系統擴充套件遊戲UI套件
- iOS - 從零開始繪製一個帶動畫的多邊形能力分佈圖iOS動畫
- 從零開始做一個SLG遊戲(五):UI系統之彈窗功能遊戲UI
- 從零點五開始用Unity做半個2D戰棋小遊戲(一)Unity遊戲
- 【D3.js 入門系列一】從零開始繪製一個柱形圖JS
- 從零開始:用REACT寫一個格鬥遊戲(一)React遊戲
- 從零開始:用REACT寫一個格鬥遊戲(二)React遊戲
- 從零開始用 electron 手擼一個截圖工具
- 從零開始打造一個iOS圖片載入框架(三)iOS框架
- 從零開始製作一個linux iso映象Linux
- 開始使用SmartDraw繪製開發中的各種圖形
- 從零開始實現一個線上三角形生成器
- 一個月內從零開始做webOS開發人員Web
- 巧用 ARKit 和 SpriteKit 從零開始做 AR 遊戲遊戲
- 從零開始用 Flask 搭建一個網站(一)Flask網站
- 從零開始實現一個RPC框架(三)RPC框架
- iOS 繪圖 - 如何繪製一個多邊形能力分佈圖iOS繪圖
- 從零開始為 PicGo 開發一個新圖床PicGo圖床
- Unity3D學習筆記1——繪製一個三角形Unity3D筆記
- 手把手教你從零開始做一個好看的 APPAPP
- 從零開始開發一個 WebpackWeb
- 從零開始寫一個ExporterExport
- 從零開始實現放置遊戲(一)遊戲
- 3-地圖生成-從零開始寫一個武俠冒險遊戲地圖遊戲
- 從零開始打造一個iOS圖片載入框架(一)iOS框架
- 從零開始仿寫一個抖音App——開始APP
- java:繪製圖形Java
- 【從零開始擼一個App】PKCEAPP
- 從零開始寫一個網頁網頁
- 三個步驟,從零開始快速部署LoRaServerServer
- 微信小程式-從零開始製作一個跑步微信小程式微信小程式
- 從零開始釋出一個ArcGIS Server地圖服務Server地圖
- 從零開始打造一個iOS圖片載入框架(二)iOS框架