简化道路系统的碰撞网格?

7

在Unity中,如果给定一个类似于所示的3D基于瓷砖的道路或人行道系统,我会发现刚体在它们的接缝处产生微小颠簸(即使是相同的垂直位置和比例,并且对于每个瓷砖使用盒形碰撞器)。是否有一种好的方法将碰撞网格组合成一个超级简化且全平的网格?例如,导出为Object(即使使用我的网格简化器),显然的微小颠簸仍然存在,当重新导入并摆脱单个盒形碰撞器时。我的要求不是必须实时的(尽管这是一个加分项,如果没有缺点的话)。谢谢!

A road system


1
你提到了“网格简化器”?它是如何工作的?我可以想象当处于相同位置的顶点没有合并时,可能会出现微小的凸起。如果我正确地理解了你的网格简化器的工作方式,那么在生成简化网格时删除重复的顶点可能会有所帮助。 - Aleksandr Stepanov
我尝试过Lightwave 3D和Blender的各种Poly-etc-reduce功能。(我还试玩了MeshLab,但是它有数十个不同的选项。)同时,我通过简单地重叠长四边形手动重新创建了碰撞网格,并且可以消除凸起。自动化这个过程仍然很好,可以更轻松地添加或编辑图块... - Philipp Lenssen
3个回答

6
你可以尝试使用 Mesh.CombineMeshes 来将所有小碰撞体组合成一个大碰撞体。
[RequireComponent(typeof(MeshCollider))]
public class MeshCompositeCollider : MonoBehaviour
{
    void Start()
    {
        var meshColliders = GetComponentsInChildren<MeshCollider>();
        var combine = new CombineInstance[meshColliders.Length];

        for (int i = 0; i < meshColliders.Length; i++)
        {
            combine[i].mesh = meshColliders[i].sharedMesh;
            combine[i].transform = meshColliders[i].transform.localToWorldMatrix;
            meshColliders[i].enabled = false;
        }

        var compositeMesh = new Mesh();
        compositeMesh.CombineMeshes(combine);
        //WeldVertices(compositeMesh);
        GetComponent<MeshCollider>().sharedMesh = compositeMesh;
        GetComponent<MeshCollider>().enabled = true;
    }
}

需要注意的一点是:合并后的网格是以世界空间坐标为基础的,因此如果这个游戏对象有任何变换改变,它们也会被应用。

如果您启用了 MeshCollider -> Cooking Options -> 焊接重叠顶点,这可能已经足够将具有相同位置的顶点组合起来。

如果没有,您可以尝试自己焊接顶点,看看是否可以解决问题。

public static void WeldVertices(Mesh aMesh, float aMaxDistDelta = 0.01f)
{
    var aMaxDelta = aMaxDistDelta * aMaxDistDelta;
    var verts = aMesh.vertices;    
    List<int> newVerts = new List<int>();
    int[] map = new int[verts.Length];
    // create mapping and filter duplicates.
    for (int i = 0; i < verts.Length; i++)
    {
        var p = verts[i];

        bool duplicate = false;
        for (int i2 = 0; i2 < newVerts.Count; i2++)
        {
            int a = newVerts[i2];
            if ((verts[a] - p).sqrMagnitude <= aMaxDelta)
            {
                map[i] = i2;
                duplicate = true;
                break;
            }
        }
        if (!duplicate)
        {
            map[i] = newVerts.Count;
            newVerts.Add(i);
        }
    }
    // create new vertices
    var verts2 = new Vector3[newVerts.Count];    
    for (int i = 0; i < newVerts.Count; i++)
    {
        int a = newVerts[i];
        verts2[i] = verts[a];        
    }
    // map the triangle to the new vertices
    var tris = aMesh.triangles;
    for (int i = 0; i < tris.Length; i++)
    {
        tris[i] = map[tris[i]];
    }

    aMesh.Clear();       
    aMesh.vertices = verts2;
    aMesh.triangles = tris;                
}


这是一个示例(之前和之后):上面有6个带有明显间隙的碰撞器,下面1个没有间隙的碰撞器。

enter image description here


谢谢!我已经授予您奖励,并且一旦我有机会实现它并检查它是否适用于我的刚体碰撞,我也会将其标记为答案。 - Philipp Lenssen

0

你正在寻找一个叫做复合碰撞器的东西。2D Tilemaps甚至有一个“用于复合”的复选框。如果你将Composite Collider 2D组件附加到你的tilemap对象上并勾选“used by composite”,它将把所有的碰撞器合并成一个大碰撞器。

话虽如此,看起来你正在使用prefabs将道路片段生成到网格上,而不是使用tilemap构建地图。你仍然可以通过在碰撞器上勾选“Used by composite”来为许多不同的GameObject使用复合碰撞器,但这比简单地在一个组件上勾选一个按钮要复杂一些,因为你将不得不配置所有的游戏对象。如果你能简化使用tilemap来制作你的道路,那么应该会更简单。


谢谢。我应该提到,但我的应用程序是一个完全的3D世界,而不是2D。人们甚至可以上下移动,驾驶直升机等等。为了这些道路,我们可以假设它们都在同一垂直位置上。 - Philipp Lenssen
1
很不幸,运行时并不存在针对3D对象的复合碰撞器选项。你可以尝试构建自定义网格,但我在3D开发方面经验不足,无法为你提供帮助,抱歉 @PhilippLenssen - Erik Overflow

0

也许,你不需要改变电路,而是改变汽车碰撞系统:

  • 制作一个“隐形假车”,具有真实的碰撞器和输入反馈,然后制作真实的汽车(只有模型,或者带有额外的碰撞器用于其他类型的碰撞检测(带动力升级?)但我认为在假车中只使用一个碰撞器就足够了)
  • 使模型跟随假车位置,但插值垂直故障(例如,每个帧更新您的真实模型位置是当前位置+“y”差异的1/10(如果太大)或类似的东西,用于夹紧位置的时间或百分比将需要手动测试以获得良好的感觉)
  • 如果您使用两个碰撞器,请玩弄它们的层和碰撞系统以完善系统(真实模型仅与环境或汽车发生碰撞),而虚假的碰撞器则忽略环境。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接