深入UE5——GameFeatures架构(六)扩展和最佳实践(完结)

引言

前文介绍过AddComponents这个最常用的Action的作用机制,至此GameFeatures的各部件原理其实讲解的就差不多了。在学习完这些原理之后,有一个问题你肯定会开始琢磨,那我该怎么用好GameFeatures呢?这个问题的回答要分为两部分,一是你在使用GameFeatures的过程中,你不可避免的需要一定程度对它进行改造和扩展;二是有一些原则和经验你可以借鉴参考,避免趟坑走弯路。

扩展

一,Action扩展

接下来来说一下我们如何扩展Action,这也是我们很经常也很有必要要做的操作。还是老套路,《古代山谷》里也已经实现了一些挺有用的Action,相比原生的4个,新的Action都是非常有用的,比如输入映射,SpawnActor,为Actor添加Ability。我们可以这些Action开始学习借鉴如何写一个Action。

在实现Action的时候有一个值得注意的地方是,因为GFS是继承于EngineSubsystem的,因此Action的作用机制也是Engine的,因此在编辑器里播放游戏和停止游戏,这些Action其实都是在激活状态的。因此特意有定义了一个WordActionBase的基类,专门注册了OnStartGameInstance的事件,用来在游戏启动的时候执行逻辑。当然在真正用的时候,还需要通过IsGameWorld来判断哪个世界是要发挥作用的世界。

二,GameFeatrues依赖机制

这里要讲一下GameFeature跟普通的插件不一样的地方。普通的插件一般是被CoreGame所引用,用来实现CoreGame的支撑功能。而GameFeature是给已经运行的游戏注入新的功能的,因此CoreGame理论上来说应该对GameFeature的存在和实现一无所知,所以CoreGame就肯定不能引用GF,而是反过来被GF所引用。资产的引用也是同理,GF的资产可以引用CoreGame的,但不能被反过来引用。 所以在有了GF框架之后,一个游戏的架构应该就是如图一样,底层是引擎和一些其他模块,再上面是CoreGame和一些插件。到这里为止是跟以前的方式是一样的。现在有了GF就在上面又加了一层,允许有一些GF来给CoreGame注入一些逻辑。

需要讲清楚这一点的原因是,当我们在扩展或自定义我们自己的类的时候,也常常会有一个疑惑,我是应该把这个我们自定义的类应该定义在哪里呢?是应该写在CoreGame还是GameFeature里?因此就必须事先理清楚依赖关系。

三,可继承扩展的类

当然,前面也讲过,你很可能也需要对GameFeature框架进行二次开发扩展。因此我也给大家列一下你有可能会需要继承下来的类:

  • UGameFeaturesProjectPolicies,决定是否加载GF的策略,CoreGame
  • UGameFeatureStateChangeObserver,注册到GFS里监听GF状态变化,CoreGame
  • GameFeatureData,为GF添加更多描述数据 UGameFeaturesSubsystemSettings::DefaultGameFeatureDataClass ,CoreGame
  • GameFeatureAction,更多Action,CoreGame&GF
  • GameFrameworkComponent,更多Component, CoreGame&GF
  • ModularActor,更多Actor, CoreGame&GF
  • IAnimLayerInterface,改变角色动画, CoreGame&GF

我在前面讲过我们可以扩展Actor,Component,Action。这些我们自定义的类应该定义在哪里呢?我在每个的末尾用加黑给你标明了。我们要注意,GF在未加载的时候,定义在其中的C++类是不会被加载到编辑器的,因此像Policy类是应该写在CoreGame里的。而Action和Component的扩展可以定义在GameFeature中。

四,GameFeature模块协作

一些朋友可能会问一个问题,一个游戏玩法用到的模块还是挺多的,GameFeature都能和他们协作进行逻辑的更改吗?下面这个图,我列了一些模块和GameFeature的协作交互方式,大家可以简单看一下。如果有别的模块需要交互,还可以通过增加新的Action类型来实现。

最佳实践

一,参考《古代山谷》和Lyra

第一个推荐的,当然是《古代山谷》项目了,大家可能光注意场景的炫丽,而没有去看项目的C++代码。其实《古代山谷》项目其实整合了很多UE5 EA的新功能,因此还是很值得去研究一下里面的实现的。比如两个GameFeature的实现,一开始无人机的视角的HoverDrone,还有在黑暗世界里战斗的AncientBattle。此外在项目里还实现了之前提过的很有的模块,ModularGameplayActors,还有Framework和GameFeatures下面的代码都实现得挺不错的。然后也可以去看一下这两个GF里的Action是如何配置的。 伴随UE5正式版发布的Lyra初学者项目,也是非常好的学习资料。

二,CoreGame预留好逻辑注入点

GameFeature的出现,虽然说大大解耦了游戏玩法和游戏本体之间的联系,CoreGame理论上来说应该对GF一无所知,极致理想情况下,CoreGame也不需要做任何改变。但GF明显还做不到这点,还是需要你去修改CoreGame的一些代码,事先预留好逻辑的注入点。要修改的点主要有这些:

  • 原先的Gameplay Actor修改为从之前的Modular里继承,或者自己手动添加AddReceiver的调用。
  • 在动画方面,可以利用Anim Layer Interface,在GF里可以定义不同的动画蓝图来实现这个接口。之后在组件里Link Anim Class Layers绑定上就可以修改动画了。
  • 关于数据配置,既可以用AddDataRegistrySource添加数据源到CoreGame里现有的DataRegistry里,也可以在GF里用AddDataRegistry新增新的数据注册表。

