SRPG开发之八-状态

老实讲,状态系统虽然常见,但本作并没有太多(或者可以说,几乎没有)额外的想法,硬要讲的话,也只有状态之间会根据优先级进行替换了(这也是一个非常常见而且非常直观的想法)。这一部分一开始甚至没有写进策划案,或者说,根本就没打算做。

后来,我慢慢想了一些有代表性的角色技能和符卡。单独做出来一个两个并不困难,但挨个写死进去,工作量大不提,还很难解决后续维护的问题。由于大部分的效果,都可以抽象简化为一个或数个“状态”。再加上和网友( @七月七日遊中書 七月太太!(逃)的讨论中对方提出了数个关于状态的点子,于是,最终还是回到了最开始被忽略的状态系统上来。

有过之前拍脑瓜造AVG轮子,最后把自己恶心的不能再恶心了,还得照着krkr现成的意思来的经验,从一开始我就决定参考现有案例,即RPG Maker的实现方法。在实验室摸鱼(嘘……)的时候,花了一上午的时间大致翻了翻自带的代码,发现几乎没有涉及状态系统本身的处理流程,只能大略知道它是对每个行动角色使用一个数组存储了当前的所有状态,而状态对象里又包含了解除时机剩余回合等等,在战斗处理流程(部分会影响状态也会影响大地图行进,这里暂且略过)中进行回调,根据状态在数据库中的设定和附加的特性来执行相应的效果。

相对于回合制RPG游戏,战旗在回调的时机上要更多更丰富,仅仅移动一项,就至少多出来无法移动、移动结束时、移动范围计算阶段时(例如,本次无视Zoc)三项,更不用说与攻击、符卡、角色特性以及其他状态等要素进行排列组合之后了。不过,在原型测试阶段并不需要考虑那么多,只要能确定目前这个框架大概(FLAG再一次高高竖起)能够处理比较常见的状态就足够了。具体的回调点和特性,可以等状态本身的详细设计完成后,再根据实际需求实装。循环式和递归式开发的苦头我可是吃够了,尽管在可预见的未来,这个苦头还要不可避免的继续吃,可见没事儿翻翻软件工程是多么重要,尽管我还是看不懂。

Fusion里虽然可以使用数组,但并不能像C++那么方便,给角色对象新建一个容器作为成员变量,然后把状态随便往里面塞就行。更何况,当前版本中,对于Active对象,还有着26个私有双精度变量,10个私有字符串变量,32个私有布尔型变量,并且不能插入其他类型的变量,这种对入门者而言没什么影响但在需要的时候非常恶心并且匪夷所思的限制(在2016年的早期宣传阶段,Clickteam就展示了完全不限制成员变量种类和数量,并完全支持面向对象的下一代Fusion原型)。为每一个角色新开一个二维数组(是的,你不能用容器,尽管内部的实现可能就是链表或容器),老实讲,在当前的ACE事件体系下并不现实,或者说,异常繁琐。常言道“入乡随俗”,一个机制的设计总有其道理,转变思维方式总比对着看来的轻松,如果好好利用Fusion的对象筛选机制和循环机制,是完全可以避开容器实现的。

新建一个Active对象,作为状态对象使用,成员变量中除该状态的名称、持续回合、解除时机等变量外,还要储存附加的角色对象的ID(Fusion中,每个对象都有一个唯一的Fixed Value,大致可以理解成指针)。角色对象则对应新增一个保存当前角色拥有的状态数量的变量(然而这个根据习惯新建的变量到最后也没用上=_=)。

首先实现的,是状态动画(状态对象兼职播放状态动画,或者说Active对象都默认具有Sprite属性)和角色的坐标同步,即一个对所有单位进行For-each循环的Always循环下,使用当前循环到的单位的ID筛选状态对象,随后对筛选出来的对象进行For-each循环,更新其坐标为当前循环到的单位的坐标。其余功能的实现思路大同小异,不再赘述。对于状态动画还会进行排序,避免堆叠。除了默认的根据先后顺序排序外,还提供了根据剩余持续回合和状态优先级等其他参数进行排序的功能。

状态销毁方法会将所有持续回合数小于零的状态销毁,状态的销毁也就被抽象成了持续回合数的变更。在所有状态需要被销毁的场合,都会先将其持续回合数指定为-99,然后调用销毁方法:回合开始时,更新当前行动角色的状态持续回合,调用销毁方法;回合结束时,判断按概率销毁条件,调用销毁方法;使用特定道具解除时,调用销毁方法……正式版本中,状态的解除会有更多更复杂的条件,仅就原型而言上述已经足够。

除此之外,还提供了状态(包括状态特性)的检测、附加和强制解除方法。状态在附加前,先会对现有状态进行检测,不存在附加状态的情况下,会直接附加状态,在存在时,则会追加状态层数和持续回合。对于可替换状态的情形,高优先级的会替换低优先级的,优先级设定为-1的会强制替换现有的可替换状态,并且可以被所有可替换状态替换。



在特定的回调点,会对当前行动单位或目标单位或所有单位的状态进行检测,根据所查状态或特性的不同,执行相应的功能,例如无法行动、或受到攻击时解除,在需要的时候,显示提示信息。回调完成处理后,如果处理过程中没有指定出口,则通过默认回调出口离开,继续进行其他流程的处理。

所以我前前后后花了小一个月的时间,基本上做的全是C++模板库的工作,只用筛选和遍历,假装自己在用容器,假装自己在用容器自带的插入、删除、排序、遍历……啊,家家有本难念的经,一家更有一家坑,填不了,绕便是了。


评论(1)

© defisym | Powered by LOFTER