You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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;
    }
}

代码关键说明

  1. 层级计算逻辑GetRelativeLevel方法通过循环向上遍历父物体,计算出当前对象相对于parent的层级,确保只处理你指定层级及以下的子对象。
  2. 自动添加碰撞体AddAppropriateCollider方法实现了自动添加碰撞体的逻辑,默认用BoxCollider并适配网格边界,你可以根据需求改成MeshCollider或其他类型。
  3. 简化代码:用string.Join和Linq简化了碰撞体类型的拼接,比原来的for循环更简洁。
  4. 资源安全:用using语句管理StreamWriter,自动释放文件资源,避免手动Close可能出现的遗漏。

内容的提问来源于stack exchange,提问作者Daniel Lip

火山引擎 最新活动