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

如何禁止在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:33:23