战斗框架设计

游戏内的战斗框架涉及多个模块,包括技能,施法单元等。

大芒果对wow的实现

可施法单元Unit将会执行castspell,对某个目标使用某个法术进行施法。看起来所有的AI功能都是由CreatureAI来做的,每个精灵都会有一个CreatureAI指针,用来管理自己的行为逻辑,包括移动,施法等。例如施法的时候,会取出与自己关联的实体对象Unit* pCaster,然后调用Unit的CastSpell的方法对target使用spell法术进行战斗。而CastSpell里,将会根据法术信息创建一个Spell即法术,然后调用法术spell的prepare方法准备开始对目标进行作用。prepare方法里将创建一个spellevent,然后如果需要立即释放就调用cast方法,里面进行一系列检查后,调用handle_immediate方法,如果确认没什么问题 ,将调用DoAllEffectOnTarget方法,对目标进行所有效果的施放,调用DoSpellHitOnUnit对目标执行伤害,此时把攻击者加入被攻击者的威胁列表里,设置为被攻击者的攻击者setAttackBy,此时被攻击者启动战斗逻辑,攻击者把被攻击者放到自己的列表里。

在Unit里,维护了两个Manager,一个是ThreatManager和HostileRefManager用来管理威胁自己的对象列表和自己威胁的对象列表。

在设计面向接口的过程中,我们可以这样做,抽象出一个FightService,此服务将会负责管理两个单元之间的战斗逻辑,由于战斗都是基于技能,那么服务将设立一个FightUnit,一个Sprite和FightUnit有一一对应的关系?

如何结合mecanim,nodecanvas。mecanim是动画行为表现,不同的动画之间的切换通过事件触发,比如角色五连击,要依据玩家是否有战斗按钮输入。主要就区分下各个系统之间的关系。可以参考下别家的做法,看看他们的战斗系统,技能如何管理,与精灵之间的关系,行为树的应用,状态机的应用等。这些都值得参考下。

像暗黑3里,法师按下Q键触发回血,如果此时回血CD已经走完,直接回血,然后播放声音“好多了”,如果此时血量很低,会播放声音“我倒想那么做”,这就可以用行为树编辑出来,而不会用mecanim。

另外一个游戏的设计

每个精灵都有一个battlemanager,一个skillmanager,battlemanager会用到skillmanager,要弄明白整个的结构,可以梳理下流程。

战斗会有一个叫做EntityParent的类,看起来是战斗单元,里面有CastSkill方法。

Entity里会监听一个UI事件,OnNormalAttack,当UI触发这个事件时,将会调用battleManager的NormalAttack方法,normalattack里将会执行castskill,并且设置了个计时器,待一段事件后,再次执行normalAttack。

当调用castskill方法时,需要传入skillID,然后从skilldata里取出相应的技能。

如果是角色自己在请求施放技能,就会执行一个RPC,告诉服务器要执行施法技能;如果不是角色在请求施法,就会调用battlemanager的castskill方法。

精灵的状态机

和我之前做的差不多,流程是在changestate的时候,先做currentstate的exit,再做newstate的enter,然后newstate的process。Entity自己存了一个当前状态

currentMotionState。这些个状态的切换是什么用处呢?

主要有这么几个状态:

呼吸状态(IDLE),//进出这个状态没有多余操作,在process中会根据当前精灵类型设置animator属性,以及速度等变量值。

行走(walking),//进入这个状态,如果是角色自己,那么会给animator设置移动速度,注意是给动画状态机设置的移动速度。为什么只给玩家设置呢?其他是服务器设置?

         // 离开这个状态,精灵(所有类型)的移动速度为1,如果是怪物会有别的速度设置,如果是角色自己,给animator设置移动速度为1

         // 如果是服务器控制的怪物的话,会设置移动速度,所以进入这个状态主要就是设置速度了,但是在哪里设置精灵现在切换到动画状态的呢?有可能是给            animator状态机设置的speed变量值,导致状态机可以切换了。

死亡(Dead),  // 进出此状态没有特殊操作

         // process里,会判断当前的动作名称,根据动作名的结尾串,设置相应的动作。animator里的变量action,将被设置成对应的整数值。

          然后触发声音组logicSoundEvent里的OnHitYelling事件,应该就是播放怪物的死亡叫声

拾取(picking),//这个状态的进出无操作,process里就是融合了picking这个动作。

攻击(attacking),//在切换到这个状态时,会传入技能ID,在process里,取出skilldata,skilldata里存有skillaction列表

          // 这里将会执行延时回调,触发OnAttackingEnd事件,也许是这样的设计,当前这个action有duration,当这个duration到了,就说明action执行完了

          // 然后触发OnAttackingEnd事件。有可能它们的设定就是以时间为准,而不是以美术设置的关键帧事件,这样服务器端也可以用这个时间来模拟战斗过程。

          // 最后执行entity的onAttacking方法--->battlemanager onattacking(这里没做什么)--->skill manager onattacking 直到这一步才会去设置战斗动画

          // 所以有些奇怪,因为其他状态都是自己去setaction,切换animator的动画,到这个战斗状态却是由技能管理器切换,可能是在这个状态里做太复杂了

          // 所以最好还是都抛事件,让外面去切换动画状态

