自动技能的设计
首先新增一个技能管理类。
我们先说最简单的自动技能的逻辑,实际上就是让玩家自己将技能设置好,然后挨个释放技能。这种逻辑写起来并不复杂,只需要将所有的技能数据全部放到容器里,然后挨个释放即可。
在这个基础上,可以再增加一些逻辑,例如有一部分技能是回血技能,那么我们就去判断当前的人物血量,只有当血量到一定的阈值才进行释放。
技能表的设计
接着我们来设计一套自己的选技能的逻辑。首先设计一个技能详情的结构体
//技能使用详情
struct _stuSkillUseInfo
{
//技能名字
wstring m_name;
//技能类型
EmSkillType m_skilltype;
//权值
int m_weight;
//原始权值
int m_OriginWeight;
//需求
//自身条件 MP范围
_stuValRange m_MpRange;
//冷却时间
DWORD m_CDTime;
//.....
};
技能详情里有以下几个属性:
- 技能名字
- 技能类型:是攻击技能还是BUFF技能还是回血技能还是移动技能等等
- 权值:根据权值的大小来释放技能,权值高的优先释放,释放完成后将权值归零
- 原始权值:保存该技能最开始设置的权值
- MP范围:由于是法师职业,所以在释放的时候需要考虑到当前的MP范围。如果是近战职业,各位可以将这里换成HP范围
- 技能冷却时间
然后可以根据需求添加一些可能需要用到的值,比如说目标怪物的血量,如果说目标怪物血量太少,那么就不使用高等级技能。然后还可以加一个技能等级的字段,不过这个游戏等级影响不是很大,所以我这里就没有添加。总之这个表把你所有需要的字段全部添加上去就行。
其他两个结构体如下:
//技能类型
enum EmSkillType
{
Em_攻击技能,
Em_BUFF技能,
Em_加血技能,
};
//范围属性
struct _stuValRange
{
//最小值
DWORD MinValue;
//最大值
DWORD MaxValue;
};
以法师的第一个技能为例:
//火球术 攻击技能 HP范围(不判断) MP范围(10-100) 技能等级3 消耗魔法值5 80权值
然后将当前职业的所有技能全部按照技能详情列举出来,就完成了一张技能表的设计
//火球术 攻击技能 HP范围(不判断) MP范围(10-100) 技能等级3 消耗魔法值5 80权值
//抗拒火环 攻击技能 HP范围(不判断) MP范围(10-100) 技能等级3 消耗魔法值8 60权值
//诱惑之光 攻击技能 HP范围(不判断) MP范围(10-100) 技能等级3 消耗魔法值6 50权值
//地狱火 攻击技能 HP范围(不判断) MP范围(25-100) 技能等级3 消耗魔法值20 40权值
//雷电术 攻击技能 HP范围(不判断) MP范围(20-100) 技能等级3 消耗魔法值18 45权值
//魔法盾 BUFF技能 HP范围(不判断) MP范围(50-100) 技能等级3 消耗魔法值50 90权值
自动技能的设计原则就是首先判断技能是否满足各种需求,判断完成之后从中筛选出权值最高的。权重设计原则是加血技能>BUFF技能>攻击技能 攻击技能优先使用等级高的 技能冷却快的优于冷却慢的。
然后设置一个全局的技能表,这个表放在GlobalData.cpp
里
vector<_stuSkillUseInfo> g_skilllist =
{
{/* 技能名字*/L"火球术",/* 技能类型*/Em_攻击技能,/* 技能权重*/80,/* 原始权重*/80,/* 自身条件*/{10,1000},/*冷却时间*/3000},
{/* 技能名字*/L"抗拒火环",/* 技能类型*/Em_攻击技能,/* 技能权重*/60,/* 原始权重*/60,/* 自身条件*/{10,1000},/*冷却时间*/3000},
{/* 技能名字*/L"诱惑之光",/* 技能类型*/Em_攻击技能,/* 技能权重*/50,/* 原始权重*/50,/* 自身条件*/{10,1000},/*冷却时间*/3000},
{/* 技能名字*/L"地狱火",/* 技能类型*/Em_攻击技能,/* 技能权重*/40,/* 原始权重*/40,/* 自身条件*/{25,1000},/*冷却时间*/3000},
{/* 技能名字*/L"雷电术",/* 技能类型*/Em_攻击技能,/* 技能权重*/45,/* 原始权重*/30,/* 自身条件*/{25,1000},/*冷却时间*/3000},
{/* 技能名字*/L"魔法盾",/* 技能类型*/Em_BUFF技能,/* 技能权重*/90,/* 原始权重*/90,/* 自身条件*/{50,1000},/*冷却时间*/1000*30},
};
表里面包含所有的技能详情。由于这个游戏技能是没有冷却时间的,所以我这里全部设置为3秒。
初始化全局技能表
首先在UseSkillManager类新增一个类成员。
public:
//技能的使用数据表
vector<_stuSkillUseInfo> m_SkillList;
这个技能表实际上就是全局技能表的复制,因为后续需要修改全局技能表,所以直接复制到类成员里。
接着在UseSkillManager
类,新增一个函数
void SetSkillData(vector<_stuSkillUseInfo> skilldata);
实现如下:
void UseSkillManager::SetSkillData(vector<_stuSkillUseInfo> skilldata)
{
m_SkillList = skilldata;
}
这个函数用于给类成员的全局技能表赋值,在dll注入的时候调用。
//技能管理器
extern UseSkillManager g_SkillUseManager;
在GlobalData.h
新增一个技能管理器的类变量
//设置技能列表
g_SkillUseManager.SetSkillData(g_skilllist);
然后在窗口初始化的时候设置一下技能列表,这样g_SkillUseManager
的m_SkillList
就填充了技能详情。
获取最优技能
接着声明一个函数,用于获取最优技能
_stuObj UseSkillManager::GetTheBestUseSkill(_stuObj role, _stuObj obj, _stuObjs skilllist);
函数实现如下:
_stuObj UseSkillManager::GetTheBestUseSkill(_stuObj role, _stuObj obj, _stuObjs skilllist)
{
//判断所有权值是否已经归零
if (CheckAllWeightIsZero())
{
ReSetSkillWeight();
}
//符合条件的技能列表
vector<_stuSkillUseInfo> DataList;
//遍历技能列表 筛选出符合条件的技能
for (auto &skill : m_SkillList)
{
//通过技能名字返回技能对象
auto SkillObj = skilllist.GetDataByName(skill.m_name);
//技能名称为空 跳过
if (SkillObj.m_Name == L"")
{
continue;
}
//判断蓝量是否在设置的范围内
if (!(role.m_Obj_MP > skill.m_MpRange.MinValue&&role.m_Obj_MP < skill.m_MpRange.MaxValue))
{
continue;
}
//把符合的技能存放到表里
DataList.push_back(skill);
}
//筛选出权重最高的技能
int NowValue = 0;
_stuSkillUseInfo ChooseSkill;
for (auto skill : DataList)
{
if (skill.m_weight > NowValue)
{
//记录当前技能权重
NowValue = skill.m_weight;
//保存当前技能对象
ChooseSkill = skill;
}
}
//将当前技能权值置为0
ClearSkillWeight(ChooseSkill.m_name);
//返回权值最高的技能
return skilllist.GetDataByName(ChooseSkill.m_name);
}
代码解释如下:
- 首先判断所有技能权值是否已经全部归零,如果是则重置所有权值
- 通过设定的条件,筛选出所有符合条件的技能;这里就看各位的需求了,判断当前人物血量,判断当前人物蓝量,判断当前怪物血量等等
- 然后将筛选通过的技能进行权值排序,筛选出权值最高的技能
- 最后将权值最高的技能权值置零,下一次就不筛选该技能了,然后返回该技能对象
最后来看实际效果:
首先选择BUFF技能,然后选择低等级,CD短的技能,然后是高等级技能,最后是大招。这个排列顺序是符合我们预期的。
到这里,自动打怪自动技能的功能都已经完成了,下一篇接着来完成自动吃药功能。
Github:https://github.com/TonyChen56/GameReverseNote
完整代码:https://download.csdn.net/download/qq_38474570/79498815