设计MOBA技能系统(分析篇)

发表于2016-11-20
评论1 8k浏览

该系统设计分三篇:

理论篇:解释技能实现机制,我是链接
分析篇:基于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"
    }
}
Modifiers,技能元素说明
一个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
}
再单个分析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"
    }
}

看看引用的lua函数
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






如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引