Up to date
This page is up to date for Godot 4.2
.
If you still find outdated information, please open an issue.
使用 ArrayMesh¶
本教學將介紹使用 ArrayMesh 的基礎知識。
為此, 我們將使用函式 add_surface_from_arrays() , 它最多需要四個參數. 前兩個參數是必須的, 後兩個參數是可選的.
第一個參數是 ``PrimitiveType``(像素型別),這是 OpenGL 中的概念,用於指示 GPU 如何根據給定的頂點來安排像素,即它們表示的是三角形、線、還是點等等。可選項見 Mesh.PrimitiveType。
第二各參數 arrays
是儲存網格資訊的實際 Array。該陣列是一個普通的 Godot 陣列,用空括弧 []
建構。它為每一種型別的資訊儲存一個 ``Pool**Array``(如 PoolVector3Array、PoolIntArray 等),用於建構表面。
arrays
可能包含下列元素,另外還必須在 arrays
中包含位置資訊。另請參閱 Mesh.ArrayType。
索引 |
Mesh.ArrayType 列舉 |
陣列 |
---|---|---|
0 |
|
PoolVector2Array : Vector2 物件的陣列。 |
1 |
|
|
2 |
|
4 個浮點數一組的 PoolRealArray。前 3 個浮點數決定切線,最後一個決定雙法線方向,為 -1 或 1。 |
3 |
|
|
4 |
|
PoolVector2Array : Vector2 物件的陣列。 |
5 |
|
PoolVector2Array : Vector2 物件的陣列。 |
10 |
|
4 個 float 一組的 PoolRealArray 或 4 個 int 一組的 PoolIntArray。每一組都列出了影響某個特定頂點的 4 根骨骼。 |
11 |
|
4 個 float 一組的 PoolRealArray。每個 float 都列出了給定頂點對 |
12 |
|
在大多數情況下,在建立網格時,我們透過其頂點位置來定義它。因此,通常情況下,頂點陣列(索引 0 處)是必需的,而索參陣列(索引 12 處)是可選的,只有在包含時才會使用。也可以建立僅包含索引陣列而沒有頂點陣列的網格,但這超出了本教學的範圍。事實上,我們根本不會使用索參陣列。
其他所有陣列包含的都是關於頂點的資訊。他們也是可選的,包含時才會用到。有些陣列(例如 ARRAY_COLOR`)用每個頂點一個元素的形式來提供額外的頂點資訊。他們的大小必須與頂點陣列一致。另一些陣列(例如 ARRAY_TANGENT
)用四個元素來描述一個頂點。他們必須正好是頂點陣列的四倍大小。
正常的使用場景下,add_surface_from_arrays() 的最後兩個參數通常都是留空的。
設定遊戲區域¶
在編輯器中,建立一個 MeshInstance 並在屬性面板中為其新增一個 ArrayMesh。通常,在編輯器裡新增 ArrayMesh 沒什麼用,但這裡可以讓我們免去用程式碼建立的麻煩,直接使用這個 ArrayMesh。
將腳本附加到節點。
在 _ready()
下建立一個新的陣列。
var surface_array = []
var surfaceArray = new Godot.Collections.Array();
這將是保存表面資訊的陣列——將保存表面需要的所有資料陣列。Godot 希望它的大小是 Mesh.ARRAY_MAX
,所以要相應地調整。
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
var surfaceArray = new Godot.Collections.Array();
surfaceArray.Resize((int)Mesh.ArrayType.Max);
接下來, 為您將使用的每種資料型別建立陣列.
var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()
var verts = new List<Vector3>();
var uvs = new List<Vector2>();
var normals = new List<Vector3>();
var indices = new List<int>();
一旦你用幾何體填充了你的資料陣列, 就可以通過將每個陣列新增到 surface_array
, 然後提交到網格中來建立網格.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
# No blendshapes, lods, or compression used.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
var arrMesh = Mesh as ArrayMesh;
if (arrMesh != null)
{
// No blendshapes, lods, or compression used.
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
}
備註
在這個例子中,使用了 Mesh.PRIMITIVE_TRIANGLES
,但你也可以使用網格所提供的任何像素型別。
把這些放到一起,完整的程式碼是這樣的:
extends MeshInstance3D
func _ready():
var surface_array = []
surface_array.resize(Mesh.ARRAY_MAX)
# PackedVector**Arrays for mesh construction.
var verts = PackedVector3Array()
var uvs = PackedVector2Array()
var normals = PackedVector3Array()
var indices = PackedInt32Array()
#######################################
## Insert code here to generate mesh ##
#######################################
# Assign arrays to surface array.
surface_array[Mesh.ARRAY_VERTEX] = verts
surface_array[Mesh.ARRAY_TEX_UV] = uvs
surface_array[Mesh.ARRAY_NORMAL] = normals
surface_array[Mesh.ARRAY_INDEX] = indices
# Create mesh surface from mesh array.
# No blendshapes, lods, or compression used.
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array)
public partial class MyMeshInstance3D : MeshInstance3D
{
public override void _Ready()
{
var surfaceArray = new Godot.Collections.Array();
surfaceArray.Resize((int)Mesh.ArrayType.Max);
// C# arrays cannot be resized or expanded, so use Lists to create geometry.
var verts = new List<Vector3>();
var uvs = new List<Vector2>();
var normals = new List<Vector3>();
var indices = new List<int>();
/***********************************
* Insert code here to generate mesh.
* *********************************/
// Convert Lists to arrays and assign to surface array
surfaceArray[(int)Mesh.ArrayType.Vertex] = verts.ToArray();
surfaceArray[(int)Mesh.ArrayType.TexUV] = uvs.ToArray();
surfaceArray[(int)Mesh.ArrayType.Normal] = normals.ToArray();
surfaceArray[(int)Mesh.ArrayType.Index] = indices.ToArray();
var arrMesh = Mesh as ArrayMesh;
if (arrMesh != null)
{
// Create mesh surface from mesh array
// No blendshapes, lods, or compression used.
arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, surfaceArray);
}
}
}
中間可以放你想要的任何程式碼。下面我們會給出一些範例程式碼,用於生成球體。
建立內容¶
這是生成球體的範例程式碼。儘管程式碼是用 GDScript 編寫的,但是 Godot 並沒有指定用特定的方式來實作它。這種實作方式與 ArrayMesh 無關,僅僅是一種通用的生成球體的方式。如果您覺得這比較難以理解,或者想更全面地瞭解程式式幾何體,可以在網上尋找相關的教學進行學習。
extends MeshInstance3D
var rings = 50
var radial_segments = 50
var radius = 1
func _ready():
# Insert setting up the PackedVector**Arrays here.
# Vertex indices.
var thisrow = 0
var prevrow = 0
var point = 0
# Loop over rings.
for i in range(rings + 1):
var v = float(i) / rings
var w = sin(PI * v)
var y = cos(PI * v)
# Loop over segments in ring.
for j in range(radial_segments):
var u = float(j) / radial_segments
var x = sin(u * PI * 2.0)
var z = cos(u * PI * 2.0)
var vert = Vector3(x * radius * w, y * radius, z * radius * w)
verts.append(vert)
normals.append(vert.normalized())
uvs.append(Vector2(u, v))
point += 1
# Create triangles in ring using indices.
if i > 0 and j > 0:
indices.append(prevrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j - 1)
indices.append(prevrow + j)
indices.append(thisrow + j)
indices.append(thisrow + j - 1)
if i > 0:
indices.append(prevrow + radial_segments - 1)
indices.append(prevrow)
indices.append(thisrow + radial_segments - 1)
indices.append(prevrow)
indices.append(prevrow + radial_segments)
indices.append(thisrow + radial_segments - 1)
prevrow = thisrow
thisrow = point
# Insert committing to the ArrayMesh here.
public partial class MyMeshInstance3D : MeshInstance3D
{
private int _rings = 50;
private int _radialSegments = 50;
private float _radius = 1;
public override void _Ready()
{
// Insert setting up the surface array and lists here.
// Vertex indices.
var thisRow = 0;
var prevRow = 0;
var point = 0;
// Loop over rings.
for (var i = 0; i < _rings + 1; i++)
{
var v = ((float)i) / _rings;
var w = Mathf.Sin(Mathf.Pi * v);
var y = Mathf.Cos(Mathf.Pi * v);
// Loop over segments in ring.
for (var j = 0; j < _radialSegments; j++)
{
var u = ((float)j) / _radialSegments;
var x = Mathf.Sin(u * Mathf.Pi * 2);
var z = Mathf.Cos(u * Mathf.Pi * 2);
var vert = new Vector3(x * _radius * w, y * _radius, z * _radius * w);
verts.Add(vert);
normals.Add(vert.Normalized());
uvs.Add(new Vector2(u, v));
point += 1;
// Create triangles in ring using indices.
if (i > 0 && j > 0)
{
indices.Add(prevRow + j - 1);
indices.Add(prevRow + j);
indices.Add(thisRow + j - 1);
indices.Add(prevRow + j);
indices.Add(thisRow + j);
indices.Add(thisRow + j - 1);
}
}
if (i > 0)
{
indices.Add(prevRow + _radialSegments - 1);
indices.Add(prevRow);
indices.Add(thisRow + _radialSegments - 1);
indices.Add(prevRow);
indices.Add(prevRow + _radialSegments);
indices.Add(thisRow + _radialSegments - 1);
}
prevRow = thisRow;
thisRow = point;
}
// Insert committing to the ArrayMesh here.
}
}
保存¶
最後,我們可以使用 ResourceSaver 類來保存該 ArrayMesh。當你想生成一個網格,然後在以後使用它而不需要重新生成時,這個方法很有用。
# Saves mesh to a .tres file with compression enabled.
ResourceSaver.save(mesh, "res://sphere.tres", ResourceSaver.FLAG_COMPRESS)
// Saves mesh to a .tres file with compression enabled.
ResourceSaver.Save(Mesh, "res://sphere.tres", ResourceSaver.SaverFlags.Compress);