至于其他的模块部分,例如UI、输入这些,之前已经讲过可以用Action和Component的配合来做。下图以动画的注入为例子:

三,如何转换现有逻辑到GF?

这时候有一个问题出来,如果我的项目已经开发了一段时间,现在我也想在我们项目应用GameFeature,那应该怎么做呢? 我在这里列出了一些步骤给大家参考:

  • 创建Plugins/GameFeatures目录
  • 创建.uplugin文件
  • 资产
    • 移动资产到新的GF目录
    • 在原目录Fixup redirectors
    • 修复所有资产验证问题
  • 代码
    • 创建.Build.cs
    • 迁移项目代码到GF下的Public/Private
    • 修复include路径错误
    • 修复代码引用错误
    • 在DefaultEngine.ini里加CoreRedirects +ClassRedirects=(OldName="/Script/MyGame.MyClass", NewName="/Script/MyFeature.MyClass"

可能还会遇见别的问题,但只是改变结构,应该都是比较容易解决的。祝大家好运~

四,Rethink in GF

GameFeature是一个新的框架,改变的更多是逻辑组织方式和项目的模块架构划分。因此就需要升级一下脑袋,重新以GameFeature的方式来思考。

  • 首先面对一个项目,你需要去思考哪些游戏功能应该可以拆分成GF?哪些是游戏的基本机制?哪些是可以动态开关的功能?就是确定哪些东西应该抽出来形成一个GF。
  • Actor逻辑拆分到Component,重新以(组件==功能)的方式来思考,而不是所有逻辑都堆在Actor中。然后这些逻辑,在之前往往是直接写在各种Actor身上,现在你需要重新以(组件==功能)的方式来思考,在以前我们提倡Component是用来实现相对机械的独立于游戏逻辑的基础功能的,现在我们为了适应GameFeature,就需要把一部分Component用来单纯的当做给Actor动态插拔的游戏逻辑实现了。因此你需要把这些逻辑从Actor拆分到Component中去,并做好事件回调注册和转发的相应胶水衔接工作。
  • 在Gameplay的各个方面考虑注入逻辑的可能,数据、UI、Input、玩法等。如果你是在做新的项目或者玩法,你在一开始设计的时候就要预想到未来支持GameFeature的可能,留好各种逻辑注入点。
  • 进行资产区域划分,哪些是CoreGame,哪些是GameFeature。当然在资产部分,要进行区域划分,哪些是CoreGame用到的资产,哪些是只在Game Feature里使用的资产,各自划分到不同的文件夹里去。
  • 考虑GF和CoreGame的通用通信机制,事件总线、分发器、消息队列等。在某些时候,你可能光是利用Action和Component还不够,为了解耦CoreGame和GF,你可能还会需要用到一些通用的通信机制,比如事件总线分发器消息队列之类的。
  • PAK和DLC是对游戏主体的补充,GF是动态装载卸载功能,虽然也可通过PAK动态加载。自然的,有些人可能会想到GF跟Pak和dlc有一点点像,是不是可以结合一下呢。这里也注意要辨析一下,pak和dlc是在资产级别的补丁,一般来说是在资产内容上对游戏本体的补充。而GF是在逻辑层面,强调游戏功能的动态开关。但是GF也确实可以打包成一个pak来进行独立的分发下载加载,因此GF也是可以配合热更新来使用的。

五,注意要点

GameFeature虽然好用,也推荐大家去使用。但依然要注意一些点,免得踩坑。

  • 保证GF是自包含的,不需要依赖外部。首先是怎么强调也不为过的,你在实现一个GF的时候,一定要保持一个意识,这个GF是自包含的,不需要依赖外部。当然在某些时候,GF之间也是可以互相依赖的,你要偏要用GF1给GF2里的Actor添加组件,也不是不可以,只要你在GF1的插件依赖列表里添加上GF2。
  • 在代码层面,要注意一些特殊点,比如GF的StartModule会在Mounting的时候被加载一次,之后就不释放了。因此ShutdownModule不会调用,不要当做会像Deactivating那样去写逻辑。另外AddComponent的接收者Actor Class配置不要配置成Actor类本身,否则会检查场景里所有的Actor,大大影响性能。
  • 我们前面说过Action是Engine级别的,这意味着GF其实也可以在Editor模式下使用,比如开启一个GF就可以在编辑器上开一个新的工具栏,但是对于Editor这种相对静态的功能,不太推荐这么折腾。
  • 当然GF也不一定适合所有项目,对于玩法比较固定的游戏,比如单机游戏,或者企业项目,可能就不太适合用GF,你直接按原来的方式直接放在一起可能会更加的便利。

总结

最后我们来总结一下,整套GF框架其实就是给每个插件增加一个GFD资产来定义每个GF的Action列表,其中最常用的AddComponent会把ActorClass和ComponentClass的组合注册到GFCM里进行管理。

希望我一系列的文章能够给大家带来收获,能够理解这个框架图。有关于GameFeature的疑问和问题,也欢迎大家在评论区提问。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值