EditableMesh 将应用的视觉网格更改为连接到 MeshPart 时,允许在 Studio 和体验中查询和修改网格。
启用发布体验
为了安全目的,默认情况下,使用 EditableMesh 对发布的体验失败。要启用使用,您必须年满 13 岁并身份验证通过。验证通过后,打开 Studio 的 游戏设置,选择 安全 ,然后启用 允许网格/图像 API 切换。
权限
为了防止滥用,AssetService:CreateEditableMeshAsync() 只允许您加载和编辑网格资产:
- 这些是由体验的创建者拥有的(如果体验由个人拥有)。
- 这些是由群组拥有的(如果体验由群组拥有)。
- 这些是由登录的 Studio 用户拥有的(如果地点文件尚未保存或发布到 Roblox)。
内存限制
可编辑的资产目前对内存使用成本很高。为了最大限度地减少对客户端性能的影响,EditableMesh 有严格的客户端内存预算,尽管服务器、Studio 和插件都使用无限内存。使用 FixedSize 可能会帮助您保持在内存预算内,在一些场景中,将一个 EditableMesh 链接到多个 MeshParts (多次引用) 可以帮助优化内存。
创建和显示
一个 EditableMesh 可以从现有的 Content 的 MeshPart 或网格 ID 使用 AssetService:CreateEditableMeshAsync() 或空白 EditableMesh 创建,或使用 AssetService:CreateEditableMesh() 可以创建一个空白 。然后可以显示、修改和更新其碰撞模型。不是所有步骤都是必要的;例如,你可能想创建一个 EditableMesh 仅用于射线投射,而不显示它。
local AssetService = game:GetService("AssetService")
-- 创建空可编辑的网格
local editableMesh = AssetService:CreateEditableMesh()
-- 从资产 ID 创建可编辑的网格
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID))
end)
-- 从另一个可编辑的网格中创建可编辑的网格
local editableMeshFromAnother = nil
local success, errorMessage = pcall(function()
editableMeshFromAnother = AssetService:CreateEditableMeshAsync(Content.fromObject(OTHER_EDITABLE_MESH))
end)
-- 从网格部件中创建可编辑网格
local editableMeshFromMeshPart = nil
local success, errorMessage = pcall(function()
editableMeshFromMeshPart = AssetService:CreateEditableMeshAsync(MESH_PART.MeshContent)
end)
当它链接到新的 EditableMesh 时,将显示一个 MeshPart ,通过 AssetService:CreateMeshPartAsync() 。您可以创建更多 引用相同 的实例,或通过 链接到现有的 。
local AssetService = game:GetService("AssetService")
local Workspace = game:GetService("Workspace")
-- 从资产 ID 创建可编辑的网格
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID))
end)
-- 创建新的网格部件,与可编辑网格连接
local newMeshPart = nil
local success, errorMessage = pcall(function()
newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMeshFromAsset))
end)
-- 或者,将上面创建的新 MeshPart 链接到现有的 MeshPart
local existingMeshPart = Workspace:FindFirstChild("EXISTING_MESH_PART")
existingMeshPart:ApplyMesh(newMeshPart)
要重新计算编辑后的碰撞和流体几何图形,您可以再次调用 AssetService:CreateMeshPartAsync() 和 MeshPart:ApplyMesh() 来更新现有的 MeshPart 。一般建议在概念编辑结束后执行此操作,而不是在调用个别方法来操作几何图形之后。网格的视觉变化始终会立即反映在引擎上,无需调用 AssetService:CreateMeshPartAsync() 。
固定尺寸网格
当从现有网格资产中创建一个 EditableMesh 时(通过 AssetService:CreateEditableMeshAsync() ),结果的可编辑网格默认为固定尺寸。固定尺寸的网格在内存方面更高效,但您无法更改边顶点、面或属性的数量。只能编辑顶点属性和位置的值。
local AssetService = game:GetService("AssetService")
-- 创建无固定尺寸默认的可编辑网格
local editableMeshFromAsset = nil
local success, errorMessage = pcall(function()
editableMeshFromAsset = AssetService:CreateEditableMeshAsync(Content.fromAssetId(ASSET_ID), {FixedSize = false})
end)
稳定的垂直/面 ID
许多 EditableMesh 方法使用 vertex , normal , UV , color 和 face ID。这些在 Luau 中被视为整数,但需要一些特殊处理。主要区别是,ID 是稳定的,即使其他部分的网格发生变化,它们也是相同的。例如,如果一个 EditableMesh 有五个边顶 {1, 2, 3, 4, 5} ,如果你移除边顶 4 ,那么新的边顶将是 {1, 2, 3, 5} 。
请注意,ID 不保证是按顺序排列的,因此,当遍历垂直或面时,您应该遍历由 GetVertices() 或 GetFaces() 返回的表。
分割垂直面属性
一个 边角 是面的角落,并且可以将面连接在一起。顶点可以有几个属性:位置、正常、UV坐标、颜色和透明度。
有时对于所有接触顶点的面都有用使用相同的属性值,但有时你会想让不同的面使用不同的属性值在同一顶点上。例如,在光滑的球体上,每个边点只有一个正常。相反,在立方体的角落,顶点将有 3 个不同的正常(每个邻近面都有一个)。您还可以在 UV 坐标或顶点颜色上有缝隙或锋利的变化。
当创建面时,每个vertex默认会拥有每个属性中的一个:一个正常值、一个UV坐标和一个颜色/透明度。如果你想创建缝合,你应该创建新属性并将它们设置在面上。例如,这段代码会创建一个锋利的立方体:
local AssetService = game:GetService("AssetService")
-- 给出 4 个顶点 ID,添加新的正常和 2 个三角形,制作锋利的四角
local function addSharpQuad(editableMesh, vid0, vid1, vid2, vid3)
local nid = editableMesh:AddNormal() -- 这会创建一个正常 ID,它会自动计算
local fid1 = editableMesh:AddTriangle(vid0, vid1, vid2)
editableMesh:SetFaceNormals(fid1, {nid, nid, nid})
local fid2 = editableMesh:AddTriangle(vid0, vid2, vid3)
editableMesh:SetFaceNormals(fid2, {nid, nid, nid})
end
-- 使6边之间的边缘增加的立方体
local function makeSharpCube()
local editableMesh = AssetService:CreateEditableMesh()
local v1 = editableMesh:AddVertex(Vector3.new(0, 0, 0))
local v2 = editableMesh:AddVertex(Vector3.new(1, 0, 0))
local v3 = editableMesh:AddVertex(Vector3.new(0, 1, 0))
local v4 = editableMesh:AddVertex(Vector3.new(1, 1, 0))
local v5 = editableMesh:AddVertex(Vector3.new(0, 0, 1))
local v6 = editableMesh:AddVertex(Vector3.new(1, 0, 1))
local v7 = editableMesh:AddVertex(Vector3.new(0, 1, 1))
local v8 = editableMesh:AddVertex(Vector3.new(1, 1, 1))
addSharpQuad(editableMesh, v5, v6, v8, v7) -- 前
addSharpQuad(editableMesh, v1, v3, v4, v2) -- 返回
addSharpQuad(editableMesh, v1, v5, v7, v3) -- 左
addSharpQuad(editableMesh, v2, v4, v8, v6) -- 右侧
addSharpQuad(editableMesh, v1, v2, v6, v5) -- 底部
addSharpQuad(editableMesh, v3, v7, v8, v4) -- 顶部
editableMesh:RemoveUnused()
return editableMesh
end
卷线
网格面有前面和后面。当绘制网格时,默认情况下只绘制面的前部,尽管您可以通过设置网格的 DoubleSided 属性为 true 来更改此情况。
面周围的边缘点的顺序决定你是否正在查看前部或后部。当垂直向前时,面的前部可见,当垂直向后时,面的后部可见。

