本篇博客用来介绍mmorpg中的技能系统,虽说是mmorpg游戏技能系统,对比其他游戏技能系统,大同小异。本文将详细介绍技能系统的设计思路,和实现细节。
技能相关的实现,这里可以分为三个部分
SkillManager:技能管理,负责技能拥有者(主要是player,monster)的管理。主要实现技能的添加,移除,属性计算等等。
SkillDefCenter:包括技能的定义,以及技能的load.服务器通过配置文件,将所有技能的定义加载进来,就是说技能定义在服务器只有一份。在SkillManager里保存的只有技能ID。在需要GetSkillDef时,用技能ID从SkillDef获取相关信息即可。
SkillRelase:技能释放
SkillDefCenter结构图
在SkilldefCenter中,我们主要实现三个功能模块,一个是技能的定义,一个是技能的load/reload(热更),还有就是提供获取技能定义的接口。
SkillDef定义
在我们游戏开发的过程中,除了技能必要的属性外,还需要根据策划大大的脑洞,附加各种属性,这部分也是服务端开发时需要与策划大大详细沟通与交流的过程。策划决定了技能属性的行为。
技能ID:标识每个技能
技能名称:客户端用来显示名字
所需职业:用于标识所属职业,也有所有职业通用的,也就是所谓的无职业需求。一般来说通用职业的武器要么非常垃圾,要么非常牛逼。
技能等级:技能本身有等级的,可以升级。类似于通过熟练度,技能书之类的升级。
技能条件:技能所需要的人物等级。
消耗蓝量: 技能所消耗的蓝量。
作用对象: 技能所作用的对象类型。(某些技能只能作用己方,比如恢复HP,MP类型的技能)
作用人数上限: 技能最多作用人数
技能攻击比: 计算技能攻击力时,人物属性的攻击力乘以该比例,即为该技能的攻击力。类比英雄联盟中的AP加成,AD加成。
攻击距离:技能的攻击距离,类比英雄联盟中的平A,QWE之类的都有攻击距离。
技能范围形状参数:类比,塞恩,寒冰的W,是有技能形状的。(矩形的,扇形,目标圆形)。
使用方式:分为主动技能和被动技能,英雄联盟中每个英雄都有被动技能。
是否位移:类比伊泽瑞尔的E,闪现。都可算作位移。但在mmorpg中很少有过墙这一说。
冷却时间:技能CD
指向性:是否需要选中目标,就是英雄联盟中的指向性技能和非指向性技能。
技能属性加成:该技能会增加人物的某种属性。
技能触发状态:释放该技能会触发某种buff状态.
附加破防伤害:释放该技能后,额外附加的无视防御伤害。
增加战斗力:技能附加的战斗力,用于计算人物战斗力,mmorpg类都有战斗力这一说。
持续伤害时间:持续伤害持续时间
持续伤害频率:比如说持续三十秒,每五秒伤害多少巴拉巴拉
class SkillDef
{
public:
enum Sill_Use_Type
{
SKILL_USE_TYPE_ACTIVE_SKILL = 1, //主动技能
SKILL_USE_TYPE_PASSIVE_SKILL = 2, //被动技能
};
enum Skill_Trigger_Type
{
SKILL_TRIGGER_TYPE_DIRECT = 0, //直接按玩家所在位置和方向释放的技能
SKILL_TRIGGER_TYPE_NEED_OBJECT = 1, //技能释放需要先选中目标
};
enum Skill_Release_Pos_Change_TYPE
{
SKILL_POS_CHANGE_NONE = 0, //释放技能不发生位移
SKILL_POS_CHANGE_2_TARGET_POS = 1, //释放者位置要变更成目标位置,但释放者与目标间存在地图不可行走处则停止在该位置前一个位置上。
};
enum Skill_Target_Type
{
SKILL_TARGET_TYPE_SELF = 1, //对自己释放的技能。
SKILL_TARGET_TYPE_GROUP = 2, //对队伍中的队员释放(包括自己)的技能
SKILL_TARGET_TYPE_ENEMY = 3, //对敌方释放的技能
};
enum Skill_Attack_Shape_type
{
SKILL_SHAPE_TYPE_SINGLE = 1, //单体
SKILL_SHAPE_TYPE_RECTANGLE = 2, //矩形
SKILL_SHAPE_TYPE_SECTOR = 3, //扇形
SKILL_SHAPE_TYPE_CIRCULAR = 4, //圆形(与释放技能者坐标为中心坐标)
SKILL_SHAPE_TYPE_CIRCULAR_POINT = 5, //圆形(需设置释放技能中心坐标)
};
enum Skill_Type_ID
{
//战士
SKILL_TYPE_WARRIOR_ATTACK_1 = 100, //战士3连击
SKILL_TYPE_WARRIOR_ATTACK_2 = 101, //战士3连击
SKILL_TYPE_WARRIOR_ATTACK_3 = 102, //战士3连击
SKILL_TYPE_REGE_SWORD_SLASH = 103, //狂怒剑斩
SKILL_TYPE_CROSS_SLASH = 104, //十字斩
SKILL_TYPE_BRUTAL_CLASH = 105, //野蛮冲撞
SKILL_TYPE_WHIRLWIND = 106, //旋风斩
//法师
SKILL_TYPE_WIZZARD_ATTACK_1 = 300, //法师3连击
SKILL_TYPE_WIZZARD_ATTACK_2 = 301, //法师3连击
SKILL_TYPE_WIZZARD_ATTACK_3 = 302, //法师3连击
SKILL_TYPE_FIRE_BIRD = 303, //火鸟
SKILL_TYPE_FLAME_STORM = 304, //火焰风暴
SKILL_TYPE_GUARDIAN_SHIELD = 305, //守护屏障
SKILL_TYPE_FLAME_WHIRL = 306, //火焰龙卷
//弓手
SKILL_TYPE_ARCHER_ATTACK_1 = 500, //射手3连击
SKILL_TYPE_ARCHER_ATTACK_2 = 501, //射手3连击
SKILL_TYPE_ARCHER_ATTACK_3 = 502, //射手3连击
SKILL_TYPE_IMPALE_ARROW = 503, //穿刺剑
SKILL_TYPE_TREBLE_ARROW = 504, //三重箭射
SKILL_TYPE_FROST_ARROW = 505, //寒冰箭
SKILL_TYPE_HAIL_OF_ICE_ARROW = 506, //玄冰箭雨
//术士
SKILL_TYPE_SORCERER_ATTACK_1 = 700, //术士3连击
SKILL_TYPE_SORCERER_ATTACK_2 = 701, //术士3连击
SKILL_TYPE_SORCERER_ATTACK_3 = 702, //术士3连击
SKILL_TYPE_POISON_STING = 703, //毒刺
SKILL_TYPE_POSION_SPELL = 704, //猛毒咒
SKILL_TYPE_SPACETIME_RIFT = 705, //时空裂隙
SKILL_TYPE_BLOOD_MAGIC_CIRCLE = 706, //咒血魔阵
//通用
SKILL_TYPE_COMMON_SKILL_BEGIN = 1900, //通用技能
SKILL_TYPE_TUMBLE_DODGE = 1900, //翻滚躲避
SKILL_TYPE_COMMON_SKILL_END = 1999,
//被动光环类
SKILL_TYPE_PASSIVE_SKILL_START = 2000,
SKILL_TYPE_IRON_BODY = 2000, //被动钢筋铁骨
SKILL_TYPE_SUPPER_POWER = 2001, //被动蛮荒神力
SKILL_TYPE_MYSTERY_OF_FIRE = 2002, //被动火炎奥妙
SKILL_TYPE_ANGRY_OF_FIRE = 2003, //被动怒火焚心
SKILL_TYPE_SWIFTNESS = 2004, //被动疾风迅捷
SKILL_TYPE_ART_OF_WAR = 2005, //被动战争艺术
SKILL_TYPE_WITCHCRAFT_SHIELD = 2006, //被动巫蛊护体
SKILL_TYPE_WITCHCRAFT_POSION = 2007, //被动巫蛊猛毒
SKILL_TYPE_PASSIVE_SKILL_END,
//特殊类
SKILL_TYPE_SPLASH_SKILL = 9001, //溅射攻击特殊技能标识
};
static bool Is_Passive(int skill_type)
{
if (skill_type >= SKILL_TYPE_PASSIVE_SKILL_START && skill_type < SKILL_TYPE_PASSIVE_SKILL_END)
return true;
return false;
}
static bool IsNomalAttackSkill(int skill_type)
{
if (skill_type >= SKILL_TYPE_WARRIOR_ATTACK_1 && skill_type <= SKILL_TYPE_WARRIOR_ATTACK_3 ||
skill_type >= SKILL_TYPE_WIZZARD_ATTACK_1 && skill_type <= SKILL_TYPE_WIZZARD_ATTACK_3 ||
skill_type >= SKILL_TYPE_ARCHER_ATTACK_1 && skill_type <= SKILL_TYPE_ARCHER_ATTACK_3 ||
skill_type >= SKILL_TYPE_SORCERER_ATTACK_1 && skill_type <= SKILL_TYPE_SORCERER_ATTACK_3)
{
return true;
}
return false;
}
inline bool Is_Combot_Skill()
{
if (mCombotIndex > 0) return true;
return false;
}
inline bool IsSingleSkill() //单体技能?
{
if (mAttackShapeType == SKILL_SHAPE_TYPE_SINGLE) return true;
return false;
}
public:
std::string mName; //技能名称
int mSkillID; //技能表中的编号
int mSkillTypeID; //技能ID
int mJob; //技能需要职业,如果设置成0表示为全职业可用
int mCombotIndex; //连击编号
int mCombotRetsetGap; //连击重置时间
int mSkillLevel; //技能等级
int mMaxExp; //技能升级需要的经验值
int mUpgradeNeedLevel; //技能升级需要人物达到的等级
int mMpCost; //MP消费
int mSkillUseType; //技能使用类型
int mPosChangeType; //释放者位置变更类型
int mCoolDown; //技能CD毫秒级
int mSkillPriority; //技能优先级。(服务端:用于怪物技能使用判断,客户端:用于挂机使用技能判断)
int mTriggerType; //技能释放需要类型
int mTargetType; //技能目标类型
int mMaxTarget; //技能作用人数上限
int mDmgShowDelay; //伤害显示延迟
int mAttackParam; //技能攻击比
int mAttackDirectPower; //技能直接伤害,无视防御
int mAttackDuration; //技能持续时间(毫秒级)
int mAttackGap; //技能伤害间隔(毫秒级)
int mAttackDistance; //技能攻击距离
int mAttackShapeType; //技能攻击范围形状类型
int mAttackShapeParam1; //形状参数1
int mAttackShapeParam2; //形状参数2
int mCombatEffectiveness; //技能附加属性战斗力
std::vector mAttackShapeParam3; //形状参数3
std::vector mTriggerBuffer; //触发buff规则ID,规则定义在buff触发表中
CalculateAttrib mSkillAttrib; //属性增加值
ProCalculateAttribPercent mSkillAttribPercent; //属性百分比增加
std::vector mSkillPacketAttDefs; //特效属性
};
class SkillDefCenter
{
public:
typedef std::unordered_map SkillDefineMap;
typedef std::unordered_map SkillDefineIDMap;
//这里是两种map,一种是通过技能ID,一种是通过 技能ID和技能等级转换后的在结构体中组成的Key 这里是不是有点不好理解哈哈哈
private:
SkillDefineMap mSkillDefineMap;
SkillDefineIDMap mSkillDefineIDMap;
public:
bool LoadSkillDefine(GameServerLib* lib); //load 技能定义配置表
bool ReLoadSkillDefine(GameServerLib* lib); //重新load,热更用
void Dest();
SkillDef * GetSkillDefine(SkillKeyTransorm & skill_key); //通过转换后的key获取技能定义
SkillDef * GetSkillDefine(int iSkillID); //通过技能ID获取技能定义
};
load过程中大同小异,再另一篇博客中对loadfile(加载配置文件)会有详细介绍。
SkillManager里主要负责管理mapobject(玩家,怪物之类的object)的技能,从结构图已然可以了解大体功能主要有 添加技能,移除技能,增加技能经验,计算技能属性,技能升级,技能释放时间,获取技能等等,还有些细节的地方是根据不同的需求实现即可。