You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中C#层级化分组GameObject及手术刀模式交互问题求助

问题

我正在Unity开发人体循环系统生物仿真项目,用Blender制作3D人体模型,要导出到Chrome、IE浏览器运行。需要把心脏、动脉、静脉等GameObject按以下逻辑层级分组,右侧菜单支持点击展开/收起各级分组:

  • 循环系统
    • 心脏
      • 右心房
      • 左心房
      • ...
    • 静脉
      • 消化系统静脉
        • D静脉
        • ...
      • 泌尿系统静脉
        • V静脉
        • ...
      • 呼吸系统静脉
        • Z静脉
        • ...
    • 动脉
      • 消化系统动脉
        • 肠系膜上动脉
        • 肠系膜下动脉
        • ...
      • 泌尿系统动脉
        • X动脉
        • ...
      • 呼吸系统动脉
        • Y动脉
        • ...

目前使用以下脚本,但无法访问嵌套分组内的GameObject,且手术刀(bisturi)模式无法和这些嵌套对象交互,求技术帮助:

// 导入必要的命名空间
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

// 定义sensorDeObjetos类
public class sensorDeObjetos : MonoBehaviour
{
    // 声明公共变量
    public static string Nome;
    public GameObject painel;
    public Text nomeObjeto;
    public Button painelbutton;
    public GameObject cursorBisturi;
    public GameObject principalObject;
    public bool open;
    public bool bisturi;
    public GameObject buttonInformation;
    public GameObject[] panels;

    // 初始化时调用的方法
    void Start()
    {
        // 为面板按钮添加点击事件监听器
        painelbutton.GetComponent<Button>().onClick.AddListener(buttonBisturiControler);
        bisturi = false;
        open = true;
    }

    // 每帧调用的方法
    void Update()
    {
        // 获取主对象的面板列表
        panels = principalObject.GetComponent<listadeObjetos>().listpanel.ToArray();
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        // 检查手术刀是否未激活
        if (!bisturi)
        {
            open = true;
            cursorBisturi.SetActive(false);

            // 检查是否射线命中对象
            if (Physics.Raycast(ray, out hit))
            {
                Nome = hit.collider.name;

                Debug.Log(Nome);

                // 检查面板未激活且鼠标左键点击
                if (painel.activeSelf == false && Input.GetMouseButtonDown(0))
                {
                    nomeObjeto.text = Nome;
                    painel.SetActive(true);
                    painel.transform.position = new Vector3(Input.mousePosition.x + painel.GetComponent<RectTransform>().sizeDelta.x / 2 + 5, Input.mousePosition.y, 0);
                }
                else if (painel.activeSelf == true && Input.GetMouseButtonDown(0))
                {
                    painel.SetActive(false);
                }

                // 更新面板位置
                if (painel.activeSelf == false)
                {
                    painel.transform.position = new Vector3(Input.mousePosition.x + painel.GetComponent<RectTransform>().sizeDelta.x, Input.mousePosition.y, 0);
                }
            }
            else if (painel.activeSelf == true && Input.GetMouseButtonDown(0))
            {
                painel.SetActive(false);
            }
        }

        // 检查手术刀是否激活
        if (bisturi)
        {
            open = false;
            cursorBisturi.SetActive(true);

            // 从屏幕中心发射射线,若命中对象且鼠标左键点击
            if (Physics.Raycast(ray, out hit) && Input.GetMouseButtonDown(0))
            {
                // 对命中对象执行相关操作
                // hit.collider.gameObject.GetComponent<ColorMaterial>().valor = 0;

                for (int i = 0; i < principalObject.GetComponent<listadeObjetos>().sliderList.Length; i++)
                {
                    if (hit.collider.gameObject.GetComponent<ReferenceName>().reference == principalObject.GetComponent<listadeObjetos>().sliderList[i].GetComponent<ReferenceName>().reference)
                    {
                        principalObject.GetComponent<listadeObjetos>().sliderList[i].GetComponent<Slider>().value = 0;
                    }
                }
            }

            // 为所有滑块添加值改变事件监听器
            for (int i = 0; i < principalObject.GetComponent<listadeObjetos>().sliderList.Length; i++)
            {
                principalObject.GetComponent<listadeObjetos>().sliderList[i].GetComponent<Slider>().onValueChanged.AddListener(delegate { SliderModificado(); });
            }
        }

        // 更新手术刀光标位置
        cursorBisturi.transform.position = new Vector3(Input.mousePosition.x + cursorBisturi.GetComponent<RectTransform>().sizeDelta.x / 2 + 2, Input.mousePosition.y - cursorBisturi.GetComponent<RectTransform>().sizeDelta.y / 2 - 2);
    }

