新手引导系统设计与编辑器设计(一)

最近忙成狗。 因为高层的决策, 导致我们年前需要在大部分东西都没有的情况下, 硬生生的怼出一个比较正常, 且能玩, 美术效果还不错的版本, 所以加班是特别厉害。

而且因为TeamLeader启用了 敏捷开发模式 scrum 的项目管理模式。 也是折腾的够呛,这个模式对开发人员要求比较高,主要是要求团队成员的主观能动性特别强,能力越强越能感受到累,因为你平时开发的时候时间都是足够充足的,因为启用了scrum,你可能会被指定为一个sprint的程序的负责人,那么需要你去督导这个sprint中其他的人的开发情况。而且自己做完了之后大概率会马上又从sprint中又挑一个活接着做,所以最后特别的累。

在分功能模块的会议中,分配给我做的是新手引导模块, 而且距离我们分模块到过年只有短短的12天。

12天的时间, 开发一个比较完善的新手引导模块,也不是不可以,但是这种情况下的新手一般是硬写的, 我上一个项目的新手引导,大概写了 5天(不是我实现的), 然后通过 配表, 完整的实现了一个简单的新手引导, 之前的项目新手引导比较简单, 基本就只有逻辑点击这样的流程。属于比较简单的类型。 但是后来当策划需要简单修改下流程的时候, 有部分时候,程序得陪着一起改。这在我看来是不太合理的地方。 因为会消耗大量的工时进入到里面,而且还面临着随时都会修改的问题。

所以在目前这个项目的新手引导, 虽然时间比较紧, 但是还是和teamleader商量之后, 我和他都一致觉得我们应该通过开发编辑器的模式, 让策划直接参与到新手引导的开发, 只要我们编辑器开发的比较完善, 那么以后需要我们去修改的东西也就越少。

罗列一下开发新手引导编辑器需要面临的问题和困难:

  • 开发一个完善的编辑器
  • 定义一套完整流程,以支持编辑器编辑器出来的数据的运行
  • 编辑器编辑的引导流程之间的衔接
  • 跟服务器之间的交互
  • 当玩家掉线或者强退的时候,下次进入游戏的处理问题
  • 教会策划使用编辑器
  • 与其他模块之间的交互
  • 一套完善的事件系统的需求,用于触发新手引导与处理新手引导逻辑

开发一个完整的编辑器


因为我们使用的是Unity, 所以我们直接使用的Unity来进行开发。所以我们面临编辑器的难点有几个:

  1. 一个简单的数据的序列化和反序列化的功能,要足够好用,当我新增功能的时候, 不用为他单独的去写导出之类。
  2. 足够完善的Editor渲染功能, 足以支撑我们写出一个比较正常的代码逻辑, 而不是为了配合渲染模块,而牺牲代码的结构。
  3. 足够好用的编辑功能, 至少保证策划的吐槽也就卡在某一个级别。 不要难用到所有策划都抗议的级别。

对于数据的导出和导入, 因为是使用Unity编写, 所以是使用的C#, C#完善的反射机制能够帮我们实现的这个功能。
首先我们能通过反射拿到 数据项的类型, 然后我们能够通过自定义C# attribute 来定义数据项的存储的键值等,用于在反序列化的时候使用。 因为TeamLeader之前的项目中已经做过这个工作了, 所以直接就拿过来使用, 他是通过 反射+attribute 生成Json脚本。 我之前也写过一个场景编辑器, 也是基于Unity的,功能和实现都差不多, 不过我是生成的是 lua脚本。 因为我们项目目前暂时没有启用lua, 所以直接采用他的版本。

对于Editor的渲染问题, 也是有一套现成的拿UnityApi封装的好的东西来使用, 因为之前有写场景编辑器, 所以也可以直接拿过来使用。(我们的场景编辑器是直接编辑xml的表格数据)

足够好的编辑器功能, 这个是直接使用Teamleader提供框架的功能,是他们原有项目中使用的。 满分10分, 我可以给个5分。 所以我觉得策划应该勉强能接受。

定义一套完整的流程,支持数据的运行


我们大概把新手引导分为3个级别: 脚本 > 步骤 > 节点

脚本

一个脚本是编辑器的基本单位, 策划每次进行编辑的一个完整的流程就是一个脚本,一次引导流程就是一个完整的脚本。 脚本由 一些步骤组成。

步骤

步骤是脚本的基本单位, 步骤之间支持任意跳转,节点之间是串行的,一次只会有一个步骤在运行。 步骤由一些节点组成, 节点之间并行的, 当一个步骤被执行的时候,该节点下的所有的节点都会被执行。

节点

节点是最基础的组成部分, 大量节点的各种组合, 就形成了一个新手引导的流程。
节点分为 普通节点, 事件节点

