如何禁止在UnityEvent所属类外部调用其Invoke()方法?
解决方案:限制UnityEvent仅内部触发,允许外部订阅
要实现外部仅能订阅事件、无法手动触发Invoke()的需求,核心思路是隐藏UnityEvent的Invoke权限,只对外暴露订阅/取消订阅的接口。以下是两种实用方案:
方案1:直接封装订阅方法(简单直观)
把UnityEvent设为私有,对外提供专门的订阅/取消订阅方法,完全隔绝外部对Invoke的访问:
using UnityEngine; using UnityEngine.Events; public class Unit : MonoBehaviour { // 私有事件实例,仅本类可调用Invoke private UnityEvent _internalTriggerEvent = new UnityEvent(); // 对外公开订阅方法 public void AddEventListener(UnityAction listener) { _internalTriggerEvent.AddListener(listener); } // 对外公开取消订阅方法 public void RemoveEventListener(UnityAction listener) { _internalTriggerEvent.RemoveListener(listener); } // 内部逻辑触发事件(仅本类可调用) private void CheckInternalConditions() { // 示例:当满足某个内部条件时触发 if (/* 你的内部判断逻辑,比如血量低于阈值 */) { _internalTriggerEvent.Invoke(); } } }
外部调用示例:
public class RandomClass : MonoBehaviour { [SerializeField] private Unit _unit; private void Start() { // 合法操作:订阅事件 _unit.AddEventListener(() => Debug.Log("事件触发了!")); // 非法操作:无法调用Invoke,因为_internalTriggerEvent是私有,且无公开Invoke方法 // _unit._internalTriggerEvent.Invoke(); // 编译报错 // _unit.Invoke(); // 编译报错 } }
方案2:面向接口编程(更规范)
定义一个仅包含订阅方法的接口,让Unit类实现该接口并对外暴露接口实例,从类型层面限制外部的操作权限:
using UnityEngine; using UnityEngine.Events; // 定义仅允许订阅的接口 public interface ISubscribableEvent { void AddListener(UnityAction call); void RemoveListener(UnityAction call); } public class Unit : MonoBehaviour, ISubscribableEvent { private UnityEvent _internalEvent = new UnityEvent(); // 对外暴露接口类型的属性,外部仅能调用接口定义的方法 public ISubscribableEvent PublicEvent => this; // 实现接口的订阅逻辑 void ISubscribableEvent.AddListener(UnityAction call) { _internalEvent.AddListener(call); } void ISubscribableEvent.RemoveListener(UnityAction call) { _internalEvent.RemoveListener(call); } // 内部触发事件 private void Update() { // 示例:按下空格键触发(仅内部逻辑可控制) if (Input.GetKeyDown(KeyCode.Space)) { _internalEvent.Invoke(); } } }
外部调用示例:
public class RandomClass : MonoBehaviour { [SerializeField] private Unit _unit; private void Start() { // 合法操作:通过接口订阅 _unit.PublicEvent.AddListener(() => Debug.Log("接口方式订阅的事件触发了!")); // 非法操作:无法调用Invoke,因为接口未定义该方法 // _unit.PublicEvent.Invoke(); // 编译报错 } }
注意事项
- 如果需要在Unity Inspector面板中编辑事件(比如拖拽回调),可以把私有UnityEvent标记为
[SerializeField],这样编辑器里能配置,但外部代码依然无法访问它。 - 两种方案都能完全避免外部手动调用Invoke,确保事件仅在你定义的内部条件下触发。
内容的提问来源于stack exchange,提问作者Alex Terkov
相关产品推荐
相关产品推荐