FACS 姿势
可动头使用面部动作编码系统(FACS)。查看 FACS 姿势参考 以获取使用 GetFacsPoses() 和类似方法时有用的信息。
每个 FACS 姿势由 Enum.FacsActionUnit 值确定。对于 FACS 姿势,虚拟骨头每个都可以有一个 CFrame 将骨头的初始 CFrame 在绑定姿势的网格中转换为该 FACS 操作单元的姿势的 CFrame 。所有骨头 CFrames 都在网格的本地空间。
这些 FACS 姿势在动画期间融合在一起。有时,基础姿势的混合产生了糟糕的结果。在这些情况下,您可以通过使用更令人满意的 纠正姿势 来覆盖特定基础姿势的混合。纠正姿势由 2 或 3 Enum.FacsActionUnit 值确定。像基础 FACS 姿势一样,为了纠正姿势,虚拟骨头每个都可以有一个 CFrame 将网格中的初始 CFrame 骨头转换为该 FACS 纠正的 CFrame 骨头。
限制
EditableMesh 目前有 60,000 个边和 20,000 个三角形的限制。尝试添加太多边或三角形会导致错误。
概要
方法
添加新颜色到几何图形,并返回稳定的颜色 ID。
添加新的正常到几何图形,并返回稳定的正常 ID。
添加新的三角形到网格并返回稳定的面 ID。
添加新的 UV 到几何体并返回稳定的 UV ID。
添加新的vertex到几何图形中,并返回稳定的vertex ID。
摧毁网格。
找到网格表面上最接近的点。
找到最接近特定点的坐标点的vertex。
查找特定球体内的所有边点。
返回与给定面相邻的面列表。
返回与给定边点相邻的边点列表。
返回给定颜色ID的颜色。
返回指定颜色 ID 的颜色 alpha(透明度)。
返回网格的所有颜色。
返回面的颜色 ID,用于面上的边顶点。
返回面的正常 ID,用于面上的边顶点。
返回面的 UV ID,用于面上的边顶点。
返回面的顶点 ID。
返回网格的所有面。
返回给定普通ID的普通向量。
返回网格的所有正常。
获取vertex的位置。
返回指定的 UV ID 的 UV 坐标。
返回网格的所有 UV。
返回所有顶点为一列稳定顶点 ID 列表。
返回描述稳定ID的字符串,有助于调试目的。
合并触碰在一起的边点。
使用稳定的面 ID 去除一个面。
移除所有未使用的边顶点、正常、UV和颜色,并返回移除的ID。
重置此普通 ID 以自动计算。
设置颜色为颜色 ID。
设置颜色 alpha(透明度)为颜色 ID。
将面的顶点颜色设置为新的颜色 ID。
将面的顶点正常设置为新的正常 ID。
将面的顶点 UV 设置为新的 UV ID。
将面的顶点设置为新的顶点 ID。
设置正常ID的正常值。
在网格的本地对象空间中设置一个vertex位置。
设置 UV 坐标为 UV ID。
将网格上的所有面分为三角形
属性
方法
AddColor
添加新颜色到几何图形,并返回稳定的颜色 ID。
参数
返回
新颜色的稳定颜色 ID。
AddNormal
添加新的正常值到几何图形,并返回稳定的正常 ID。如果未指定正常值,正常将自动计算。
参数
普通向量。如果未指定普通值,普通将自动计算。
返回
新常态的稳定普通 ID。
AddTriangle
添加新的三角形到网格并返回稳定的面 ID。
参数
三角形的第一个顶点的 ID。
三角形第二个顶点的 ID。
三角形的第三个顶点的 ID。
返回
新脸部的稳定面 ID。
AddVertex
添加新的vertex到几何图形中,并返回稳定的vertex ID。
参数
在网格的本地对象空间中的位置。
返回
新边缘的稳定vertex ID。
Destroy
摧毁网格内容,立即回收使用的内存。
返回
FindClosestPointOnSurface
找到网格表面上最接近的点。返回面部ID,指向本地对象空间的网格,以及面部内部位置的坐标。见 RaycastLocal() 获取更多关于椭圆坐标的信息。
参数
网格的本地对象空间中的点位置。
返回
面 ID tuple、在本地对象空间上的网格点以及脸部内的位置坐标。
FindClosestVertex
找到空间中最接近特定点的vertex并返回稳定的vertex ID。
参数
网格的本地对象空间中的点位置。
返回
最接近稳定顶点 ID 到指定空间点。
FindVerticesWithinSphere
查找特定球体内的所有边点并返回一列稳定边点ID列表。
参数
返回
请求球体内稳定边界ID列表。
GetAdjacentVertices
给出稳定的顶点 ID,返回邻近顶点列表。

