战斗行动机制
战斗行动
是什么
我们将造成伤害、给与治疗、赋给效果、缩放技能等由战斗实体发起的操作称为战斗行动CombatAction,战斗行动独立于战斗实体,存储着此次战斗行动需要用到的所有数据,并持有相关战斗实体的引用
为什么
复杂的战斗会在战斗的过程中插入各种效果和计算,如果只是简单的用一套方法,会难以区分某个效果应该写在攻击者的流程还是写在受击者的流程里,抽象出战斗行动这个概念后,我们就可以把难以区分的效果放进战斗行动里
行动点
是什么
定义
:一次战斗行动会经过并触发相关战斗实体一系列行动点
例子
:比如一次造成伤害的战斗行动,会触发攻击者造成伤害行动点和受击者承受伤害行动点。一次给与治疗的战斗行动,会触发施法者给予治疗行动点和被施法者接受治疗行动点
世界法则
是什么
定义
:世界法则是决定战斗行动(伤害行动或技能行动等)触发后产生的结果
例子
:就比如你赤手重拳出击,你这一击是对敌人造成伤害还是对自己造成伤害取决于你攻击的对象,你自己是没有办法决定的。如果你攻击的是一个鸡蛋,那么鸡蛋会被打碎而你毫发无伤,但如果你攻击的是一个铁球,那么结果会变成铁球毫发无伤而你痛得甩手。这就是现实世界的法则。
联系
战斗实体发起战斗行动,战斗行动触发行动点,世界法则决定行动的结果
能力系统
我们把可以通过应用效果、修改数据来影响角色的属性或状态的这类功能称为能力,这类功能有技能、被动、buff、debuff
能力实体AbilityEntity
是什么
能力实体可以存储某个能力的配置数据(伤害、消耗等)和状态数据(冷却计时、击杀计数等),配置数据是策划配置的固定数据,状态数据时在战斗中会变化的数据
能力执行体AbilityExecution
是什么
能力执行体是用来执行能力表现、触发能力效果应用的地方。可以存储一些表现执行相关的临时的状态数据
技能执行体
通常buff和被动不会有很明显的表现行为,一般来说具有复杂能力表现行为的是主动技能,主动技能的释放一般会伴随玩家的输入,包含下面三种类型:
- 目标战斗实体
- 位置
- 方向
能力任务AbilityTask
在能力执行体中我们可以创建一系列的能力任务来实现各种复杂的技能表现,比如播放动作、创建特效、创建触发器
Status状态效果
状态Status
是什么
状态效果会影响角色的行为和战斗的结果,最终确定角色当前时刻的状态。我们将增益效果称为buff,减益效果称为debuff
状态效果类型
- 属性修饰
诸如提升10%的攻击力、增加30点护甲值这些状态效果我们称之为属性修饰
- 行为禁制
行为禁止与控制,像眩晕、沉默、恐惧等就属于行为禁制类型的状态效果。比如眩晕包含移动禁止、攻击禁止、施法禁止,沉默包含施法禁止,恐惧包含施法禁止、攻击禁止、移动控制。
- 逻辑触发
buff的逻辑触发机制分为3种,时间触发、条件触发、行为触发
。比如按时间触发有几秒后触发伤害、每间隔多少秒造成伤害等,按行为动作触发有受伤时怎么怎么样、攻击时怎么怎么样等,按条件触发有当生命值低于多少时怎么样、当受到的伤害大于多少时怎么样等。(条件机制比较复杂些,基本每种类型的条件都需要单独写代码判断,这个最好是通过抽象分离和事件解耦来减少代码的侵入性。)
- 其他
以上3种虽然囊括了大部分的buff效果,但依然还是会有一些非常特殊的效果可能需要单独写逻辑代码
Buff和被动
buff和被动本质是一种东西,唯一区别就是被动开始装载就一直存在,而buff的装载时机任意,而且可以一直存在或者短暂存在
StatusAbility
状态想过也叫状态能力,属于能力的一种类型,继承于AbilityEntity
StatusAbilityExecution
状态能力执行体,这个用来实现一些表现效果,比如装载了一个被动,每隔几秒就会释放一个逐渐向扩散的光环对敌人造成伤害或者是对己方治疗,就可以用状态能力执行体实现
ConditionEntity
在一些比较复杂的状态效果里,按条件触发的效果会比较多,我们会抽象出一个条件实体ConditionEntity,并统一由条件管理组件ConditionManageComponent
管理,每个战斗实体都会持有一个
Skill技能
Skill和Status的区别
技能
:玩家主动释放,释放之后有一次性的短暂的特效、动作和表现效果,这种能力我们可以将其称为技能
状态
:被动接受,或者是由技能造成的持续一段时间的状态效果,将其称为状态
SkillAbility
我们将抽象的技能概念表示为SkillAbility,实际的技能表现为SkillExecution技能执行体。SkillAbility代表这个技能的状态和信息,比如等级、冷却、是否可用、技能类型等,它是一直挂载在战斗实体上的
SkillExecution
SkillExecution技能执行体由SkillAbility创建,临时挂载到战斗实体上,一次性的由短暂的生命周期的东西,它主要负责技能释放后表现出来的短暂的一系列的特效和效果,。可用结合皆能编辑器或行为树编辑器来实现技能表现
代码分析
黑火球术
Hero
加载技能配置,绑定按键
SkillConfigObject config = Resources.Load<SkillConfigObject>("SkillConfigs/Skill_1001_黑火球术");
SkillAbility abilityA = CombatEntity.AttachSkill<Skill1001Entity>(config);
CombatEntity.BindSkillInput(abilityA, KeyCode.Q);
SkillPreviewComponent
当我们按下Q的时候,会出现圆形指示器,碰到怪物会变成红色
public void EnterPreview()
{
CancelPreview();
Previewing = true;
if (PreviewingSkill is Skill1001Entity)
{
TargetSelectManager.Instance.Show(OnSelectedTarget);
}
}
SkillPreviewComponent
,选中怪物单击左键,会触发上面的回调函数,并生成技能行动,然后执行技能
private void OnInputTarget(CombatEntity combatEntity)
{
if (PreviewingSkill.Spelling)
{
return;
}
//Log.Debug($"OnInputTarget {combatEntity}");
var action = GetEntity<CombatEntity>().CreateCombatAction<SpellSkillAction>();
action.SkillAbility = PreviewingSkill;
action.SkillAbilityExecution = PreviewingSkill.CreateAbilityExecution() as SkillAbilityExecution;
action.SkillAbilityExecution.InputCombatEntity = combatEntity;
action.SpellSkill();
}
private void OnInputPoint(Vector3 point)
{
if (PreviewingSkill.Spelling)
{
return;
}
//Log.Debug($"OnInputPoint {point}");
var action = GetEntity<CombatEntity>().CreateCombatAction<SpellSkillAction>();
action.SkillAbility = PreviewingSkill;
//创建能力执行体
action.SkillAbilityExecution = PreviewingSkill.CreateAbilityExecution() as SkillAbilityExecution;
action.SkillAbilityExecution.InputPoint = point;
//执行技能
action.SpellSkill();
}
Skill1001Entity
,上面的逻辑会调用子类Skill1001的方法,并执行该技能,执行技能的时候会创建一个能力任务CastProjectileTask
,该任务会根据配置信息生成”黑火球“,并按照目标点移动
public class Skill1001Entity : SkillAbility
{
public override AbilityExecution CreateAbilityExecution()
{
var abilityExecution = Entity.CreateWithParent<Skill1001Execution>(this.GetParent<CombatEntity>(), this);
return abilityExecution;
}
}
public class Skill1001Execution : SkillAbilityExecution
{
public override async void BeginExecute()
{
base.BeginExecute();
var taskData = new CastProjectileTaskData();
taskData.ProjectilePrefab = (AbilityEntity as Skill1001Entity).SkillConfigObject.SkillEffectObject;
//创建能力任务
var task = Entity.CreateWithParent<CastProjectileTask>(this, taskData);
//执行能力任务
await task.ExecuteTaskAsync();
//应用能力效果
AbilityEntity.ApplyAbilityEffectsTo(InputCombatEntity);
EndExecute();
}
}
“黑火球”移动到目标点,会根据技能配置信息,应用能力效果
public virtual void ApplyAbilityEffectsTo(CombatEntity targetEntity)
{
List<Effect> Effects = null;
if (ConfigObject is SkillConfigObject skillConfigObject)
{
Effects = skillConfigObject.Effects;
}
if (ConfigObject is StatusConfigObject statusConfigObject)
{
if (statusConfigObject.EnabledLogicTrigger)
{
Effects = statusConfigObject.Effects;
}
}
if (Effects == null)
{
return;
}
foreach (var effectItem in Effects)
{
ApplyEffectTo(targetEntity, effectItem);
}
}
DamageAction,接着会触发伤害行动,根据配置信息还有数值公式,目标怪物会扣血
// TODO
AssignEffectAction,最后还有触发赋予效果行动,目标怪物会中毒
//TODO
技能释放流程如下:
- 生成技能行动
- 执行技能
- 创建能力任务
- 应用能力效果
- 触发伤害行动
- 触发赋予效果行动
参考资料
https://zhuanlan.zhihu.com/p/272216809