入行半年做的cocos2dx-c++新手引导方案,留存纪念。
新手引导系统
引导触发流程一览
1.管理器GuideManager
1.1 介绍
-
调用新手引导的入口,所有和新手引导相关的操作都在manager中进行。
1.2 入口
-
通过GuideManager::getInstance()获得
1.3 三种基础引导方式
-
硬引导
C++ |
-
硬引导要求玩家只能点击某区域
-
通过ClippingNode实现在遮盖图上裁切出可点击区域
-
判断点击到裁切出的区域后移除遮盖图
-
软引导
C++ |
-
软引导不要求玩家点击限定区域
-
点击在遮盖图上直接移除遮盖图
-
对话
C++ |
-
调用ActorTalkLayer
1.4 引导剧本生命周期
1.4.1 开始阶段
-
遍历可播放列表内的所有剧本。
-
判断执行条件是否满足,满足则开始,否则不执行。
-
从可播放列表中取出剧本,设置为正在播放的剧本。
-
清空、初始化GuidePlayLayer。
1.4.2 执行阶段
-
播放第一个步骤,完成一个步骤生命周期。
-
切换到下一步骤,如果直接触发,则直接播放,否则等待下一次onGuide。
-
判断是否满足条件,如果满足则执行步骤。
-
重复2、3,直到所有步骤完成。
1.4.3 结束阶段
-
清空GuidePlayLayer。
-
发送保存数据给服务器。
-
将后续剧本加入可播放列表。
-
将自己加入已完成列表。
1.5 引导步骤生命周期
1.5.1 生成引导内容
-
在对应的引导层(硬引导、软引导、对话)创建对应内容
-
规定可点击区域
-
根据是否可跳过显示或隐藏跳过按钮
1.5.2 等待用户输入
-
如果是硬引导或软引导,点击跳过按钮或者可点击区域可以进入结束引导阶段。
-
如果是对话,如果不是最后一个对话内容,则点击后播放下一对话,直到对话结束,回到1或者直接结束。
-
如果是硬引导,则在高亮区域允许事件穿透
1.5.3 结束引导
-
如果是最后一步,结束剧本。
1.6 函数
-
onGuide
C++ |
-
该函数在BTLayer的onEnter中默认调用一次,故大部分的引导是以某个类(ui)被创建的时候为时机开始的。
-
通过72号指令可以通过战斗剧本调用该函数,实现战斗过程中的引导触发。
2.数据GuideDataModel
2.1 介绍
-
在GuideDataModel中保存引导的剧本信息、引导剧本的完成情况、目前正在播放的剧本、可以被触发的剧本等内容。
-
Manager通过此类判断是否需要播放剧本或步骤。
2.2 剧本相关数据
2.2.1 剧本构成
-
一个剧本往往是一个完整的引导流程,由多个步骤组成。
-
剧本的触发以类作为锚定,所有剧本必须依托于某个类触发引导。
-
剧本有用作标识的唯一id,不可重复,可以跳号,号码大小不决定触发顺序。不允许使用0。
-
剧本本身记录自己的唯一前置剧本,当前置剧本播放完成时,才允许该剧本播放。0为没有前置剧本
2.2.2 剧本的保存形式-森林
-
剧本的保存类似森林结构,由多棵树组成,每棵树表示一个剧本路线,允许多个剧本路线同时存在,同时可触发。
-
每个剧本记录自己播放完成后有哪些剧本进入可播放状态。
2.2.3 可播放列表
-
有一map记录可播放的剧本信息
-
初始状态将不需要前置剧本的剧本先添加进列表。
-
当没有剧本在播放时,播放列表解锁,触发新手引导的搜索行为在剧本中进行。
-
当有剧本开始播放时,取出该剧本,并锁定可播放列表(同一时间只能有一个剧本在播放)
-
登陆游戏时,先进入初始化状态,再根据服务器传来的播放完成列表进行更新
2.2.4 正在播放的剧本
-
同一时间只能有一个正在播放的剧本
-
当有剧本在播放时,触发新手引导的搜索行为在该剧本的步骤中进行。
2.2.5 播放完成列表
-
记录已经播放完成的列表
-
获取playerinfo时从服务器读取。
-
当剧本播放完成时加入,同时向服务器发送数据进行保存。
3.引导控制器IGuideController
3.1 介绍
-
任何类只能通过控制器与GuideManager交互。
-
控制器与管理器的交互只传递类名信息,即所有的引导触发都是基于类名的。
-
BTLayer继承IGuideController,并默认在onEnter中会和Manager交互一次,实现打开某个ui开启引导。
3.2 实现
3.2.1 类名的传递
-
通过typeid可以获得当前类名
C++ |
-
this是指向类的指针,*this可以获得类本身
-
如果在正常的基类中调用该函数,由于其指针指向基类,只能获得基类的类名,比如在BTLayer的onEnter中调用会打印出Class BTLayer。
-
要想获得基类的派生类的类名,需要使基类具有多态性,即至少拥有一个纯虚函数,由于项目已经非常庞大,修改BTLayer不实际,所以可以在IGuideController中做文章,定义虚析构函数,使继承它的类具有多态性。
C++ |
-
此时typeid就可以获得表达式的动态类型,即在BTLayer的onEnter调用,但可以打印继承了它的子类的类名。
3.3 函数
-
doGuide
传递自己的类名给manager,以此启动引导
C++ |
4.引导层GuidePlayLayer
4.1 介绍
-
所有的引导ui都加载在引导层上。
-
引导层分为三个部分,分别对应软引导,硬引导和对话,三个部分独立管理,可同时存在。
-
层级关系自下而上顺序是软引导、硬引导、对话
-
引导的创建和销毁只能在GuideManager中进行。
5.剧本与步骤
5.1 Json结构
剧本结构
key | value | 说明 | 示例 |
guide_id | int | 引导剧本编号,唯一。 | |
guide_name | string | 剧本名,代码里不用,策划自己方便看 | |
father_guide | int | 前置剧本编号,只有前置剧本已经引导完毕,才能触发该剧本,若填0则不需要前置剧本。 | |
trigger_layer | string | 触发剧本的类名,在该类被创建时(ui显示时)会查询条件是否满足,满足则触发剧本。如果是剧本指令72可以填写Script来触发。 | trigger_layer:“GuideLayer” |
trigger_place | string | 触发时机,onEnter是ui弹出时,onExit是ui被关闭时,指令填Script | |
trigger_level | int | 触发剧本所需的主公等级, 0为不限制 | |
trigger_warId | int | 触发剧本需要通关的关卡waiId, 0为不限制 | |
trigger_special | int | 特殊触发条件,后续有其他触发条件。由这个字段控制,0为无 | |
step_list | list<step> | 剧本的步骤列表 | step_list:[ { }, { } ] |
步骤结构
key | value | 是否必填 | 说明 | 示例 |
step_id | int | 是 | 步骤编号,剧本内唯一,需要连贯 | |
step_name | string | 是 | 步骤名,代码里不用,策划自己方便看 | |
can_pass | bool | 否 | 步骤是否可以跳过,一定程度上规避因为适配导致的无法游戏的问题 | |
trigger_type | int | 是 | 触发方式:0条件触发,1直接触发。如果为1,则上一步骤结束时立即触发执行。0需要根据其他字段判断条件。第一个步骤默认直接出发,请填1。填1时,tirgger_layer和trigger_playce可以不填 | |
trigger_layer | string | 否 | 触发剧本的类名,在该类被创建时(ui显示时)会查询条件是否满足,满足则触发剧本。剧本指令72可以填写Script触发。 | trigger_layer:“GuideLayer” |
trigger_place | string | 否 | 触发时机,onEnter是ui弹出时,onExit是ui被关闭时,指令填Script | |
soft_open | int | 否 | 软引导是否开启,1开启0关闭 | |
soft_img | string | 否 | 软引导图片 | |
hard_open | int | 否 | 强引导黑色遮罩是否开启,1开启0关闭,强引导开启时强制启用点击限制touch_limit | |
hard_pos_relative | int | 否 | 强引导原点相对屏幕的位置,0为中心,1为左侧底部,2为右侧底部,3为左侧顶部,4为右侧顶部 | |
touch_limit | int | 否 | 是否启用点击限制,如果启用,只有可穿透位置可以点击,其他位置不会结束当前步骤 | |
is_follow_widget | int | 否 | 是否跟随控件 | |
follow_className | string | 否 | 控件所属的类 | |
follow_widgetName | string | 否 | 控件名称 | |
is_battle_pos | int | 否 | 是否是战场坐标 | |
battle_pos | int&int | 否 | 战场坐标 | |
hard_highlight | int&int&int&int | 否 | 触摸事件的可穿透位置,同时高亮显示,四个值分别是原点x、原点y、width、height,以屏幕中心为(0, 0),决定强引导的高亮位置,决定点击限制的可点击区域 | |
dialogue_open | int | 否 | 对话是否开启,1开启0关闭 | |
dialogue_pos_type | int | 否 | 对话的相对位置,同hard_pos_relative | |
dialogue_pos | int&int | 否 | 对话的位置,对话框底部中心为原点 | |
dialogue_list | list<dialogue_list> | 否 | 对话列表 | dialogue_list:[ { "heroId": 1, "dialogue": "hhh" }, { "heroId": 1, "dialogue": "hhh" } ] |
6.指令触发
-
通过72号指令可以触发引导的进行。
-
代号666。
-
需要在trigger_layer中填写Script