    // 手术刀控制方法
    void buttonBisturiControler()
    {
        if (bisturi)
        {
            bisturi = false;
            Cursor.visible = true;
        }
        else if (!bisturi && open)
        {
            bisturi = true;
            Cursor.visible = false;
        }
    }

    // 滑块被修改时调用的方法
    void SliderModificado()
    {
        // 此处可添加滑块修改相关逻辑
    }

    // 控制按钮显示/隐藏的方法
    void aparece(GameObject botao)
    {
        if (botao.GetComponent<boolO>().Active == false)
        {
            botao.GetComponent<boolO>().Active = true;
            botao.GetComponent<buttonImage>().image.transform.Rotate(0, 0, -90);
        }
        else
        {
            botao.GetComponent<boolO>().Active = false;
            botao.GetComponent<buttonImage>().image.transform.Rotate(0, 0, 90);
        }
    }
}
解决方案

1. 嵌套对象射线检测失效修复

  • 核心问题:父对象Collider遮挡子对象射线、子对象无Collider、射线未指定正确层级。
  • 修复步骤:
    1. 给所有需要交互的嵌套子对象添加Mesh Collider(勾选Convex确保射线可检测),移除父对象不需要的Collider或设置为Trigger。
    2. 添加层级过滤,避免检测无关对象:
      public LayerMask interactiveLayer; // 在Inspector中选择交互对象层级
      // 替换原有Raycast调用
      if (Physics.Raycast(ray, out hit, Mathf.Infinity, interactiveLayer))
      

2. 递归获取所有嵌套对象

原脚本仅获取直接子对象,需递归遍历所有层级:

// 在listadeObjetos类中添加递归获取方法
public Slider[] GetAllNestedSliders()
{
    List<Slider> sliders = new List<Slider>();
    GetSlidersRecursive(principalObject.transform, sliders);
    return sliders.ToArray();
}

private void GetSlidersRecursive(Transform parent, List<Slider> sliders)
{
    foreach (Transform child in parent)
    {
        Slider slider = child.GetComponent<Slider>();
        if (slider != null) sliders.Add(slider);
        GetSlidersRecursive(child, sliders);
    }
}

在sensorDeObjetos中调用该方法,即可获取所有嵌套Slider。

3. 手术刀模式交互修复

  • 问题:重复绑定事件、嵌套对象无ReferenceName组件导致匹配失败。
  • 修复:
    1. 确保所有交互嵌套对象挂载ReferenceName并赋值。
    2. 将事件绑定移到Start方法,避免每帧重复绑定:
      void Start()
      {
          painelbutton.GetComponent<Button>().onClick.AddListener(buttonBisturiControler);
          bisturi = false;
          open = true;
      
          // 一次性绑定Slider事件
          foreach (Slider slider in principalObject.GetComponent<listadeObjetos>().GetAllNestedSliders())
          {
              slider.onValueChanged.AddListener(SliderModificado);
          }
      }
      
    3. 优化手术刀匹配逻辑:
      if (Physics.Raycast(ray, out hit, Mathf.Infinity, interactiveLayer) && Input.GetMouseButtonDown(0))
      {
          ReferenceName hitRef = hit.collider.GetComponent<ReferenceName>();
          if (hitRef == null) return;
      
          foreach (Slider slider in principalObject.GetComponent<listadeObjetos>().GetAllNestedSliders())
          {
              ReferenceName sliderRef = slider.GetComponent<ReferenceName>();
              if (sliderRef != null && sliderRef.reference == hitRef.reference)
              {
                  slider.value = 0;
                  break;
              }
          }
      }
      

4. 层级菜单展开/收起逻辑

给每个分组父对象添加以下脚本:

using UnityEngine;
using UnityEngine.UI;

public class GroupToggle : MonoBehaviour
{
    public Button toggleButton;
    private bool isExpanded = true;

    void Start()
    {
        toggleButton.onClick.AddListener(ToggleGroup);
        SetChildrenActive(isExpanded);
    }

    void ToggleGroup()
    {
        isExpanded = !isExpanded;
        SetChildrenActive(isExpanded);
        toggleButton.transform.Rotate(0, 0, isExpanded ? -90 : 90);
    }

    void SetChildrenActive(bool active)
    {
        foreach (Transform child in transform)
        {
            if (child != toggleButton.transform) child.gameObject.SetActive(active);
        }
    }
}

绑定对应按钮后,即可实现点击展开/收起子分组功能。

内容的提问来源于stack exchange,提问作者Kauã Kelvin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:14:57