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(预设体),这样后面再产生箭头就可以由该预设体产生了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 生成正方形的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边形近似的箭头

相应代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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文件。

1
2
3
4
5
6
    // 建立游戏物体的数组,当前只有一个
        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下载)

posted @   多见多闻  阅读(978)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示