参数
绕着哪个垂直 ID 获得邻近垂直。
返回
给定垂直ID周围的邻近边缘的ID列表。
GetColorAlpha
返回指定稳定颜色 ID 的颜色 alpha(透明度)。
参数
获取Alpha的颜色ID。
返回
颜色 alpha 在请求稳定颜色 ID。
GetFaceUVs
返回面的 UV ID,用于面上的边顶点。
参数
用于获取 UV ID 的面部识别。
返回
用于指定面上的顶点的 UV ID 列表。
GetPosition
获取网格本地对象空间中vertex的位置。
参数
稳定的垂直 ID,用于获取位置。
返回
在网格本地对象空间中的顶点位置。
IdDebugString
返回描述稳定ID的字符串,用于调试目的,如 f17 或 v12 ,包含类型、ID号和版本。
参数
用于返回调试信息字符串的ID。
返回
描述ID以人阅读可读格式的字符串。
MergeVertices
合并触碰在一起的边点,使用单个边点 ID,但保留其他原始属性 ID。
参数
在那里,边顶被认为相互接触的距离。
返回
将旧的vertex ID映射到新的vertex ID以融合已合并的垂直,
RaycastLocal
投射射线并返回交叉点、面 ID 和球心坐标。该方法的输入和输出位于网格的本地对象空间。
一个 椭圆坐标 是指将面内的一个点指定为面的 3 个边缘的权重组合的方式。这有助于作为一般方法来融合vertex特性。看这个方法的代码示例作为说明。
参数
返回
交叉点、面 ID 和楔形坐标的 tuple。
代码示例
This code finds the position and UV coordinates of the closest point on an EditableMesh to the input point.
local AssetService = game:GetService("AssetService")
-- Initialize EditableMesh in space
local editableMesh = nil
local success, errorMsg = pcall(function()
editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri("rbxassetid://ASSET_ID"))
end)
local meshPart = nil
if success and editableMesh then
meshPart = AssetService:CreateMeshPartAsync(
Content.fromObject(editableMesh),
{ CollisionFidelity = Enum.CollisionFidelity.Hull }
)
meshPart.Parent = workspace
else
print(errorMsg)
end
local function castRayFromCamera(position)
if not meshPart then
return
end
-- Create ray from camera along the direction of a clicked point
local camera = workspace.CurrentCamera
local ray = camera:ScreenPointToRay(position.X, position.Y)
-- Convert to object space to use with RaycastLocal()
local relativeOrigin = meshPart.CFrame:PointToObjectSpace(ray.Origin)
local relativeDirection = meshPart.CFrame:VectorToObjectSpace(ray.Direction)
local triangleId, point, barycentricCoordinate
triangleId, point, barycentricCoordinate = editableMesh:RaycastLocal(relativeOrigin, relativeDirection * 100)
if not triangleId then
-- Didn't hit any triangles
return
end
-- Interpolate UVs within the triangle
local vert1, vert2, vert3 = editableMesh:GetTriangleVertices(triangleId)
local uv0 = editableMesh:GetUV(vert1)
local uv1 = editableMesh:GetUV(vert2)
local uv2 = editableMesh:GetUV(vert3)
local u = (barycentricCoordinate.x * uv0.x) + (barycentricCoordinate.y * uv1.x) + (barycentricCoordinate.z * uv2.x)
local v = (barycentricCoordinate.x * uv0.y) + (barycentricCoordinate.y * uv1.y) + (barycentricCoordinate.z * uv2.y)
return Vector2.new(u, v)
end
SetColorAlpha
设置颜色 alpha(透明度)为颜色 ID。
参数
返回
SetFaceColors
将面的顶点颜色设置为新的颜色 ID。
参数
返回
SetFaceNormals
将面的顶点正常设置为新的正常 ID。
参数
返回
SetFaceUVs
将面的顶点 UV 设置为新的 UV ID。
参数
返回
SetFaceVertices
将面的顶点设置为新的顶点 ID。
参数
返回
SetNormal
设置一个正常ID的正常值。这将更改每个使用正常ID的面向的顶点的正常值。
参数
返回
SetPosition
在网格的本地对象空间中设置一个vertex位置。
参数
返回
Triangulate
将网格上的所有面分为三角形目前,由于只能创建三角形,因此无需做任何操作,但如果您的代码依赖于三角形,建议您在调用 AssetService:CreateEditableMeshAsync() 后调用此方法。