Unity中重现Button组件On Click()功能的技术问询
1. 定义可序列化的调用数据结构
先做一个能在Inspector里保存调用信息的类,用来存目标对象、要调用的方法名:
[System.Serializable] public class MethodCall { public UnityEngine.Object target; public string methodName; } public class CustomButton : MonoBehaviour { public List<MethodCall> functionArray = new List<MethodCall>(); public bool callFunctions; void Update() { // 这里用Update做触发示例,实际可绑定到自定义点击/触发逻辑 if (callFunctions) { foreach (var call in functionArray) { if (call.target == null || string.IsNullOrEmpty(call.methodName)) continue; // 用反射调用目标方法 call.target.GetType().InvokeMember( call.methodName, System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, null, call.target, null ); } callFunctions = false; // 避免单次触发重复调用 } } }
2. 编写编辑器扩展实现Inspector选择界面
Unity默认List没法直接选脚本和方法,得自定义编辑器来实现类似Button的选择逻辑:
#if UNITY_EDITOR using UnityEditor; using UnityEngine; using System.Collections.Generic; using System.Reflection; using System.Linq; [CustomEditor(typeof(CustomButton))] public class CustomButtonEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); CustomButton customBtn = (CustomButton)target; EditorGUILayout.Space(); EditorGUILayout.LabelField("Method Calls", EditorStyles.boldLabel); // 遍历所有调用项 for (int i = 0; i < customBtn.functionArray.Count; i++) { EditorGUILayout.BeginHorizontal(); // 选择目标对象 customBtn.functionArray[i].target = EditorGUILayout.ObjectField("Target", customBtn.functionArray[i].target, typeof(UnityEngine.Object), true); // 有目标对象时,显示脚本和方法选择 if (customBtn.functionArray[i].target != null) { MonoBehaviour[] scripts = customBtn.functionArray[i].target.GetComponents<MonoBehaviour>(); if (scripts.Length > 0) { // 选择挂载的脚本 int scriptIndex = EditorGUILayout.Popup("Script", 0, scripts.Select(s => s.GetType().Name).ToArray()); MonoBehaviour selectedScript = scripts[scriptIndex]; // 筛选出无参、返回值为void的公共方法,和Button的OnClick逻辑对齐 List<MethodInfo> validMethods = selectedScript.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.GetParameters().Length == 0 && m.ReturnType == typeof(void)) .ToList(); if (validMethods.Count > 0) { // 选择要调用的方法 int methodIndex = EditorGUILayout.Popup("Method", 0, validMethods.Select(m => m.Name).ToArray()); customBtn.functionArray[i].methodName = validMethods[methodIndex].Name; } else { EditorGUILayout.LabelField("无符合要求的方法"); } } else { EditorGUILayout.LabelField("未挂载脚本"); } } // 删除当前调用项 if (GUILayout.Button("-", GUILayout.Width(20))) { customBtn.functionArray.RemoveAt(i); i--; } EditorGUILayout.EndHorizontal(); } // 添加新的调用项 if (GUILayout.Button("添加方法调用")) { customBtn.functionArray.Add(new MethodCall()); } // 标记对象修改,确保数据能保存 if (GUI.changed) { EditorUtility.SetDirty(customBtn); } } } #endif
3. 关键细节说明
- 反射调用前必须做空值判断,避免空引用报错
- 编辑器里过滤的方法规则和Unity Button一致:只显示无参、返回值为void的公共方法
- 可以把触发逻辑从
Update改成自定义的点击检测(比如OnMouseDown),更贴近Button的使用场景 - 用
EditorUtility.SetDirty确保修改的配置能保存到场景或预制体中
内容的提问来源于stack exchange,提问作者Aliph Null
相关产品推荐
相关产品推荐