被击(Hit),   // 当进入这个状态时,会传入攻击者和受击者的ID,通过一些列的动画判定,决定当前精灵受击应当播放的具体action,然后设置animator的action,

         // 此时会触发一个OnHitYelling事件,播放被击音效。

准备(prepare,主要是给技能施放前准备的),//这个状态说是在攻击前做准备用,其实什么也没做

锁定(lock),//这个状态就是和locking动画做crossfade,为什么没有让mecanim去做这件事呢?

charging(突进),//这个状态只有一句设置动画的代码 setAction(4),说明就是个切换动画

翻滚(roll),//翻滚状态,在process里,将会直接设置当前精灵的animator事件,没做其他的事情。

切换状态的时机:

fsmstate还是附着在entity身上,battlemanager里做了一些状态切换,还有其他一些地方也做了切换。

BattleManager

是个基类,在创建的时候就要监听自己所归属那个精灵的状态事件,

主要有这么几个:

OnPrepareEnd    //这个状态是给精灵做技能前准备的,例如旋转角色,朝向,靠近目标等

OnAttackingEnd //攻击结束状态,回到IDLE状态

OnHitAnimEnd   //受击结束

ORollEnd,//滚动结束

OnHit,//受击,这里会判断是否播放特效等一些操作

基类里的castskill做的是切换玩家的动作,告知状态机说现在切换状态了,切换到attacking状态,但是他这个状态可不是mecanim的状态,mecanim的状态仅仅只是动画。

SkillManager 

SkillManager技能管理器,每个精灵有这么一个,使用的是skilldata数据,这个数据是如何初始化的?

SkillAction和SkillData是两组数据,当播放一个技能时,需要指定一个actionID。

SkillManager会收听一个onAttacking事件,负责执行真正的action,触发attackingfx产生特效,然后告知entity播放特效,再告知fxmanager播放具体的特效。attackingMove产生移动,因为SkillAction有附加的速度,因此会告知Entity的motor执行移动操作。

attackbuff产生buff

随后会执行delayAttack,里面执行AttackEffect,查找出客攻击的Entity列表,对于主角来说,执行AttackDummy攻击怪物;对于怪物来说,执行AttackPlayer;其中将执行CalculateDamage计算伤害,然后抛出一个OnHit事件,如果怪物死亡(仅针对客户端怪物),就执行RPC调用,通知服务器。

每个精灵的BattleManager都会监听OnHit事件,当收到这个事件后,处理受击逻辑,包括播放被击特效,击退,击飞等效果(击退等移动效果由Entity内部的moto管理)。

看起来,它的状态机是因为当时4.3版本没有Mecanim提供的状态机脚本支持而自己实现的状态机,耦合还是很重的。

关于行为树的应用。行为树只负责精灵的逻辑控制,务必整理一个架构。

角色:当玩家开启自动战斗时,就让行为树启动逻辑。

伤害计算放在了CalculateDamage,这个类里只有静态方法,相当于一个静态对象,它没有数据成员,所有的数据都是从Entity里拿到的,例如计算攻击者此次的伤害值,就是 传入一个攻击动作ID,攻击者ID,被击者ID,然后函数将取出攻击者和被击者的数据,进行计算,然后返回结果给SkillManager。SkillManager里会执行对攻击者消耗的计算以及血量的计算。

因此skillmanger里做了攻击属性的逻辑,那么battlemanager的职责是什么呢?

时间: 2024-10-10 05:37:22

战斗框架设计的相关文章

静态网页框架设计首次体验(文章改)

根据教材与上网成功解决了Tomcat与Myeclipse的安装,同时熟悉了Java web创建项目到部署运行整个过程.今天起正式开始学习有关Java web的编程部分.Java web静态网页(HTML网页)的标记含义.基本语法的介绍到框架设计基本模板与案例,今天的学习的内容,让网页编程有了一个初步的框架.结合自身所在协会的情况,计划制作一个关于协会的网页,已有初步想法,望通过学习不断完善和修改协会网站.根据今天所学,并参考书本30页框架设计案例对网页进行初步搭建. 具体代码如下 TW.jsp:

Linux设备驱动框架设计

引子 Linux操作系统的一大优势就是支持数以万计的芯片设备,大大小小的芯片厂商工程师都在积极地向Linux kernel提交设备驱动代码.能让这个目标得以实现,这背后隐藏着一个看不见的技术优势:Linux内核提供了一套易于扩展和维护的设备驱动框架.Linux内核本身提供一套设备驱动模型,此模型提供了Linux内核对设备的一般性抽象描述,包括设备的电源管理.对象生命周期管理.用户空间呈现等等.在设备模型的帮助下,设备驱动开发工程师从设备的一般性抽象中解脱出来.但是每个设备的具体功能实现还需要大量

