EventPlayer | 懒人专用的免费可视化事件插件

大家好!我叫Threeyes,是全世界最懒的程序员之一,开发这款可视化插件的唯一原因就是:减少重复工作!消除加班时间!可通过以下渠道获取:

开发痛点

使用Unity作法时,我经常遇到以下问题:
  • 需要频繁调用系统组件或第三方插件的公开方法,或需要组合使用多个插件的接口
  • 脚本所在物体被隐藏,但需要调用其协程方法
  • 基于时间(如延迟或间歇)调用方法(如:子弹射出后延迟销毁,或程序化控制灯泡闪烁)
虽然代码就那么几行,但本人作为洁癖狂就是不想写鸭!因为:
  • 这么简单的功能也需要写个不通用脚本?
  • 编写脚本不需要时间?
  • 编译不需要时间?
  • 摸鱼不需要时间?
Unity在2015年提供了 UnityEvent 特性,开发者得以可视化编辑回调方法。这虽然能减少上述部分代码工作量,然而仍有以下缺点:
  • 无法方便地更改回调方法的调用顺序,如从[唱→跳→Rap]改为[跳→Rap→唱](Unity2020版增加了编辑器回调方法列表重排的功能,但只能同时在Inspector窗口中编辑单个UnityEvent)
  • 无法禁用某个回调方法
  • 无法混用、重用多组回调方法
  • 无通用的组件载体,开发者需要在代码声明UnityEvent及其子类的字段,或者是使用与Unity其他功能模块耦合的组件(如Button或EventTrigger)
为了解决以上问题,我为项目组写了一个EventPlayer插件用于管理UnityEvent,不但减少了新手的学习成本,还能够将代码复用率提高数十倍。项目组在此插件基础上配合基础通用库可开发多个不同逻辑的项目,并且大部分时间不需要写任何额外的脚本,目前在数十个项目中运行良好。

EventPlayer核心功能

名词解释
  • 子EventPlayer:子物体中的EventPlayer或其子类
EventPlayer组件
每个项目都能细分为模块及基础功能,而大部分基础功能都是通过一小段方法组合而成,我们暂时称之为“元素方法”。本插件通过将“元素方法”与EventPlayer及其子类的实例绑定,将 编程的文本增删改操作 变为 编辑器中的可视化物体摆放 。
EventPlayer组件是整个插件的核心,用于管理UnityEvent,可挂在任意游戏物体上。它有以下功能:
  • 可在Hierarchy直接调用事件,以及查看当前的配置信息
  • 当Play/Stop方法被调用时,执行对应UnityEvent(onPlayStop/onPlay/onStop)
  • 可设置激活状态(IsActive)
  • 可设置自动执行事件(IsPlayOnAwake)
  • 可设置事件是否仅能执行一次(IsPlayOnce)
  • 可设置Play/Stop相关事件是否翻转(IsReverse)
  • 可设置为组长(IsGroup),管理子物体中的EventPlayer或其子类
  • 可向监听者广播事件(listListener)
  • 可指定ID,便于匿名或远程调用
  • 支持网络同步
视频演示【0 BasicSetting.unity】:
EventPlayer子类(时间相关)
以下组件继承于EventPlayer,提供了与时间相关的事件控制功能:
  • DelayEventPlayer:延迟执行事件,如延迟销毁
  • RepeatEventPlayer:重复执行事件,如重复播放提示音
  • TempEventPlayer:临时调用Play事件,并在指定时间后调用Stop事件,如临时亮灯
视频演示【3 Coroutine.unity】:
EventPlayer子类(参数相关)
以下组件调用事件时,可接收或向子EventPlayer传递特定的参数,并且支持仅当参数匹配才会执行事件。EventPlayer提供了常用的组件,开发者也可以创建其他参数的子类:
  • EventPlayer_Int/Float/String/...:传递基础的参数
  • EventPlayer_SOAction:将执行方法存储在ScriptableObject的子类实例中,并通过EventPlayer调用,可以实现跨场景共享行为、持久化行为,让调用更加灵活。(注意:该功能尚未集成到当前版本中,有兴趣的可以下载本人另一个Github库 AliveCursorSDK ,通过PackageManager窗口导入Action案例进行测试)
  • EventPlayer_PlayableInfo:实时接收Timeline Clip的相关信息,后面详叙
  • EventPlayer_Video:扩充VideoPlayer组件的功能,提供播放/暂停/音量控制等基础功能,可传入指定的VideoClip参数来播放视频
EventPlayer_Video视频演示,其中提前展示了后面提到的EventPlayer组及EventPlayer序列组件功能【31 Extend_VideoPlayer.unity】:
EventPlayer组
当EventPlayer为组长时(IsGroup字段为true),该组件能够管理特定范围子物体所挂载的EventPlayer,当其Play/Stop等方法被调用时,会按照子EventPlayer在物体层级的顺序调用同名方法,该特性能够极大地提升事件组织能力。可以把一个组理解为一个方法体,把每个子EventPlayer理解为方法体中的一行代码。
Hierarchy中各EventPlayer的关系如下:
EventPlayer成组有以下特点:
  • 所有EventPlayer及其子类都能设置为组长,并保留原功能特性,如DelayEventPlayer成为组长后可以让所有子EventPlayer延后执行
  • 可将参数传到带参子EventPlayer中,从而实现参数传递
  • 隐藏的子EventPlayer也能够被父EventPlayer管理及调用
  • 可快速直观地改变子EventPlayer的执行顺序。因为EventPlayer与游戏物体绑定,因此通过改变游戏物体在层级中的顺序,就能影响对应EventPlayer实例的执行顺序(类似于代码段重排,但是避免了代码的重编译耗时)
  • 不同类型的子EventPlayer组件放在同一组中,可以轻松组合出复杂的逻辑。如在程序开始后立即播放背景音乐,并在第2帧后初始化UI,然后临时显示3D指示路标并在5秒后消失。
  • 常见的方法序列可以单独变成一个通用组(如下面的EventPlayerGroup(Common)),方便开发人员在任意地方调用:
