今天给大家分享一下,我们做微小抖小采用的游戏暂停方案的设计。
游戏暂停需求分析
1: 当角色升级的时候,需要弹出升级的选项,此时游戏需要暂停,包括子弹飞行,人物动画等。

2: 开宝箱/获得游戏道具时,需要暂停游戏,然后查看当前你获得哪个道具。此时游戏也是暂停的状态,游戏中的动画,技能,粒子都暂停。
对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀

3: 游戏暂停/结算时,也需要暂停游戏,不再计算与迭代;

游戏引擎的暂停API能否使用?
大部分的游戏引擎都提供了暂停的API,如Cocos Creator 3.8提供的暂停API为: game.pause/game.resume; Unity提供的暂停API为: Time.timeScale = 0,暂停游戏,Time.timeScale = 1则恢复游戏。
这类API最大的问题就是让整个游戏迭代全部暂停了,包括逻辑,动画,粒子等。但是这里会出现一个问题,如果我的UI上面要带动画,比如我获得一个宝箱,要开一个宝箱,要播放一个动画特效,这时使用系统的暂停接口就无法满足需求了。所以做商业项目时,我们大概率都不会采用系统的API来直接进行暂停。
商业项目中如何针对暂停来做架构与设计
思考1:规范化业务逻辑脚本的Update调用。
通常情况下,目前主流的引擎Unity/Cocos/Laya都有脚本代码的Update,理论上来说每个Update,在设置暂停的时候都可能要停止迭代。所以写Update代码的时候要相对慎重。如果为每个Update来做暂停机制,会比较麻烦。
思考2: 游戏暂停,我们是针对战斗逻辑暂停,其它的部分可以不用暂停,这样开宝箱等动画需求时,都不会受到影响,我们只是暂停游戏的逻辑迭代+游戏角色的动画暂停,游戏世界中的粒子暂停。
针对以上的思考,我们给出的解决方案如下:
针对"游戏逻辑的迭代",我们把"暂停机制"做到战斗管理迭代的Update中。基于战斗管理的Update迭代,我们来迭代战斗中所需要的各战斗单元的逻辑迭代。每个"战斗单元的逻辑"迭代我们不写道传统组件的Update里面,而是单独的函数LogicUpdate来进行实现,在战斗管理里面统一来迭代。
protected update(dt: number): void {if(this.state !== FightState.Playing) { // 游戏暂停中,不做迭代计算return;}// 处理上一帧的碰撞引擎的碰撞对BulletCalcSystem.Instance.Update(dt);// 军团策略决策LegionCtrlSystem.Instance.Update(dt);// 怪物策略策略MonsterCtrlSystem.Instance.Update(dt);// 本地导航的迭代;this.NavSystemUpdate(dt);// 技能与Buff迭代this.OnSelectSkillUpdate(dt);this.OnSkillAndBuffUpdate(dt);// 子弹迭代this.OnBulletsUpdate(dt);// 同类物体的互斥控制EntityMutexSystem.Instance.update(dt);// 复活需要复活的军团this.OnReviveLegionUpdate(dt);// 角色动画同步迭代this.CharactorAnimStatusUpdate();// 角色血条同步UpdateBloodSystem.update(dt);// 子弹,角色删除回收this.OnDeleteEntitiesUpdate();// end// 传送门传送Heroif(this.isShowTransfer) {this.OnTransferHeroUpdate();}// end}
如此一来,当游戏战斗世界要暂停的时候,只要针对"游戏管理"来暂停不迭代,从而导致游戏管理的update不再迭代其它的战斗单元的"Logic Update",这样导致整个的游戏世界+游戏战斗单元全部暂停。而UI等都不会受"游戏暂停"的印象。
游戏暂停时,我们只暂停"战斗世界里面的战斗单元"的动画,特效,粒子,遍历每个战斗单元的角色上的相关组件,对这些动画组件暂停即可,不影响其它地方的动画播放。
public FightPause() {this.state = FightState.Paused;// 获取所有的Spine动画组件,然后暂停动画组件var spSkeAnimArray: sp.Skeleton[]= this.entityRoot.getComponentsInChildren(sp.Skeleton);if(spSkeAnimArray && spSkeAnimArray.length > 0) {for(var i = 0; i < spSkeAnimArray.length; i ++) {spSkeAnimArray[i].paused = true;}}// end// 获取所有的Animation动画组件,然后暂停动画组件var spAnimArray: Animation[]= this.entityRoot.getComponentsInChildren(Animation);if(spAnimArray && spAnimArray.length > 0) {for(var i = 0; i < spAnimArray.length; i ++) {spAnimArray[i].pause();}}// end}
public FightResume() {this.state = FightState.Playing;// 获取所有的Spine动画组件,然后暂停动画组件var spSkeAnimArray: sp.Skeleton[]= this.entityRoot.getComponentsInChildren(sp.Skeleton);if(spSkeAnimArray && spSkeAnimArray.length > 0) {for(var i = 0; i < spSkeAnimArray.length; i ++) {spSkeAnimArray[i].paused = false;}}// end// 获取所有的Animation动画组件,然后暂停动画组件var spAnimArray: Animation[]= this.entityRoot.getComponentsInChildren(Animation);if(spAnimArray && spAnimArray.length > 0) {for(var i = 0; i < spAnimArray.length; i ++) {spAnimArray[i].resume();}}// end}
总结:
游戏暂停:
(一)针对游戏战斗世界暂停,游戏逻辑管理负责做好游戏战斗单元的暂停与恢复即可:暂停战斗单元迭代,暂停战斗单元动画,特效。
(二)战斗单元的逻辑更新迭代,不要写到传统组件的Update里面,而是新增加一个Update函数,由游戏世界管理来进行统一的迭代,这样就可以统一在游戏世界暂停的逻辑下来进行管理了。
,
605

被折叠的 条评论
为什么被折叠?



