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、射线未指定正确层级。
- 修复步骤:
- 给所有需要交互的嵌套子对象添加Mesh Collider(勾选Convex确保射线可检测),移除父对象不需要的Collider或设置为Trigger。
- 添加层级过滤,避免检测无关对象:
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组件导致匹配失败。 - 修复:
- 确保所有交互嵌套对象挂载
ReferenceName并赋值。 - 将事件绑定移到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); } } - 优化手术刀匹配逻辑:
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
相关产品推荐
相关产品推荐

