Unity中如何设置层级筛选对象并添加合适的玩家阻挡碰撞体?
解决方案:碰撞体选型与层级过滤优化
我来帮你搞定这两个核心需求:选合适的碰撞体阻挡玩家,以及按指定层级筛选子对象,这都是Unity开发里非常实用的场景。
一、碰撞体类型怎么选?
不同碰撞体各有优劣,得根据物体的形状和用途来定:
- 基础碰撞体(Box/Sphere/Capsule):首选!性能开销极小,适合规则形状的物体(比如墙面、箱子、柱子)。只要物体形状接近这些基础几何体,用它们完全能阻挡玩家,还不会拖慢游戏帧率。
- MeshCollider:专门对付形状复杂、没法用基础碰撞体模拟的物体。但要注意两点:
- 普通MeshCollider只能给静态物体用,性能开销比基础碰撞体大;
- 如果需要让这个物体和玩家/动态物体交互(比如被推动),必须勾选
Convex选项,但Convex要求网格是凸面体,不然会报错。
- 总结原则:能用基础碰撞体就不用MeshCollider,复杂静态物体用非凸MeshCollider,需要交互的复杂物体用凸MeshCollider。
二、怎么实现按层级筛选子对象?
我们可以加一个int类型的变量targetStartLevel,用来指定要获取的子对象起始层级。这里的层级是相对于你设置的parent来算的:parent是层级0,它的直接子物体是层级1,子物体的子物体是层级2,以此类推。我们要做的就是筛选出层级≥targetStartLevel的对象,同时排除parent本身。
三、修改后的完整代码
using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; public class GetComponents : MonoBehaviour { public Transform parent; // 这里设置起始层级,比如设为2,就会获取层级2及以下的子对象(排除parent和层级1的Child1-4) public int targetStartLevel = 2; public List<GameObject> allObjects = new List<GameObject>(); private void Start() { string path = "e:/colliders.txt"; // 用using自动释放StreamWriter资源,比手动Close更安全 using (StreamWriter writer = new StreamWriter(path, true)) { allObjects = FindObjectsOfType<GameObject>().ToList(); Transform[] allChildren = parent.GetComponentsInChildren<Transform>(); foreach (Transform child in allChildren) { // 跳过parent自身 if (child == parent) continue; // 计算当前物体相对于parent的层级 int currentLevel = GetRelativeLevel(child, parent); // 只处理层级符合要求的对象 if (currentLevel < targetStartLevel) continue; var colliders = child.GetComponents<Collider>(); int length = colliders.Length; if (length == 0) { writer.WriteLine($"{child.name} - No Colliders"); // 自动给无碰撞体的对象添加合适的碰撞体,逻辑可以自己改 AddAppropriateCollider(child.gameObject); } else { // 拼接已有碰撞体类型并写入文件 string colliderTypes = string.Join(", ", colliders.Select(c => c.GetType().Name)); writer.WriteLine($"{child.name} - {colliderTypes}"); } } } } // 计算当前物体相对于根父物体的层级 private int GetRelativeLevel(Transform current, Transform rootParent) { int level = 0; Transform tempParent = current.parent; // 从当前物体的父级往上找,直到找到rootParent while (tempParent != null && tempParent != rootParent) { level++; tempParent = tempParent.parent; } // rootParent是层级0,所以它的直接子物体是层级1,以此类推 return level + 1; } // 自定义添加碰撞体的逻辑,你可以根据需求修改 private void AddAppropriateCollider(GameObject obj) { // 示例1:添加BoxCollider,并适配MeshRenderer的边界(如果有的话) BoxCollider boxCollider = obj.AddComponent<BoxCollider>(); MeshRenderer renderer = obj.GetComponent<MeshRenderer>(); if (renderer != null) { boxCollider.center = renderer.bounds.center - obj.transform.position; boxCollider.size = renderer.bounds.size; } // 示例2:如果要添加MeshCollider,替换成下面的代码: // MeshCollider meshCollider = obj.AddComponent<MeshCollider>(); // 如果需要支持动态交互,取消注释下面一行: // meshCollider.convex = true; } }
代码关键说明
- 层级计算逻辑:
GetRelativeLevel方法通过循环向上遍历父物体,计算出当前对象相对于parent的层级,确保只处理你指定层级及以下的子对象。 - 自动添加碰撞体:
AddAppropriateCollider方法实现了自动添加碰撞体的逻辑,默认用BoxCollider并适配网格边界,你可以根据需求改成MeshCollider或其他类型。 - 简化代码:用
string.Join和Linq简化了碰撞体类型的拼接,比原来的for循环更简洁。 - 资源安全:用
using语句管理StreamWriter,自动释放文件资源,避免手动Close可能出现的遗漏。
内容的提问来源于stack exchange,提问作者Daniel Lip




