Unity3D中產生任意形狀的物體


用過Unity3D的讀者知道,Unity中只提供了正方體、球形、圓柱體等基礎形狀的預設體,那么如何在Unity3D中產生任意形狀的物體呢?下面就我個人的經驗,以產生一個箭頭為例,提供可能的幾個思路。

一、物體拼接

這是最簡單的方法,因為一個箭頭可以由細長圓柱 + 圓錐拼接而成。

我們需要先在Unity3D中新建一個空的物體,命名為“Arrow”(圖2)。

圖2. 新建空的游戲物體

然后我們創建一個圓柱體(3D Object -> Cylinder),但是這里沒有圓錐體,怎么辦?這就用到了Unity3D的Asset Store了,在商店中搜索“ConeCollider”,可以找到相應的免費資源(圖3)。

圖3. 商店中的“ConeCollider”

點擊下載,下載完成后導入,可以找到相應的模型ConeCollider(圖4),把它拉進Hierarchy中即可。

圖4. 建立圓錐體

接下來將圓柱和圓錐都移動到“Arrow”的空物體處,成為其子物體,同時調整圓柱、圓錐的大小和位置直至合適,這樣一個箭頭就產生了(圖5)。同時,可以將Arrow拉倒下方文件夾中,成為一個Prefab(預設體),這樣后面再產生箭頭就可以由該預設體產生了。

// 生成正方形的4個頂點
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(0, 0, 0);
vertices[1] = new Vector3(0, 0, 1);
vertices[2] = new Vector3(0, 1, 0);
        vertices[3] = new Vector3(0, 1, 1);
// 生成正方形的三角拓撲信息
        int[] triangle = new int[6] {0,2,1,1,2,3 };
// 獲取物體的網格
        Mesh mesh = GetComponent<MeshFilter>().mesh;
// 清除原有網格
mesh.Clear();
// 賦予網格新的頂點
mesh.vertices = vertices;
// 賦予網格新的拓撲信息
        mesh.triangles = triangle;
// 網格重計算法線
        mesh.RecalculateNormals();

將相應的腳本綁定在場景中的游戲物體上,運行結果見圖8。

圖8. Unity3D運行產生的正方形

回到我們之前的例子——箭頭,那么你需要動腦筋梳理出箭頭的頂點坐標和拓撲信息應該是怎么樣的了。圖9給出了作者采用的一種方案,以12邊形近似為圓。

圖9. 12邊形近似的箭頭