EventPlayer序列
使用EventPlayerSequence的子类,能够按指定顺序调用某个子EP,适用于按特定顺序调用EP的需求。其中子EP被禁用时,序列会无法执行下一步,适用于仅当条件满足才能继续的任务类逻辑。
视频展示【10 Sequence.unity】:

EventPlayer扩展功能

Timeline
此类组件用于监听事件及接收参数,可用于Timeline进度同步等复杂操作。
Unity提供了优秀的 Timeline 库,能用于创建影片内容、游戏序列、音频序列和复杂的粒子效果,然而其提供的Signals功能并不完善。在开发时难免有在特定进度调用事件的需求,比如动画运行到某一帧时调用指定方法(虽然可通过AnimationEvent或TimelineSignals实现,但是耦合度高且不灵活),或者通过UI显示动画的播放进度。
此类需求的关键在于需要在特定时间/时间段内获取Timeline的信息,正好EventPlayer的功能之一就是传参。插件将Timeline片段的运行时数据(开始、结束、时长等)封装成PlayableInfo实例,然后通过EventPlayerClip将相关信息实时传递给场景的EventPlayer实例,之后就又回归到熟悉的EventPlayer可视化编辑流程。
开发者只需要在Timeline Track中创建一段EventPlayerClip,并为其指定场景中的某一个EventPlayer实例作为目标,当Timeline进入/退出该EventPlayerClip片段时,EventPlayer实例的Play/Stop方法会被调用。如果需要获取Timeline片段的PlayableInfo数据,可以将上述的目标换成EventPlayer_PlayableInfo,并将其listListener字段添加对应的TLEPListener子类即可。程序流程如下:
Timeline中使用EventPlayer的优点:
  • 仅需要写少量代码即可使第三方插件适配Timeline,(仅需处理PlayableInfo数据,减少开发者写Timeline、Playable相关代码的痛苦)
  • 可获取Clip的实时信息,进行实时预览及相应同步操作
  • 配合DoTweenPro等插件可实现程序化动画,减少手搓动画的痛苦
Timeline基础视频展示【21 Extend_Timeline.unity】:
Timeline+BezierSolution插件视频展示【21_1 Extend_Timeline_BezierSolution.unity】:
Timeline+DoTweenPro插件视频展示【21_2 Extend_Timeline_DoTweenPro.unity】:
编辑器绘制工具
为了数值可视化及方便开发者自行编写子类,插件提供了编辑器界面信息的复写方法,只需要在方法体中提供自定义信息,就能在Hierarchy窗口实时显示。
整体类图

所以EventPlayer有什么特点

既然已经有那么多成熟的可视化插件(VisualScripting、PlayerMaker、Bolt),这个插件的存在意义在哪? 如下:
  • 没有新增窗口,一切都在Unity原窗口中显示,避免使用VisualScripting等插件需要来回切换窗口的麻烦
  • 零学习成本,物体摆放及编辑器属性设置操作都是初学者必修技能
  • 下完即用,小巧(核心代码不到100KB的容量,增加的项目负担可忽略不计)
  • 模块化设计,支持自定义代码
  • EventPlayer的信息保存在场景或预制物的资源文件中,因此将EventPlayer挂载在预制物上,可以实现方法重用或跨场景使用
  • 仅使用Unity内置接口,不涉及Reflection、Inject等高级特性,兼容各平台,与Unity各版本兼容性良好(经测试,Unity2018.4~2022都能使用)。只要Unity不更改UnityEvent的接口,本插件基本不出大问题(紧跟Unity官方更新节奏的同学们应该能理解那种接口刚用习惯,然后下一版啪一下就弃用的痛苦囧)
  • 最重要的一点是,他还提供中文支持!
当然,任何事物都有优缺点,强如一拳超人也只是个光头。本插件与UnityEvent的缺点类似,你需要注意:
  • EventPlayer如果分组不当或随意引用,可能会导致调用混乱。(无规矩不成方圆,好的组织能够避免此问题。后面会介绍推荐的分组)
  • EventPlayer与GameObject绑定,而GameObject与预制物或场景文件绑定,因此注意回调方法不能跨场景链接,否则会出现引用丢失的问题(可以通过ID调用)
  • UnityEvent通过方法名称进行事件绑定,因此一旦方法被删除/重命名/改变参数数量或类型、类所在的命名空间有变动、使用 Assembly definitions 封装Dll后,可能会导致UnityEvent无法找到对应方法。(如下图,原回调方法是用户充值后即可增加运行帧率,后来增加了一个充值价格的参数,会导致方法栏显示“Missing”的错误)

预告

目前Github及AssetStore上的插件为稳定版,后续预计会进行重构,涉及修改如下:
  • 优化库之间的依赖关系,增加可选Asmdef
  • 通过 package 的形式发布新版,方便通过PackageManager集成

注意

  • 部分视频录制时间有点久远,有些组件名字发生变动(如IntEventPlayer→EventPlayer_Int,EventPlayerSequence→Sequence_EventPlayer),但核心功能不变。

写在结尾

希望各位看官试用完插件后提供更多建议,你们的挑刺就是对我最大的支持,谢谢朋友们!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值