界面代码
首先新增一个复选框,绑定控件变量和按钮事件,然后在按钮事件添加如下代码
//自动打怪
void MainWnd::OnBnClickedCheck1()
{
static HANDLE hThread = 0;
if (m_Auto_Attack.GetCheck())
{
hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)AutoAttackProc, NULL, 0, NULL);
OutputDebugStringA("开启自动打怪");
}
else
{
TerminateThread(hThread, -1);
CloseHandle(hThread);
hThread = 0;
OutputDebugStringA("关闭自动打怪");
}
}
如果复选框被选中,就启动线程开始自动打怪,否则强行结束线程。
接着新建一个类,命名为WorkThread
,在这里实现我们的自动打怪的逻辑。
流程设计
自动打怪的流程设计如下:
- 首先获取到角色的数据
- 获取到周围对象的数据
- 根据周围对象和角色的数据取到最近的怪物
- 释放技能打怪
- 判断怪物血量,如果不为零,则继续打怪
- 如果血量为零则跳转到第一步
在整个自动打怪的流程中,我们已经完成了大部分的步骤,就差第三步,取最近怪物的数据。
取最近怪物
_stuObj GetNearestAroundObj(_stuObj RoleData, _stuObjs AroundList);
声明一个函数,取最近的怪物,传入角色对象和怪物对象集合。想要实现这个功能,有三个步骤。
首先第一步,筛选出周围对象的类型,因为我们获取到的对象是包含NPC,怪物,玩家等各种对象类型的。所以需要写一个函数来筛选出所有的对象,代码如下:
//筛选周围对象的类型
_stuObjs FilterAroundObj(_stuObjs& list, DWORD type)
{
_stuObjs newList;
_stuObj tmp;
//遍历周围对象的容器
for (auto it = begin(list.m_data); it != end(list.m_data); it++)
{
if (it->m_Obj_HP&&it->m_ObjType == type)
{
tmp = *it;
newList.m_data.push_back(tmp);
}
}
return newList;
}
第二步,计算每个对象和人物之间的距离,代码如下:
void CalcAroundDistance(_stuObjs& list, _stuObj self)
{
for (auto it = begin(list.m_data); it != end(list.m_data); it++)
{
//勾股定理求距离
float a = it->m_Obj_Pos.x - self.m_Obj_Pos.x;
float b = it->m_Obj_Pos.y - self.m_Obj_Pos.y;
it->m_ObjDistance = sqrt(a*a + b * b);
}
}
这里需要给_stuObj新增一个对象距离的属性
float m_ObjDistance; //对象距离
第三步,通过排序算法取出最近的怪物,代码如下:
_stuObj SortNearestMonster(_stuObjs& list)
{
_stuObj tmp;
//遍历周围对象的容器
for (auto it = begin(list.m_data); it != end(list.m_data); it++)
{
for (auto it = begin(list.m_data); it != end(list.m_data) - 1; it++)
{
if (it->m_ObjDistance > (it + 1)->m_ObjDistance)
{
tmp = *(it + 1);
*(it + 1) = *it;
*it = tmp;
}
}
}
//返回第一个对象
return list.m_data.front();
}
完成了这三个函数,就完成了取最近怪物的功能了,GetNearestAroundObj完整代码如下:
_stuObj GetNearestAroundObj(_stuObj RoleData, _stuObjs AroundList)
{
_stuObj nearobj;
//筛选出所有的怪物
_stuObjs listdata2 = FilterAroundObj(AroundList, 0x5);
//如果size大于0 说明周围有怪物对象
if (listdata2.m_data.size())
{
//计算每个对象和人物之间的距离
CalcAroundDistance(listdata2, RoleData);
//冒泡排序取最近怪物
nearobj = SortNearestMonster(listdata2);
}
return nearobj;
}
实际效果如图:
自动打怪
完成了取最近怪物的功能,就相当于完成了一个自动打怪的功能,代码如下:
DWORD WINAPI AutoAttackProc()
{
_Start:
//取角色数据
auto RoleData = GetRoleData();
//取周围对象数据
auto AroundData = GetAroundData();
//取最近的怪物
_stuObj nearobj = GetNearestAroundObj(RoleData, AroundData);
//如果坐标和血量都为0 说明没取到最近的怪物
if (nearobj.m_Obj_Pos.x==0&&nearobj.m_Obj_HP==0)
{
return 0;
}
//获取技能数据
auto SkillData = GetSkillData();
//获取技能对象
auto skill = SkillData.GetDataByName(L"基础射击");
//释放技能
Fn_UseSkill(nearobj, skill);
//循环打怪
while (true)
{
//打完了以后更新一下血量
UpdateAroundObj(nearobj);
//如果怪物血量大于0 继续打怪
if (nearobj.m_Obj_HP)
{
Fn_UseSkill(nearobj, skill);
}
else
{
//否则 换下一个怪物
goto _Start;
}
//这个休眠是给技能间隔的时间
Sleep(300);
}
return 0;
}
其中更新血量的函数就是重新读取一下目标怪物的HP
//更新血量(再次读取HP)
void UpdateAroundObj(_stuObj& obj)
{
//读取血量
obj.m_Obj_HP = ReadDword(obj.m_Obj + 0x65C);
obj.m_Obj_HP = ReadDword(obj.m_Obj_HP + 0x1AC);
//读取坐标
obj.m_Obj_Pos.x = ReadFloat(obj.m_Obj + 0x80);
obj.m_Obj_Pos.y = ReadFloat(obj.m_Obj + 0x84);
obj.m_Obj_Pos.z = ReadFloat(obj.m_Obj + 0x88);
}
到这里,我们就完成了整个自动打怪的功能了,但是这里有个问题,每次攻击怪物的时候都释放同一个技能。如果想要做到更加完善的话就需要一套更加强大的自动筛选技能的逻辑。
那么下一篇文章我们来讲自动技能的设计和实现。
Github:https://github.com/TonyChen56/GameReverseNote
完整代码:https://download.csdn.net/download/qq_38474570/79498815