设计MOBA技能系统(分析篇)
发表于2016-11-20
该系统设计分三篇:
理论篇:解释技能实现机制,我是链接
分析篇:基于dota2技能源码来分析,我是链接
代码篇:编码结构设计,我是链接
沙王地震具体的实现

首先定义技能通用说明,这是一个技能的基础部分
1 2 3 4 5 6 7 8 9 | "BaseClass" "ability_datadriven" //继承的父类 "AbilityType" "DOTA_ABILITY_TYPE_ULTIMATE" //技能类型 --- 终极大招 "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_CHANNELLED | DOTA_ABILITY_BEHAVIOR_NO_TARGET | DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING" //技能行为 --- 引导类型技能|可以无目标攻击|忽略后摇 "AbilityUnitDamageType" "DAMAGE_TYPE_MAGICAL" //伤害类型--- 魔法 "SpellImmunityType" "SPELL_IMMUNITY_ENEMIES_NO" //法术免疫类型---不免疫 "FightRecapLevel" "2" // "AbilityTextureName" "sandking_epicenter" //技能图片资源名 "AbilityCastAnimation" "ACT_DOTA_IDLE" //技能动作名 ---休闲攻击 |
1 2 3 4 | "AbilityCooldown" "140.0 120.0 100.0" //技能cd "AbilityDuration" "3.0 3.0 3.0" //持续时间 "AbilityChannelTime" "2.0 2.0 2.0" //引导时间,或者叫吟唱 "AbilityCastPoint" "0.0 0.0 0.0 0.0" //施法前摇 |
1 | "AbilityManaCost" "175 250 325" //技能魔法消耗 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | "AbilitySpecial" { "01" { "var_type" "FIELD_INTEGER" "epicenter_radius" "275 325 375 425 475 525 575 650 675 700 775 825" //每一波的震荡半径 } "02" { "var_type" "FIELD_INTEGER" "epicenter_pulses" "6 8 10" //震击次数 } "03" { "var_type" "FIELD_INTEGER" "epicenter_damage" "110 110 110" //每波伤害 } "04" { "var_type" "FIELD_INTEGER" "epicenter_slow" "-30 -30 -30" //降低移动速度 } "05" { "var_type" "FIELD_INTEGER" "epicenter_slow_as" "-30" //降低攻速 } "06" { "var_type" "FIELD_INTEGER" "epicenter_slow_duration_tooltip" "3" //减速持续时间 } "07" { "var_type" "FIELD_INTEGER" "epicenter_pulses_scepter" "6 8 10" //和epicenter_pulses对应的权重 } "08" { "var_type" "FIELD_FLOAT" "epicenter_cooldown_scepter" "120.0 110.0 100.0" //技能cd } // radius of each pulse "09" { "var_type" "FIELD_FLOAT" "epicenter_damage_interval" "0.5" //伤害间隔,每0.5秒一个伤害打击 } "10" { "var_type" "FIELD_INTEGER" "epicenter_pulse_01_radius" "275" //第一波地震的半径 } //后面省略,为1-10波的地震半径 }
|
1 2 3 4 5 6 7 | "precache" { "soundfile" "soundevents/game_sounds_heroes/game_sounds_sandking.vsndevts" "particle" "particles/generic_gameplay/generic_stunned.vpcf" "particle" "particles/units/heroes/hero_sandking/sandking_epicenter.vpcf" "particle" "particles/units/heroes/hero_sandking/sandking_epicenter_tell.vpcf" } |
注意这里定义的ModifierName 在后面有详细说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | "OnSpellStart" //技能开始时触发 { "ApplyModifier" //执行技能元素 { "ModifierName" "modifier_epicenter_precast_datadriven" "Target" "CASTER" } "FireSound" //播放音效 { "EffectName" "Ability.SandKing_Epicenter.spell" "Target" "CASTER" } } "OnChannelFinish" //技能完成时触发 { "RemoveModifier" //移除技能元素 { "ModifierName" "modifier_epicenter_precast_datadriven" "Target" "CASTER" } } "OnChannelSucceeded" //技能释放成功时触发 { "ApplyModifier" //执行技能元素 { "ModifierName" "modifier_epicenter_buff_datadriven" "Target" "CASTER" } } |
一个Modifier的方法中(例如OnCreated)可以嵌套另一个Modifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | "Modifiers" { "modifier_epicenter_precast_datadriven" { "IsPurgable" "0" //0表示不能被清除 "IsHidden" "0" //0表示不隐藏 "OnCreated" //当创建时播放一个地震特效 { "AttachEffect" { "Target" "CASTER" "EffectName" "particles/units/heroes/hero_sandking/sandking_epicenter_tell.vpcf" "EffectAttachType" "start_at_custom_origin" "ControlPointEntities" { "CASTER" "attach_tail" "CASTER" "attach_tail" "CASTER" "attach_tail" } } } } //下面还有很多modifier } |
它的结构定义像一个类,由变量和接口组成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | "modifier_epicenter_buff_datadriven" { "IsPurgable" "0" "IsHidden" "0" "Duration" "%epicenter_damage_interval * %epicenter_pulses + 0.25" "OnCreated" { //创建时逻辑 } "OnDestroy" { //销毁时 } //定时器 "ThinkInterval" "%epicenter_damage_interval * 1" "OnIntervalThink" { "ActOnTargets" //攻击多个目标 { "Target" { "Center" "CASTER" "Radius" "%epicenter_pulse_01_radius" "Teams" "DOTA_UNIT_TARGET_TEAM_ENEMY" "Types" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC" "Flags" "DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES" } "Action" { "Damage" //攻击到敌人时触发伤害 { "Damage" "%epicenter_damage" "Type" "DAMAGE_TYPE_MAGICAL" "Target" "TARGET" } "ApplyModifier" //攻击到敌人时触发buff { "ModifierName" "modifier_epicenter_debuff_datadriven" "Target" "TARGET" } } } "AttachEffect" //攻击特效 { "Target" "CASTER" "EffectName" "particles/units/heroes/hero_sandking/sandking_epicenter.vpcf" "EffectAttachType" "start_at_customorigin" "ControlPoints" { "01" "%epicenter_pulse_01_radius %epicenter_pulse_01_radius %epicenter_pulse_01_radius" } } } |
暗灭的法球效果

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | "AbilitySpecial" { "01" { "var_type" "FIELD_INTEGER" "bonus_damage" "50" //增加50点攻击力 } "02" { "var_type" "FIELD_INTEGER" "corruption_armor" "-7" //护甲削弱7 } "03" { "var_type" "FIELD_FLOAT" "corruption_duration" "15.0" //持续15秒 } } "Modifiers" { "modifier_item_desolator_datadriven" { "Passive" "1" "IsHidden" "1" "Attributes" "MODIFIER_ATTRIBUTE_MULTIPLE" "Properties" { "MODIFIER_PROPERTY_BASEATTACK_BONUSDAMAGE" "%bonus_damage" } "Orb" { "Priority" "DOTA_ORB_PRIORITY_ITEM" "ProjectileName" "particles/items_fx/desolator_projectile.vpcf" "CastAttack" "0" } "OnOrbFire" { "ApplyModifier" //击中目标后,施加一个buff { "Target" "TARGET" "ModifierName" "modifier_item_desolator_datadriven_corruption" } "FireSound" //音效 { "EffectName" "Item_Desolator.Target" "Target" "TARGET" } } } "modifier_item_desolator_datadriven_corruption" //相当于一个buff,持续15秒,降低15点护甲 { "Duration" "%corruption_duration" "Passive" "0" "IsHidden" "0" "Properties" { "MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS" "%corruption_armor" } } } |

1 2 3 4 5 6 7 8 9 10 11 | "OnSpellStart" //重点在这,可以在json中指定一个lua文件来编写逻辑 { "RunScript" { "ScriptFile" "items/item_blink.lua" "Function" "item_blink_datadriven_on_spell_start" "Target" "POINT" "MaxBlinkRange" "%max_blink_range" "BlinkRangeClamp" "%blink_range_clamp" } } |
keys里面可以获取到很多参数caster、target 、MaxBlinkRange (自定义的最大闪烁距离)、blink_range_clamp (当闪烁大于MaxBlinkRange时的移动量)
该函数的功能:
1.播放特效、音效
2.闪烁判断,当闪烁距离大于MaxBlinkRange时修正移动量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function item_blink_datadriven_on_spell_start(keys) ProjectileManager:ProjectileDodge(keys.caster) --Disjoints disjointable incoming projectiles. ParticleManager:CreateParticle( "particles/items_fx/blink_dagger_start.vpcf" , PATTACH_ABSORIGIN, keys.caster) keys.caster:EmitSound( "DOTA_Item.BlinkDagger.Activate" ) local origin_point = keys.caster:GetAbsOrigin() local target_point = keys.target_points[ 1 ] local difference_vector = target_point - origin_point if difference_vector:Length2D() > keys.MaxBlinkRange then --Clamp the target point to the BlinkRangeClamp range in the same direction. target_point = origin_point + (target_point - origin_point):Normalized() * keys.BlinkRangeClamp end keys.caster:SetAbsOrigin(target_point) FindClearSpaceForUnit(keys.caster, target_point, false) ParticleManager:CreateParticle( "particles/items_fx/blink_dagger_end.vpcf" , PATTACH_ABSORIGIN, keys.caster) end |
以上的实例解释了技能的运行机制,以及一些常用接口,如何调用lua函数
我觉得如果能配套一个技能编辑器,去生成json,会更容易调试一些
分享一个小工具SpellLibrary,里面有很多dota2的实例和资源

资源来源于dota2社区分享,是爱好者基于dota2编辑器复刻版。
https://github.com/Pizzalol/SpellLibrary/blob/master/README.md