最近做了一个包含播报制战斗的活动,活动大致是玩家一起挑战某个世界boss,直至将boss消灭,按伤害量进行奖励。该活动的战斗是播报类型的,即战斗过程是完全无操作,玩家只是要看完整个完整的战斗过程。
很显然,在玩家一开始进行战斗时,战斗结果已经确定了,客户端只是在接收到服务端发来的该场战斗的数据后,像播“视频”般将整场战斗播放出来。在此总结分析下战斗部分的代码。
与战斗相关的UML类图:
Main为活动的核心类,拥有很多模块的引用,如声音模块,网络通讯模块,资源加载模块,窗口管理模块,这些在上面的UML类图里没有一一列出来。CombatVideo(战斗播放)和StateTask(状态机)则是与战斗最为相关的模块。
状态机:
通常来说,拥有较多状态表现且状态经常改变的模块,适合设计成状态机模式。
针对播报战斗而言,有进入战斗Loading,请求战斗初始数据,初始化战斗面板UI,加载战斗资源,播放出场动画,请求战斗结果数据,播放战斗,退出战斗等状态,且某些状态需要符合一定条件后才可以转变成下一个状态,如向服务端请求战斗结果,只有当接收到服务端的数据后,才能进入播放战斗的状态。因此,很适合以状态机模式来设计。
战斗模块的状态转换还是非常简单的,基本是线性的,CombatCallState->CombatWaitingState->CombatLoadingState->SCombatResLoadedState->CombatSpiritOutState->CFightRoundState,不涉及复杂的状态变换。
由StateTask按顺序执行各个State的execute方法,当某一State的工作完成后,调用StateTask的pushTask接口,新增一个新的State。
战斗播放:
拿到后台返回的一推战斗数据后,如何将其表现成一场完整的战斗过程,这是整个战斗系统的核心。
核心类:
CombatVideo:持有CombatVideoPlayer,Screen,VideoConverter,负责了整个战斗播放的控制工作
CombatVideoPlayer:接收Segment数据,负责战斗播放
VideoConverter:将服务端的CombatData转换为Segment数据
Segment:战斗播放的最小的单位数据,由CombatVideoPlayer驱动播放。
Screen:战斗场景的UI类
CombatVideoPlayer负责按序执行处理队列里的Segment数据,调用Segment的start方法,并监听VideoEvent.ON_SEG_END事件,每个Segment执行完毕后,派发VideoEvent.ON_SEG_END事件。CombatVideoPlayer监听到VideoEvent.ON_SEG_END事件后,就处理下一个Segment数据。直到处理完所有的Segment数据。
Segment可细分为不同类型的Segment,ActionChangeSpiritSegment、ActionFightSegment、ActionStartSegment分别对应换宠、打击、进场等动作片段。各Segment通过接口控制Screen的Spirit进行各种动作表现,如宠物进场/退场、宠物打击/受击等。