Unity同一脚本内跨类引用变量:波次生成系统实现遇阻
解决Unity波次生成系统中嵌套类无法引用外部列表的问题
针对你遇到的嵌套类Bubble无法访问WaveSystemScript中validBubbles列表,且希望在Inspector中从有效列表选择预制体的需求,这里提供两种实用解决方案:
方案一:自定义Inspector PropertyDrawer(实现下拉选择)
这是最贴合需求的方案,通过自定义属性抽屉,让Bubble类的bubbleSelection在Inspector中显示为下拉菜单,选项直接取自validBubbles列表。
步骤1:调整类结构(改为内部类)
先把Bubble和Waves改为WaveSystemScript的内部类,方便自定义Drawer访问外部类的列表:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class WaveSystemScript : MonoBehaviour { [SerializeField] private WaveBubbleScriptableObject waveBubbleSO; [SerializeField] private List<GameObject> validBubbles = new List<GameObject>(); [SerializeField] private List<Waves> waveList = new List<Waves>(); [System.Serializable] public class Bubble { [SerializeField] private GameObject bubbleSelection; [SerializeField] private float spawnInterval; // 提供Getter供外部验证使用 public GameObject BubbleSelection => bubbleSelection; } [System.Serializable] public class Waves { [SerializeField] private List<Bubble> waveBubbles = new List<Bubble>(); [SerializeField] private float waveLength; // 改为可序列化,方便在Inspector编辑 } }
步骤2:创建自定义PropertyDrawer
在Editor目录下创建BubbleDrawer.cs(必须放在Editor文件夹,否则无法识别):
using UnityEditor; using UnityEngine; using System.Collections.Generic; [CustomPropertyDrawer(typeof(WaveSystemScript.Bubble))] public class BubbleDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(position, label, property); // 获取当前挂载WaveSystemScript的组件实例 WaveSystemScript waveSystem = property.serializedObject.targetObject as WaveSystemScript; if (waveSystem == null) { EditorGUI.PropertyField(position, property, label); EditorGUI.EndProperty(); return; } // 拆分布局:左侧下拉选择气泡,右侧输入生成间隔 Rect selectionRect = new Rect(position.x, position.y, position.width * 0.7f, EditorGUIUtility.singleLineHeight); Rect intervalRect = new Rect(position.x + position.width * 0.7f + 8, position.y, position.width * 0.3f - 8, EditorGUIUtility.singleLineHeight); // 构建下拉选项列表(包含空选项) List<string> bubbleNames = new List<string> { "None" }; List<GameObject> bubbleObjects = new List<GameObject> { null }; foreach (var bubblePrefab in waveSystem.validBubbles) { if (bubblePrefab != null) { bubbleNames.Add(bubblePrefab.name); bubbleObjects.Add(bubblePrefab); } } // 获取当前选中的气泡属性 SerializedProperty selectionProp = property.FindPropertyRelative("bubbleSelection"); int selectedIndex = bubbleObjects.IndexOf(selectionProp.objectReferenceValue); selectedIndex = selectedIndex < 0 ? 0 : selectedIndex; // 绘制下拉菜单并赋值 selectedIndex = EditorGUI.Popup(selectionRect, label.text, selectedIndex, bubbleNames.ToArray()); selectionProp.objectReferenceValue = bubbleObjects[selectedIndex]; // 绘制生成间隔输入框 EditorGUI.PropertyField(intervalRect, property.FindPropertyRelative("spawnInterval"), GUIContent.none); EditorGUI.EndProperty(); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return EditorGUIUtility.singleLineHeight; } }
效果说明
完成后,在Inspector中编辑waveList里的每个Bubble项时,bubbleSelection会变成下拉菜单,只能选择validBubbles中的预制体或空选项,完全符合你的需求。如果要改用ScriptableObject中的列表,只需把waveSystem.validBubbles替换为waveSystem.waveBubbleSO.xxx(对应SO中的列表字段)即可。
方案二:OnValidate验证引用(简单防错)
如果不需要下拉选择,只是希望确保bubbleSelection的引用来自有效列表,可以在WaveSystemScript中添加OnValidate方法做验证:
private void OnValidate() { foreach (var wave in waveList) { foreach (var bubble in wave.waveBubbles) { if (bubble.BubbleSelection != null && !validBubbles.Contains(bubble.BubbleSelection)) { Debug.LogWarning($"无效的气泡引用:{bubble.BubbleSelection.name},已自动置空", this); // 这里需要通过反射修改私有字段,或者把bubbleSelection改为public var selectionField = typeof(WaveSystemScript.Bubble).GetField("bubbleSelection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); selectionField?.SetValue(bubble, null); } } } }
这种方法会在Inspector修改后自动检查,若引用不在有效列表中则置空并提示,适合快速实现引用校验的场景。
内容的提问来源于stack exchange,提问作者ssnssnss
相关产品推荐
相关产品推荐

