Unity教程 | 简化碰撞几何体
发表于2017-04-29
本篇教程将展示如何为导入的3D模型自动生成简化版的碰撞体。教程将以Google SketchUp为例,但其中的概念及代码也适用于其它建模工具。 简介 现代游戏大多模型精良,通常一个人物模型需要渲染数千个顶点及三角面。但角色的碰撞会以简化过后的Mesh进行计算。例如,Unity自带的标准角色碰撞器就是一个胶囊碰撞器。球体、立方体、胶囊体及一些简单几何体之间的碰撞计算非常高效。也可以单独计算并处理3D模型每个面之间的碰撞,Unity就专门设计了MeshCollider组件来处理这种类型的碰撞。但它有着较大的性能消耗,对整个游戏体验会带来较大的影响。 第一步 难题 现在游戏中比较标准的解决办法就是,对物理对象采用简化的碰撞网格。这就需要在开发过程中进行额外的步骤。除了创建3D模型之外,还需要创建碰撞网格并将其置于正确的位置。使用某些软件可以大幅度简化该流程。但如果使用Google SketchUp这样的3D建模工具就不行了。从SketchUp中导入模型至Unity只能动态生成MeshCollider。 ![]() 对于这个问题,一种可能的解决方法是自动添加所有您需要的几何体。首先需要了解的是,一旦从SketchUp导入模型,Unity将保留其组件的层级结构。SketchUp支持组和组件的概念,它们作为几何实体(顶点及三角面)的容器,组合成为模型。组和组件在导入Unity时都会被转换为游戏对象。对于组件,Unity会保留SketchUp中的命名再加上一个增量编号。如果Sketchup模型包含两个Table组件的实例,Unity会将它们命名为Table #1 和Table #2。 ![]() 第二步 解决方案 这个简易教程的核心思想就是迭代导入游戏对象,并且只为某些特定的组件添加碰撞器。因为导入的模型包含许多嵌套的游戏对象,所以需要以递归(recursion)的形式遍历它们。递归流程如下:
C#代码如下: [C#] 纯文本查看 复制代码
使用AddComponent添加BoxCollider,与在检视面板中手动添加碰撞器效果一致,Unity会自动调整碰撞器大小直至与3D网格边框匹配。它会为游戏对象生成包围盒,并且适用于大部分情况。 第三步 自定义编辑器 在C#脚本的Start方法中调用CreateCollidersRecursively进行测试: [C#] 纯文本查看 复制代码
该方法最大的问题是每次启动游戏时都会执行代码,也可以在游戏启动之前就执行,以减少游戏加载时间。实现该功能需要在编辑器中执行脚本,而非游戏启动后执行。Unity支持使用自定义脚本来扩展编辑器的功能及外观,可以创建自定义编辑器脚本实现该功能。在函数前定义MenuItem属性,以便在运行游戏之前就可以直接从编辑器调用。 [C#] 纯文本查看 复制代码
这个新的设置可以生成碰撞器且不影响最终的玩家体验。如果有多个模型,可以将它们放在同一个游戏对象下,并从该游戏对象上调用此脚本。 ![]() 第四步 改进 删除之前的碰撞器。多次运行CreateCollidersRecursively 函数会反复添加多次碰撞器。这会导致许多问题。因此,应该确保脚本在创建新的碰撞器之前删除所有已存在的碰撞器。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public void CreateCollidersRecursively (Transform root) { // Iterates over all children of "root" foreach (Transform child in root) { // Check if we need to create a collider if (child.name.StartsWith( "Table" ) ) { // Deletes all the previous colliders Collider [] colliders = root.gameObject.GetComponents foreach (Collider collider in colliders) Destroy (collider); // Creates a new colliders child.gameObject.AddComponent return ; } // Repeats the process for each child CreateCollidersRecursively(child); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public GameObject lightPrefab; public void CreateCollidersRecursively (Transform root) { // Iterates over all children of "root" foreach (Transform child in root) { // Check if we need to create a collider if (child.name.StartsWith( "Table" ) ) { // ... } // Creates a light if (child.name.StartsWith( "Light" ) ) { GameObject light = Instantiate(lightPrefab, root.position, Quaternion.identity) as GameObject; light.transform.parent = root; } // ... } } |
本教程简单介绍了如何在Unity中循环遍历3D模型的组件,用于自动添加碰撞器并初始化一些预制件,例如:光源、音源及收集物等等。
原文作者: Alan Zucconi