普通节点不会有触发事件, 比如 SetActiveNode, 这个就是一个普通节点, 用于显示和隐藏某个Game object。 之后就不会有特殊的逻辑了。

事件节点为带有触发事件的节点, 当他条件被满足的时候, 自身就会被触发, 触发之后会根据配置的跳转的步骤的tag, 跳转到其他步骤。 比如 GuiClickNode 指引玩家去点击某个UI元素, 如果玩家点了之后, 那么就触发已经配置好跳转事件。
跳转tag,可以配置为多个, 比如让5分钟玩家打死某只怪物, Succeed: step2
Failed: step3
做一个分支跳跳转。


在这个地方我和我们的teamleader有一些分歧, 分歧在于。我觉得有一些节点, 我们可以定义为通用节点。 比如 GUIClick节点, 比如 SetActive节点, 比如ShowMask节点。 这种可以跨项目通用的节点。 或者是整个新手流程通用的节点。
然后有一些特殊的节点: 比如强制玩家在某个区域建一个建筑,因为包含一些特殊流程, 比如, 我需要修改玩家的合法建造区域,得以让他只能在某个区域建造。 这种与原有逻辑违背的节点,叫做特殊节点。

但是Teamleader的意思是, 新手引导本来就是一个特殊的东西, 他的所有的东西都是特殊的,所有不存在什么通用节点和特殊节点。 不需要去做这个抽象, 这样会增加策划和程序员的理解难度。

我能理解他想表达的意思, 他的观点也是正确的。 这个主要是看法不同, 我比较倾向于让策划去理解, 在这个地方你就要特殊的事情了, 所以和平时还不太一样。

体现到结构上面就是:
一个特殊的建造节点, 采用不抽象的方法的话, 他就是一个单独的节点, 没有什么特殊的东西。
如果采用带一层抽象的方式去做的话, 那么应该是先有一个通用节点 叫做 Command节点, 然后在Command中定义一个特殊的类型, 就是这个特殊建造,

确实有增加了理解成本。而且实现起来也会比较麻烦,所以我也没有纠结这个, 而是直接以文件夹放 Special 一个放 Common区分。不管怎么样,程序层面应该有这么一个区分。


通过这上面3种元素, 我们就能得到足够多的组合, 这样就能够完成新手引导的完整流程。 对于一些特殊的逻辑, 我们就为他单独开发一些特殊的节点, 这种东西没有办法, 就算是使用编辑器也没有办法做到完全通用, 毕竟确实新手引导本身就是一个很特殊的东西。 但是假如我们抽象的足够好, 特殊的节点也不是不能通用。

在这里我们也想到了一些问题, 比如假如玩家掉线了之后如何去做好再次上线之后的引导的衔接问题。 我估计每个做引导的人都会碰到这个头疼的问题。因为我们是demo阶段, 而且这次又特别紧, 所以这次定的策略是 我们指定某几个引导是 属于 保护期引导 , 在第一次会议上, 我们定义的是 在这个期间如果玩家掉线, 那么服务器直接把玩家重置到这个引导流程开始的状态, 因为刚开始出来的时候都是强制引导, 所以玩家是不能操作游戏的, 这个逻辑应该能走通, 但其实特别麻烦, 后来我们又修改成 当玩家掉线, 那么直接清号, 让他数据重置。 再次进入直接以刚登陆开始。 这样特别简单, 服务器和客户端都暂时不用做太多的东西, 先以Demo能跑为主。

但其实对于这种类型东西的处理,我也大概寻思过解决方案:
1.我们可以开发查询节点, 用于在一个脚本开始的时候,对玩家的当前的一些信息进行查询, 然后根据查询的返回结果,跳转到对应的 步骤, 这样可以跳过已经执行完成的步骤。
以指引玩家造一个建筑的例子举例:
我们提供一个GetBuildingStateEvenNode 这样的节点, 用于获取玩家的建筑的状态, 然后根据返回状态跳转到不同的节点:
NotBuild goto step1 (还没有建造,直接从step1开始)
WaitForHarvest goto step5 (我们的建筑需要点击收取, 从step5开始,指引玩家点击收取)
BuildOk goto step8 (已经建好,但是服务器没有当前记录的引导ID切换为下一个, 执行最后几步逻辑(展示几个文字框,然后告诉服务器我已经结束, 需要切换到下一步引导))

2.假如我们开发查询节点, 并支持跳转, 那么会出现一个流程错乱的问题, 简单的情况,
在上面那种情况下的 玩家建筑处于 WaitForHarvest 的时候, 正常流程, 玩家需要打开工具界面, 然后选中锤子, 然后敲击建筑,才能收取。 如果我出现跳步骤的时候, 那么我就需要去复现玩家从正常流程到现在的一个基本流程。