JS读书笔记:《JavaScript框架设计》——第12章 异步处理

一.何为异步   执行任务的过程可以被分为发起和执行两个部分. 同步执行模式:任务发起后必须等待直到任务执行完成并返回结果后,才会执行下一个任务. 异步执行模式:任务发起后不等待任务执行完成,而是马上执行下一个任务,当任务执行完成时则会收到通知. 面对IO操作频繁的场景,异步执行模式可在同等的硬件资源条件下提供更大的并发处理能力,也就是更大的吞吐量. 但由于异步执行模式打破人们固有的思维方式,并且任务的发起和任务的执行是分离的,从而提高编程的复杂度. 多线程.多进程均可实现异步模式. 二.从回调

WisDom.Net 框架设计(七) 验证框架

WisDom.Net-验证框架 1.分类 这里我们将数据验证分为以下几种 数据类型校验      主要用于确保数据类型输入的正确  比如年龄一项输入 A岁 ,显然不合法 域检查               主要用于验证输入的数据的是否在取值范围  比如在年龄一项 输入 400 ,显然这里不合法 格式检查            主要用于检查数据格式是否正确, 比如Email 输入Ca 显然这里也是不合法 自定义检查,       自定义校验数据. 比如 校验数据格式是否合法等 2.简介      

WisDom.Net 框架设计(五) 权限设计

WisDom.Net --权限设计 1.需求分析     基本在所有的管理系统中都离不开权限管理.可以这么说,权限管理是管理系统的核心所在. 权限管理说白一些就是每个人能够做什么,不能够做什么.可以说是一套规则.下面就说一下,在wisdom.net中的权限     1. 控制用户修改和删除数据.即 用户编辑和删除自己创建的数据,但是只能编辑和删除比自己权限小的人创建的数据     2. 模块的控制. 用户只能访问自己被授权访问的模块,不能访问其他模块     3. 用户被赋予不同的角色,各个角色

WisDom.Net 框架设计(四)

WisDom.Net  ----用户安全 1.用户单机登录 正如其名这里要求其实就是显示用户只能在一台电脑上登录.防止多处登录,这里简单的说一下实现原理,我们在这里使用session +cookie 的方法来实现  如下图所示 (1) 输入用户名密码 (2) 校验用户名密码格式是否正确 (3) 传入用户名密码 (4) 校验用户密码是否正确,返回登录LoginGuid (5) 用户名密码是否正确 (6) 判断用户在session中是否存在,存在即更新用户LoginGuid,不存在则新增,并在coo

游戏UI框架设计(三) : 窗体的层级管理

游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反向切换.代码如下: "普通显示"模式允许多个窗体同时显示,这种类型应用最多.例如RPG中的主城界面(见下图). "隐藏其他界面" 模式一般应用于全局性的窗体.我们在开发此类窗体时,为了减少UI渲染压力.提高Unity渲染效率,则设置被覆盖的窗体为"不可见&qu

niubi-job:一个分布式的任务调度框架设计原理以及实现

niubi-job的框架设计是非常简单实用的一套设计,去掉了很多其它调度框架中,锦上添花但并非必须的组件,例如MQ消息通讯组件(kafka等).它的框架设计核心思想是,让每一个jar包可以相对之间独立的运行,并且由zk辅助进行集群中任务的调度. 接下来,咱们就一步一步的来看下niubi-job整个的框架设计与实现. 框架设计概述 讲解之前,让我们先来看一张niubi-job的框架设计图.如下: 可以看到,该图的结构非常简单,只有四个部分组成. web控制台:负责发布任务,监控任务的状态信息,上传

基于SEDA的异步框架设计与实现

基于SEDA的异步框架设计与实现 二.为什么使用SEDA 目前,面对并发环境,主流互联网服务器编程模型有两种:多线程模型以及事件驱动模型.但是这两个模型都不足以解决这个问题.我们来首先看一下这两种编程模型. 1.多线程并发模型 多线程并发模型是目前最普遍的服务器编程模型,该模型的架构如下图所示:        该模型针对每一个请求,会为其创建并分配一个线程.该线程负责这个请求的处理.该模型的优点:执行粒度是整个完整的处理流程.处理逻辑清晰,容易开发.但与此同时缺点也很明显:如果处理过程中某一步骤

Entity Framework 实体框架的形成之旅--Code First的框架设计(5)

在前面几篇介绍了Entity Framework 实体框架的形成过程,整体框架主要是基于Database First的方式构建,也就是利用EDMX文件的映射关系,构建表与表之间的关系,这种模式弹性好,也可以利用图形化的设计器来设计表之间的关系,是开发项目较多采用的模式,不过问题还是这个XML太过复杂,因此有时候也想利用Code First模式构建整个框架.本文主要介绍利用Code First 来构建整个框架的过程以及碰到的问题探讨. 1.基于SqlServer的Code First模式 为了快速