【问题标题】:Modify vertices at runtime在运行时修改顶点
【发布时间】:2018-03-05 06:41:31
【问题描述】:

对于我要在 Unity 运行时使用的一小组自定义地形工具,我需要能够“绘制”地形区域以升高和降低它。

为此,我有这个功能:

public void PaintRaise(Vector3 center, float radius, float power) {
    Mesh mesh = this.gameObject.GetComponent<MeshFilter>().sharedMesh;
    Vector3[] verts = new Vector3[mesh.vertices.Length];

    for (int i = 0; i < mesh.vertices.Length; ++i) {
        // method for getting distance, faster then Vector3.Distance
        var heading = mesh.vertices[i] - center;
        var distance = heading.magnitude;
        var direction = heading / distance;
        if (heading.sqrMagnitude < radius * radius) {
            verts[i] = new Vector3(
                mesh.vertices[i].x,
                mesh.vertices[i].y + power,
                mesh.vertices[i].z);
        } else {
            verts[i] = mesh.vertices[i];
        }

    }
    mesh.vertices = verts;
}

理论上,它应该可以完美地应用于带有网格的游戏对象,而且确实如此!但不幸的是,它非常非常慢。即使使用improved 测距方法,处理 5 个单位的半径也需要大约 10 秒。

它在编辑器和编译版本中的运行速度一样慢。

如您所见,分析器显示调用 PaintRaise 函数的 Update() 函数需要大约 13 秒来执行单个帧,其中大部分是垃圾收集器!

为什么它运行缓慢,我怎样才能加快它的速度(所以理想情况下它需要几毫秒来执行)?

编辑

启用深度分析会使它更加混乱!为什么由于 GC 而获取网格顶点需要 13 秒?

【问题讨论】:

  • 何时调用 PaintRaise 函数?
  • 不是创建新数组,而是修改现有数组的各个元素。还将 GetComponent 移到方法之外。确保你不要在每一帧都调用你的方法,但是例如每 200 毫秒

标签: c# unity3d mesh


【解决方案1】:

做了一些研究后,我发现顶点是not actually a variable,看起来像。

新的 SetVertices / SetIndices / SetUVs / 方法有几个原因。首先,旧的 vertices 属性不是变量。这个事实让许多用户感到困惑。这是一个财产。所以实际上有一个 get 方法和一个 set 方法,但用法看起来像一个变量。

大多数人不明白的是,getter 不会返回对内部数组的引用,而是返回一个副本。这是必要的,因为实际的顶点数据存储在 Unity 的原生 C++ 端。

将我的函数更改为使用 Get() 和 Set() 消除了使用 vertices 属性创建的额外垃圾收集,并使其运行顺畅。

public void PaintRaise(Vector3 center, float radius, float power) {
    Vector3 localPoint = transform.InverseTransformPoint(center);
    Mesh mesh = this.gameObject.GetComponent<MeshFilter>().sharedMesh;
    List<Vector3> verts = new List<Vector3>(); 
    mesh.GetVertices(verts);

    for (int i = 0; i < verts.Count; ++i) {
        var heading = verts[i] - center;
        var distance = heading.magnitude;
        var direction = heading / distance;
        if (heading.sqrMagnitude < radius * radius) {
            verts[i] = new Vector3(
                verts[i].x,
                verts[i].y + power,
                verts[i].z);
        }
    }
    mesh.SetVertices(verts);
}

【讨论】:

  • 您还需要在PaintRaise 函数之外声明List&lt;Vector3&gt; verts,因为这也很昂贵并且会分配。每次调用PaintRaise 函数时只需清除列表即可。
  • 我想补充一点,因为它会影响整个网格的一小部分,如果你可以在更小的区域而不是整个网格上循环,它会更优化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-15
  • 2018-04-03
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多