|
GameRes游资网授权发布 文 / 水风
技能模块主要功能为技能信息管理和技能流程控制,技能信息管理在SkillManager中实现,主要是为AI等上层模块提供技能信息查询接口,管理unit所拥有的技能,对技能静态信息的运行时修改如武器符文改变技能等需求提供支持。技能流程控制在SkillController实现,提供统一的技能调用接口,管理技能动态目标选择,并使用技能树控制技能的执行流程。
技能模块介绍
##技能控制器SkillController
技能控制器提供技能调用的统一接口,提供三个接口:
- 技能开始:开始执行技能,若技能不循环进行,则技能可以自动结束。
- 技能结束:有的技能不能自己结束,比如普通攻击,当玩家松开按钮,调用技能结束接口,告诉当前技能使其结束,此时技能到达后摇点时,技能不再继续执行。
- 技能停止:当技能被强制打断时,如被攻击、晕眩等,技能会被强制停止。(此功能没有实现,感觉优先级较低)
技能真正的结束后,提供回调接口。
此外,技能控制器负责**目标查找**,技能在技能树执行过程中,从技能控制器中获得目标。
##技能类型
从技能释放表现和方式来看,技能分为两种类型,单次释放技能和重复释放技能。
- 单次施放技能:大部分玩家技能和所有怪物技能属于此类技能,这类技能具有持续时间,技能执行一段时间后技能主动结束。
- 重复释放技能:此类技能不会自己结束,如普通攻击和暗黑中的一直旋转攻击,这类技能一般是玩家点击按钮开始执行技能,不放开按钮则会一直执行下去不会结束。
##技能树
技能树是真正的技能执行部件,技能树参考行为树的设计思想,使用树形结构控制技能执行流程,并可以根据判断信息选择执行流程。
同行为树一样,技能树节点有两类:**控制节点**和**执行节点**。控制节点是控制子节点的执行,而执行节点则是真正执行一个技能行为。
理论上来说,行为树中的selector、sequence、parallel、precondition等节点类型都可以使用在技能树中,但是根据职责分配,复杂的AI逻辑应该放在行为树模块处理,而技能树只处理简单的逻辑。因此,技能树简化了传统行为树的内容,目前控制节点只实现了sequence节点类型,后面根据需求慢慢增加。
**去掉tick**
技能要求较高的实时性,不能像行为树一样每秒tick几次,因此技能树取消tick概念,当子技能执行结束后,通过回调函数立刻告知父节点,父节点进行相应的处理。
**可查询信息**
AI等上层若需要查询的技能信息,统一放在技能树的根节点。原因:Ai查询信息肯定是将其作为统一的技能。
**例子**
......
-野蛮人普攻(根节点)
-野蛮人攻击一段(节点1)
-冲锋(节点1-1)
-挥砍(节点1-2)
-挥砍二段
-挥砍击三段
......
##技能流程
技能节点enter(播放动作) -> 前摇点(技能结算) -> 后摇点(技能结束:技能节点exit,调用callback)
每个执行节点都有单独的技能流程,进入执行节点技能开始,此时播放技能动作,前摇结束后技能开始真正的执行处理逻辑,到了后摇点,技能执行结束,从执行节点离开。动作播放和技能处理逻辑无关,技能到达后摇点离开执行节点,动作可能还没有播放结束。
若一个技能由子技能搭配实现,如一个技能由冲锋+攻击构成,只有到达攻击技能的后摇时,这个技能才算结束。(参考下面要介绍的顺序节点中的原子性)
在一个技能执行过程中,如果一个新的技能到达技能控制器,技能控制器会等待当前技能结束后才执行下一个技能。
**技能流程和动作播放没有任何依赖**,到达后摇点技能就是结束了,如果没有新的技能,那么技能动作就会继续,如果有新的技能,新技能动作会覆盖上一个技能的动作。
##顺序节点
此节点会顺序执行所有可以执行的子技能。
目前顺序节点有以下属性:
- 循环技能:表示该节点会循环执行子技能,当最后一个子技能结束时会重新执行第一个子节点,技能没有执行完成的概念,只能由外部打断。
- 原子技能:表示该节点的所有子节点是一个原子操作,当外部需要结束此顺序技能时,必须等待所有子节点执行结束(最后一个子技能的后摇点)才算结束。
##并行节点
并行节点同时执行子节点的所有技能。
一个unit有些功能比如动作等是冲突的,不能同时执行两个动作。因此并行节点的第一个子技能具有所有的技能功能,但是其他子技能并不应该具有动作、转向等。
##随机节点
##策划如何使用
以普通攻击为例,普通攻击是一个循环执行三个子技能,三个子技能又是由冲锋+挥砍两个技能构成。如下所示:
......
-野蛮人普攻(根节点)
-野蛮人攻击一段(节点1)
-冲锋(节点1-1)
-挥砍(节点1-2)
-挥砍二段
-挥砍击三段
......
普攻技能是一个循环技能,玩家只要按住按钮,技能就会循环执行一段、二段、三段,所以普攻技能根节点是一个顺序节点,且它是循环技能。
而普通人攻击一段也是一个顺序节点,同时它是一个原子技能,冲锋结束并不代表着攻击一段结束,若冲锋过程中玩家停止技能(松开按钮),冲锋技能后摇并不代表着技能结束,只有挥砍后摇结束才是技能结束。
表中增加了对应表头:技能树节点类型(0/不填:执行节点,1:顺序节点),顺序技能列表(原来就有),循环机能、原子技能。
对于大部分普通技能,如创建一个法术场,创建一个子弹等,只需要创建一个执行节点即可。
##表头说明
技能树:
位移功能:位移功能在所有技能中均可以使用。
不针对自己
**发射着动作**
方向跟随人物面向:废弃
起始位置跟随枪口:废弃
锁法术场:废弃
延迟时间,后要时间,是否选择最近目标:废弃
指定枪口,动作时间锁定面向:废弃
子弹飞行:
飞行速度:对于方向弹道的技能,必填
其他不用填。
随机列表、弹幕相关:废弃。
落点变异:废弃,和弹幕有关。
暴击:保留暴击倍率和概率,其他废弃。
允许施法角度:废弃
射程半径参考:废弃
有效:
强制设计半径
最小攻击半径
射程半径
射程指示器系数:废弃
攻速是否影响动画:废弃
开火模型:废弃
开火特效组:废弃
子弹特效组:有效
技能冷却时间:只有技能树的根节点填写有效
技能模块的同步
技能模块包括:技能流程、法术场、弹道和buff。
首先介绍authority和proxy的概念,这两个概念是基于单位unit的基础上进行的区分。
authority表示单位的主控端,即此单位是有客户端和还是服务端控制。对于玩家avatar,玩家本地的客户端就是主控端。而对于怪物、NPC等,他们的行为由服务端控制,主控端就是服务端。
proxy表示代理端,表示被主控端控制。如对于怪物来说,所有的客户端都是proxy;对于玩家A来说,服务端和其他玩家的客户端都是proxy。
技能同步的原则
1.客户端先行
对于玩家控制的单位来说,玩家点击按钮释放一个技能,客户端首先响应,单位播放动作以及相应的技能特效。
2.技能流程以authority为发起端
玩家单位技能发起是由她的客户端,怪物的技能发起是由AI也就是服务端。
3.技能结算在服务端发起。
技能真正的结算,比如法术场检测、buff结算、伤害结算等,统一在服务端处理。
1、技能释放流程的同步
技能流程负责动作、特效以及技能结算,其中技能结算包括:释放法术场、弹道或buff。
技能流程中,需要同步的有两个时间点:
技能开始:技能开始播放动作
技能结算:前摇结束,即能进入结算逻辑
以玩家点击技能按钮开始释放技能为例介绍技能同步流程,如图所示:
Paste_Image.png
1.主控端点击技能按钮,技能开始播放动作,主控端告诉服务端技能开始。
2.服务端广播给所有的客户端(多玩家场景),告知其他所有的客户端此玩家开始执行技能。其他客户端收到指令后可是播放技能表现。
3.服务端延迟一段时间后,服务端开始进行技能结算,并且将结算结果通知客户端。
延迟时间=技能前摇时间-上行-下行,下行一半不能确定,所以默认为上行=下行
另一种中庸的计算方式是:延迟时间=技能前摇时间-上行,防止要求技能前摇时间过长
使用此同步流程的表现为:
1.要求技能前摇时间>2*网络延迟,若前摇时间短,则延迟时间=0,效果可能差一些
2.authority客户端表现完美。
3.proxy client表现一般,即玩家A看玩家B的效果为:玩家B刚开始执行技能动作,没到前摇时间就进行了技能结算。但是因为玩家一半不会过分关注其他玩家的动作,一般是可以接受的。
2 、技能树的同步
我上篇文章技能系统已经介绍,我们游戏使用的是技能树来管理技能流程。那么就面临一个问题,技能树如何同步。
最简单最暴力的方式,是客户端和服务端同时管理技能树,并且将其状态同步。这样,客户端和服务端的技能树状态统一、完备。
后来发现,对于proxy端,并不需要完备的技能树信息,最节省的方式是proxy根本不接受技能树同步信息,只是接受播放动作、技能结算等信息。但这样需要告诉其他proxy播放什么动作、特效等。
最终,游戏选择的同步方式是:
同步技能树根节点和技能树叶子节点的控制信息。同步的消息类型包括:进入节点,离开节点和攻击信息。
同步根节点的原因是:根节点保存一个完整技能的信息,需要和技能模块外部交互,因此需要同步。
叶子节点的执行代表着技能真正的执行逻辑,需要同步。
而对于其他节点,作为流程控制节点,只需要在主端确保技能流程无误即可。
3 、技能结算的同步
技能结算包括创建法术场、buff、弹道、技能直接伤害等。
法术场、弹道的同步
法术场、弹道的同步比较类似,他们都作为一个entity(网络同步单元)在服务端创建,创建以后使用entity管理机制服务端通知客户端他们的创建和销毁。
以法术场为例,法术场的执行和同步流程:
服务端发起创建一个法术场,并且通知客户端
法术场每隔一段时间结算一次,注意,法术场结算并不需要同步,每隔一段时间服务端执行检测逻辑,客户端播放结算特效等。两个逻辑互不依赖,也不要求时间一致。
当法术场结算时检测到攻击目标时,服务端计算攻击伤害等信息,并将攻击信息发给客户端。
客户端收到伤害信息,客户端播放相应的表现,如法术场受击特效等。此处还包括属于通用模块的跳字等。
当服务端的法术场时间到了进行destroy时,使用entity的管理机制通知所有客户端destroy法术场。
弹道的同步类似,唯一的区别就是法术场在某一位置使用攻击盒检测目标,而弹道是一个移动的子弹,客户端表现是一个特效在飞,而服务端每隔一段时间根据飞行速度等使用胶囊攻击盒去检测目标碰撞。
由以上可以发现,法术场作为一个entity他的管理成本是比较高的,所以若策划想出一些需求需要使用多个法术场实现,一般通过拓展法术场功能使用一个法术场来实现。
比如,策划要做一个冰火两重天法术场,即法术场在每次结算时使用不同的参数,第一次结算使用火焰,第二次结算是冰霜。若这种需求较少,可以使用两个法术场,但是如果要冰火雷毒水电风魔奥术神圣*N重天,则代价太大。一般可以让法术场支持每次使用不同的结算参数来结算即可。
buff同步
buff是附加在unit身上的东西(没有unit就没有buff,但是没有unit可能有法术场),所以不需要使用entity来同步。
服务端确定buff是否可以挂在unit上面。
客户端和服务端都维护一个buff管理器,挂buff的消息通知所有客户端,客户端负责表现,服务端负责结算即可。
4 、伤害、属性的同步
主要介绍下伤害的同步,顺便附带介绍下属性同步。
对于伤害结算来说,技能、buff、法术场和弹道都可能造成伤害,当服务端发现造成伤害时,服务端首先根据技能信息计算伤害值,计算以后将信息通过技能模块发送给所有客户端,所有客户端接到信息后首先播放技能伤害相关的表现,如受击特效等,然后播放跳字等通用伤害客户端表现。
buff可能修改单位属性,如攻击力、攻击速度等。单位的属性由基础成长属性+装备属性+buff属性构成,前两个属性为面板属性,属于玩家信息。而buff属性只在战斗中有效,在面板中并没有表现。
因此装备属性的同步需要通知逻辑服务器(真正的服务器),而buff并不需要通知逻辑服务器。
欢迎前来讨论、交流,我的博客:www.codingart.info
相关阅读:游戏编程干货:游戏技能系统全解析
|
|