所以节点应该新增一个接口: OnSkip, 在OnSKip函数中,我们需要去处理一些,这个Node被跳过之后的情况, 比GUIClick节点, 可能我们就需要直接用逻辑去模拟玩家点击这个界面流程。这样就能还原玩家在退出之前的状态。但因为之前设计的步骤之间是可以随意跳转的, 所以可能我们并不能知道那些步骤被跳过, 这个时候可能就需要策划去添加属于被跳过,且需要执行Skip步骤。
这个过程我们其实可以简单归于掉线场景的还原。 对于一些需要玩家复杂的场景我们直接应该拆成几个新手引导流程。
而且每个节点的开始,尽量不要依赖上一个步骤的结束,而是直接以一个新的流程开始。
比如每个新手引导流程都从主界面开始, 我觉得从策划层面应该是可以办到的。 如果是 事件触发类型, 比如 等级到9级触发某个引导, 但是这个时候并不知道玩家当时处于什么状况,我们可以直接关闭玩家的所有界面, 同时屏蔽掉玩家的所有输入事件,开始我们的新手引导流程。

这个其实还有简单问题是, 每个流程的Skip有可能并不是一个一帧之间能完成的事情, 比如打开好友界面, 可能我们在打开好用界面的时候, 需要向服务器请求数据才能打开,如果未请求到数据是不能打开。 所以对于Node的 Skip流程,也有顺序的要求。

写了这么多, 发现这样好像对策划的要求比较高。。 但是这个是没办法的事情,如果要做一个完备的编辑器, 从而脱离该改改的境地, 这个是必须做的事情, 要不然只能策划辛苦, 要不然就是程序辛苦。 但是程序辛苦的话,其实还是策划去推动的结果, 所以还不如策划直接去辛苦得了。

最后在说一个我和Teamleader在Node设计的时候的理念的不同情况


他在设计编辑器的时候, 倾向于把策划当傻瓜, 每个节点都是傻瓜式的节点, 比如一个简单的例子, CloseUIInput节点, 这个节点的作用是 关闭UI输入, 所有UI的点击事件,全部不响应,但是 Raycast的事件是可以响应的。
对于这种节点,我倾向于封装2个节点, 一个 CloseUIInput 节点,一个 OpenUIInput节点。 由策划自己去组合节点,达到一个在某个时间点开启UI输入, 然后某个时间点关闭UI输入, 说白了,我比较倾向于让策划以编程的思维去做新手引导流程的编辑。

如果是以他的理念去封装, 应该是 CloseUIInput 在Init的时候,关闭UI输入, 因为我们的node是并行的时候,所以在一个节点,我们加入这个节点,会被执行。 然后在步骤切换的时候,会调用 节点的Reset , 在Reset函数中 去重置, 开启UI输入, 这个过程策划无法干预, 如果有4步需要屏蔽UI输入, 那么策划需要在这4步中都要添加上这个节点。

这2种理念各有各的好处, 以他的想法去写的话, 策划在编辑流程的时候可能会麻烦一点, 但是足够清晰,不容易出错,因为每个步骤需要什么, 你不能指望上个步骤给你遗留什么,因为都是原子的, 你需要自己给自己准备。 所以相当于强制初始化,检查的时候会特别方便。
以我的想法去写的话, 策划在编辑流程的时候,会更加的灵活,在我们编程的时候其实也一样, 组合的方式能够让程序的可变性和可扩展性变得格外的强, 但是缺点就是流程不太好检查。 出了错debug的代价会比较大, 但是我觉得这个不是特别大的问题。因为这个难度其实也不是特别大, 如果策划在新手引导的时候脑子是清晰的, 能够简单的画个流程图,把各种情况预先过一遍,其实完全没问题的。



最后一个问题, 一套完善的事件系统,这个我估计每个项目都应该会有, 主要有个问题是, 新手引导的事件以和业务模块的事件耦合在一起呢,还是应该单独独立出来, 做一个新的事件系统。 我个人比较倾向于独立出来, 因为这套事件系统应该主要给新手引导, 任务, 成就等使用, 独立出来能好一点, 这个仁者见仁智者见智。 随便怎么样都行


突然发现,有些东西还是要自己写出来, 思路才清晰。。就是时间花的多, 我这个基本没排版, 不知道别人写博客还要考虑排版问题, 还要截图, 粘贴代码的 大概花了多长时间。。

新手引导其实还有好多细碎的东西, 比如基础节点的开发, 如: 指引点击一个控件, 指引 遮罩某个物体, 或者指引 遮罩某块区域等, 这些网上有多很教程,我也就不写。 我还是比较喜欢写一点心得之类的东西,因为做东西,其实思路最为重要,想通了就是下笔如有神。


本来以为这篇文章到这里就完了, 但是其实还没完, 第二系列文章问详细阐述一下再实际应用场景下, 遇到的一些问题.

你可能感兴趣的:(Unity游戏开发)