这是一个很好的问题。自定义事件和函数在蓝图中的确有很多重叠之处,但它们的本质设计目标不同,分别适用于不同的场景。理解它们的区别,能帮你写出更清晰、更高效的蓝图逻辑。


核心区别总结

特性 函数 自定义事件
执行方式 同步立即执行 可同步,也可异步/延迟
返回值 可以有返回值 无返回值(纯触发)
执行流程 调用者等待完成 调用者触发后继续执行(可并行)
延迟/等待 内部不可用 Delay 等节点 内部可使用 DelayTimeline 等
网络复制 需手动处理 RPC 可设置为 Replicated,自动同步
调用方式 直接调用,必须有引用 可通过接口、事件分发器等广播
性能 稍快(内联优化) 稍有开销(事件调度)

1. 执行方式的本质差异

函数:同步调用栈

  • 调用函数时,执行流程立即跳转到函数内部,完成所有操作后才返回到调用点。

  • 函数内部不能使用 DelayTimelineAsync 等需要等待的节点,因为函数必须同步完成。

自定义事件:可异步的事件驱动

  • 调用事件时,执行流程触发事件后立即继续,事件内部的逻辑在同一帧稍后多帧内异步执行。

  • 事件内部可以使用 DelayTimeline 等节点,实现“等待一段时间后再做某事”的效果。

举例
你想让角色开火后等待 0.5 秒再换弹。

  • 用函数:无法实现,因为函数不能延迟。

  • 用自定义事件:在事件内部放一个 Delay 0.5 节点,然后执行换弹逻辑,完美实现。


2. 通信方式的差异

函数:点对点调用

  • 必须知道目标对象是谁,通过对象引用直接调用。

  • 适合“我要明确让某个对象做某事”的场景。

自定义事件:广播/接口式触发

  • 可以通过 事件分发器(Event Dispatcher)接口(Interface) 或 全局事件管理器 触发。

  • 一个事件可以被多个对象监听,实现一对多的通信。

  • 适合“发生了某件事,相关对象自行处理”的场景。

举例
角色死亡时,需要播放 UI 动画、禁用输入、播放音效、通知成就系统等。

  • 用函数:你需要手动获取所有相关组件的引用,一个个调用。

  • 用自定义事件:在角色上定义一个“OnDeath”事件,UI、输入、音效等模块各自监听该事件,角色只需触发一次,所有监听者自动响应。


3. 网络复制的差异

在多人游戏中,函数需要手动标记为 ReliableUnreliableServerClient 等 RPC 类型,且调用时必须小心区分客户端/服务器。

自定义事件在蓝图中可以直接设置为 Replicated,并指定复制方式(Multicast、Server、Client),系统会自动处理网络同步,使用起来更直观。


4. 性能差异

  • 函数:UE 会尝试内联优化,调用开销很小。

  • 自定义事件:有轻微的事件调度开销,但在绝大多数游戏逻辑中可以忽略不计。

如果你的逻辑需要极高频率(如每帧执行的 Tick 内),且不需要异步,用函数更合适。


5. 什么时候用函数,什么时候用自定义事件?

推荐使用函数的情况

  • 需要返回值(计算、判断、获取数据)。

  • 逻辑是同步的,没有等待需求。

  • 需要高频调用(如每帧计算)。

  • 逻辑只针对某个特定对象,且你持有它的引用。

推荐使用自定义事件的情况

  • 需要延迟Timeline 等异步操作。

  • 需要广播通知多个对象。

  • 网络多人游戏中需要简单处理复制。

  • 希望解耦代码,让事件发起者不关心谁响应。


总结

自定义事件并非“多此一举”,它和函数服务于不同的设计需求:

  • 函数:同步、有返回值、直接调用、适合纯计算和确定逻辑。

  • 自定义事件:异步、无返回值、可广播、适合解耦和时间相关逻辑。

两者可以混合使用:在函数内部调用事件,或者在事件内部调用函数,各司其职,让蓝图逻辑更清晰。