相應代碼如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateArrow : MonoBehaviour {
    public float ra = 0.3f,// 圓柱的半徑
    ha = 3,// 圓柱的高度
    rb = 1,// 圓錐的半徑
    hb = 2;// 圓錐的高度
    public int n = 12;// 正多邊形的邊數
    // 初始化函數
    void Start () {
        Vector3[] vertices = new Vector3[3 * n + 1];// 模型的頂點
        int[] triangle = new int[18 * n - 12];// 模型的三角形拓撲
        for (int i = 0; i < n; i++)
        {
            // 圓柱的底面頂點
            vertices[i] = new Vector3((float)(ra * Math.Cos(2 * Math.PI * i / n)), (float)(ra * Math.Sin(2 * Math.PI * i / n)), 0);
            // 圓柱的頂面頂點
            vertices[n + i] = new Vector3((float)(ra * Math.Cos(2 * Math.PI * i / n)), (float)(ra * Math.Sin(2 * Math.PI * i / n)), ha);
            // 圓錐的底面頂點
            vertices[2 * n + i] = new Vector3((float)(rb * Math.Cos(2 * Math.PI * i / n)), (float)(rb * Math.Sin(2 * Math.PI * i / n)), ha);
        }
        // 圓錐的頂點
        vertices[3 * n] = new Vector3(0, 0, ha + hb);
        // 生成三角拓撲信息
        for (int i = 0; i < n - 2; i++)
        {
            triangle[3 * i] = 0;
            triangle[3 * i + 2] = i + 1;
            triangle[3 * i + 1] = i + 2;
            triangle[3 * (n - 2) + 6 * n + 3 * i] = 2 * n;
            triangle[3 * (n - 2) + 6 * n + 3 * i + 2] = 2 * n + i + 1;
            triangle[3 * (n - 2) + 6 * n + 3 * i + 1] = 2 * n + i + 2;
        }
        for (int i = 0; i < n - 1; i++)
        {
            triangle[3 * (n - 2) + 6 * i] = i;
            triangle[3 * (n - 2) + 6 * i + 2] = n + i;
            triangle[3 * (n - 2) + 6 * i + 1] = n + i + 1;
            triangle[3 * (n - 2) + 6 * i + 3] = n + i + 1;
            triangle[3 * (n - 2) + 6 * i + 5] = i + 1;
            triangle[3 * (n - 2) + 6 * i + 4] = i;
            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i] = 2 * n + i;
            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i + 2] = 3 * n;
            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i + 1] = 2 * n + i + 1;
        }
        triangle[3 * (n - 2) + 6 * (n - 1)] = n - 1;
        triangle[3 * (n - 2) + 6 * (n - 1) + 2] = n + n - 1;
        triangle[3 * (n - 2) + 6 * (n - 1) + 1] = n;
        triangle[3 * (n - 2) + 6 * (n - 1) + 3] = n;
        triangle[3 * (n - 2) + 6 * (n - 1) + 5] = 0;
        triangle[3 * (n - 2) + 6 * (n - 1) + 4] = n - 1;
        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1)] = 2 * n + n - 1;
        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1) + 2] = 3 * n;
        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1) + 1] = 2 * n;
        // 獲取物體的網格
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        // 清除原有網格
        mesh.Clear();
        // 賦予網格新的頂點
        mesh.vertices = vertices;
        // 賦予網格新的拓撲信息
              mesh.triangles = triangle;
        // 網格重計算法線
                mesh.RecalculateNormals();
    }
}

將上述代碼中的n改成128后,即以128邊形近似為圓,結果如圖10。

圖10. 12邊形近似的箭頭

三、生成fbx模型文件

第二種方法的好處是方便靈活,但是模型的可復制性不強,比如需要很多個箭頭,每次都要重復計算各個箭頭的頂點坐標,程序的計算量一下子就上去了。那么,有沒有什么辦法可以讓自己設計的網格模型成為可重復使用的模型呢?當然有!那就是生成fbx模型文件,也就是第一種方法用到的模型ConeCollider這樣的文件。

生成fbx文件的方法很多,可以借助其他的建模軟件(如3d Max, MAYA等),也可以自己使用Unity3D編寫fbx文件。下面介紹利用Unity3D生成fbx文件的方法,需要用到一個dll外部庫——WRP_FBXExporter.dll(百度搜索下載即可)。

將WRP_FBXExporter.dll移動到Assets文件夾下,引用中就會自動添加可以使用。相比第二種方法,只需要添加幾行代碼即可導出模型的fbx文件。

    // 建立游戲物體的數組,當前只有一個
        GameObject[] gameObjects = new GameObject[1];
        // 將場景中的物體作為數組元素,“CreateFBX”是場景中游戲物體名稱
        gameObjects[0] = GameObject.Find("CreateFBX");
        // 調用FBXExporter.ExportFBX,生成對應的FBX文件
        FBXExporter.ExportFBX("", "Arraw" + n, gameObjects, true);

生成結果見圖11,可以看到已經在Assets下生成了Arraw12.fbx。

圖11. 生成相應的fbx文件

對於FBXExporter.ExportFBX () 函數的參數詳解見圖12,分別對應fbx文件存放文件、fbx文件名、轉化的游戲物體數組等選項。

圖12. FBXExporter.ExportFBX () 參數詳解

參考資料

[1] https://assetstore.unity.com/?q=ConeCollider&orderBy=0(ConeCollider下載)

[2] https://assetstore.unity.com/packages/vfx/shaders/directx-11/ucla-wireframe-shader-21897(UCLA Wireframe Shader下載)

[3] https://blog.csdn.net/dong2016hong/article/details/54847235(WRP_FBXExporter.dll下載)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM