锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务,锦州广厦维修电脑,公司IT外包服务
topFlag1 设为首页
topFlag3 收藏本站
 
maojin003 首 页 公司介绍 服务项目 服务报价 维修流程 IT外包服务 服务器维护 技术文章 常见故障
锦州市广厦电脑维修|上门维修电脑|上门做系统|0416-3905144热诚服务技术文章
FPS游戏(UE4,U3D引擎)方框绘制,骨骼透视,BT功能的原理 和反外挂策略

作者: 佚名  日期:2023-07-19 13:23:07   来源: 本站整理

 朝向值  和角度差的计算过程,根据上面已经得到的朝向数据
我们编写如下代码,为了让理解更简单  这里我分成了4个象限来讲解

[C++] 纯文本查看 复制代码
 
void 周围对象::计算朝向_Cs(坐标结构_3 目标, 朝向结构_2& 角度, 朝向结构_2& 角度差)
{
        FLOAT FOV_x = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe") + 0x195fe58);
        FLOAT FOV_y = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe") + 0x195fe5C);
        FLOAT FOV_z = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe") + 0x195fe60);
        FLOAT 水平朝向 = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe") + 0x19E10C8);
        FLOAT 高低朝向 = *(FLOAT*)((DWORD)GetModuleHandleA("hl.exe") + 0x19E10C4);
         
        if (目标.x > FOV_x && 目标.y >= FOV_y)//第一象限
        {
                角度.水平朝向 = (FLOAT)((double)atan2(目标.y - FOV_y, 目标.x - FOV_x) * 180 / 3.1415);
        }
        if (目标.x <= FOV_x && 目标.y > FOV_y)//第二象限
        {
                角度.水平朝向 = 180 - (FLOAT)((double)atan2(目标.y - FOV_y, FOV_x - 目标.x) * 180 / 3.1415);
        }
        if (目标.x < FOV_x && 目标.y <= FOV_y)//第三象限
        {
                角度.水平朝向 = 180 + (FLOAT)((double)atan2(FOV_y - 目标.y, FOV_x - 目标.x) * 180 / 3.1415);
        }
        if (目标.x >= FOV_x && 目标.y < FOV_y)//第四象限
        {
                角度.水平朝向 = 360 - (FLOAT)((double)atan2(FOV_y - 目标.y, 目标.x - FOV_x) * 180 / 3.1415);
        }
        FLOAT 平面距离 = sqrt((目标.x - FOV_x) * (目标.x - FOV_x) + (FOV_y - 目标.y) * (FOV_y - 目标.y));
        if (目标.z > FOV_z)//上方
        {
                角度.高低朝向 = (FLOAT)(-(double)atan2(目标.z - FOV_z, 平面距离) * 180 / 3.1415);
        }
        if (目标.z < FOV_z)//下方
        {
                角度.高低朝向  = (FLOAT)((double)atan2(FOV_z - 目标.z, 平面距离) * 180 / 3.1415);
        }
 
 
        角度差.水平朝向 = 水平朝向 - 角度.水平朝向;
        if (角度差.水平朝向 <= -180)//跨0轴的两种情况
        {
                角度差.水平朝向 += 360;
        }
        if (角度差.水平朝向 >= 180)
        {
                角度差.水平朝向 -= 360;
        }
 
        角度差.高低朝向 = 角度.高低朝向 - 高低朝向;
}
 
 

绘制类
万事俱备,我们来写个绘制类
数据全部完毕我们开始写代码绘制了,这部分是固定的代码,很简单
完全中文编程,代码已经很清晰了
大家跟着我敲写一遍即可
当然第一种 是最粗糙的, 由奢入简易  由俭入奢易难 我们先来粗糙的

[C++] 纯文本查看 复制代码
 
struct 坐标结构_2
{
    float x, y;
};
struct 坐标结构_3
{
    float x, y, z;
};
struct 坐标结构_4
{
    float x, y, z, w;
};
 
class 绘制
{
public:
    HWND m_窗口句柄;
    RECT m_窗口;
    int m_分辨率宽;
    int m_分辨率高;
    RECT m_外窗口;
    int m_外窗口宽;
    int m_外窗口高;
    float m_矩阵[16];
    DWORD m_矩阵地址;
public:
    绘制(HWND 窗口句柄, DWORD 矩阵地址);
    绘制()
    {
    }
private:
    void 取窗口信息();
public:
    void 绘制矩形(HDC HDC句柄, HBRUSH 画刷句柄, int x, int y, int w, int h);
    void 画框(HDC HDC句柄, HBRUSH 画刷句柄, int x, int y, int w, int h, int 厚度);
    void 画线(HDC HDC句柄, int X, int Y);
    void 绘制字符串(HDC HDC句柄, int x, int y, COLORREF color,  const char* text);
    //还没讲矩阵绘制  这个函数请忽略
    //bool 世界坐标转屏幕坐标(坐标结构_3 游戏坐标, 坐标结构_2& 屏幕坐标);
    bool 世界坐标转屏幕坐标_非矩阵(坐标结构_2& 屏幕坐标, FLOAT  水平角度差, FLOAT 高低角度差);//传递真实角度
};
 
 
绘制::绘制(HWND 窗口句柄, DWORD 矩阵地址)
{
    m_窗口句柄 = 窗口句柄;
    m_矩阵地址 = 矩阵地址;
    memcpy(&m_矩阵, (PBYTE*)(m_矩阵地址), sizeof(m_矩阵));
    取窗口信息();
}
 
void 绘制::取窗口信息()
{
    GetClientRect(m_窗口句柄, &m_窗口);
    m_分辨率宽 = m_窗口.right - m_窗口.left;
    m_分辨率高 = m_窗口.bottom - m_窗口.top;
    GetWindowRect(m_窗口句柄, &m_外窗口);  //含有边框及全屏幕坐标
    m_外窗口宽 = m_外窗口.right - m_外窗口.left;
    m_外窗口高 = m_外窗口.bottom - m_外窗口.top;
}
 
void 绘制::绘制矩形(HDC HDC句柄, HBRUSH 画刷句柄, int x, int y, int w, int h)
{
    RECT 矩形 = { x,y,x + w,y + h };
    FillRect(HDC句柄, &矩形, 画刷句柄);//绘制矩形
}
 
void 绘制::画框(HDC HDC句柄, HBRUSH 画刷句柄, int x, int y, int w, int h, int 厚度)
{
    绘制矩形(HDC句柄, 画刷句柄, x, y, w, 厚度);//顶边
    绘制矩形(HDC句柄, 画刷句柄, x, y + 厚度, 厚度, h - 厚度);//左边
    绘制矩形(HDC句柄, 画刷句柄, (x + w) - 厚度, y + 厚度, 厚度, h - 厚度);//右边
    绘制矩形(HDC句柄, 画刷句柄, x + 厚度, y + h - 厚度, w - 厚度 * 2, 厚度);//底边
}
 
void 绘制::画线(HDC HDC句柄, int X, int Y)
{
    取窗口信息();
    MoveToEx(HDC句柄, m_分辨率宽 / 2, m_分辨率高, NULL);
    LineTo(HDC句柄, X, Y);
}
 
HFONT 字体属性;
void 绘制::绘制字符串(HDC HDC句柄, int x, int y, COLORREF color, const char* text)
{
    SetTextAlign(HDC句柄, TA_CENTER | TA_NOUPDATECP);
    SetBkColor(HDC句柄, RGB(0, 0, 0));
    SetBkMode(HDC句柄, TRANSPARENT);
    SetTextColor(HDC句柄, color);
    SelectObject(HDC句柄, 字体属性);
    TextOutA(HDC句柄, x, y, text, strlen(text));
    DeleteObject(字体属性);
}
 
//还没讲矩阵绘制  这个函数请忽略
/*bool 绘制::世界坐标转屏幕坐标(坐标结构_3 游戏坐标, 坐标结构_2& 屏幕坐标)
{
    取窗口信息();
    memcpy(&m_矩阵, (PBYTE*)(m_矩阵地址), sizeof(m_矩阵));
    坐标结构_4 裁剪坐标;
    裁剪坐标.x = 游戏坐标.x * m_矩阵[0] + 游戏坐标.y * m_矩阵[4] + 游戏坐标.z * m_矩阵[8] + m_矩阵[12];
    裁剪坐标.y = 游戏坐标.x * m_矩阵[1] + 游戏坐标.y * m_矩阵[5] + 游戏坐标.z * m_矩阵[9] + m_矩阵[13];
    裁剪坐标.z = 游戏坐标.x * m_矩阵[2] + 游戏坐标.y * m_矩阵[6] + 游戏坐标.z * m_矩阵[10] + m_矩阵[14];
    裁剪坐标.w = 游戏坐标.x * m_矩阵[3] + 游戏坐标.y * m_矩阵[7] + 游戏坐标.z * m_矩阵[11] + m_矩阵[15];
 
    if (裁剪坐标.w < 0.0f)
        return false;
 
    坐标结构_3 NDC;
    NDC.x = 裁剪坐标.x / 裁剪坐标.w;
    NDC.y = 裁剪坐标.y / 裁剪坐标.w;
    NDC.z = 裁剪坐标.z / 裁剪坐标.w;
 
    屏幕坐标.x = (m_分辨率宽 / 2 * NDC.x) + m_分辨率宽 / 2;
    屏幕坐标.y = -(m_分辨率高 / 2 * NDC.y) + m_分辨率高 / 2;
    return true;
}*/
 
bool 绘制::世界坐标转屏幕坐标_非矩阵(坐标结构_2& 屏幕坐标, FLOAT  水平角度差, FLOAT 高低角度差)
{
            
    取窗口信息();
    FLOAT 高低可视角度 = (FLOAT)((double)atan2(m_分辨率高, m_分辨率宽)*180/3.1415);
    if (fabs(水平角度差) > 45 || fabs(高低角度差) > 高低可视角度)
    {
        return false;// 不在屏幕范围内
    
 
    int 水平差 = (int)(tan(水平角度差 * 3.1416 / 180) * ((m_分辨率宽) / 2));
    屏幕坐标.x = (float)(m_分辨率宽 / 2 + 水平差);
    // 哪里不懂可以v  hdlw312
    int 高度差 = (int)(tan(高低角度差 * 3.1416 / 180) * ((m_分辨率宽) / 2));
    屏幕坐标.y = (float)(m_分辨率高 / 2 + 高度差);
 
    return true;
}
 

到这里  非矩阵绘制的所有准备工作都已经完毕了    ,就看你要怎么调用了


非矩阵绘制方框
编写循环绘制代码

通过上面的编写 我们终于可以调用观看效果了

[C++] 纯文本查看 复制代码
 
HBRUSH 画刷句柄;
HDC HDC句柄;
COLORREF 文本颜色_亮红 = RGB(255, 0, 0);
COLORREF 文本颜色_红 = RGB(128, 0, 0);
COLORREF 文本颜色_黄 = RGB(200, 200, 0);
COLORREF 文本颜色_黑 = RGB(0, 0, 0);
COLORREF 文本颜色_白 = RGB(255, 255, 255);
COLORREF 文本颜色_常用 = RGB(158, 255, 0);
坐标结构_2 屏幕坐标_j;
坐标结构_2 屏幕坐标_H;
周围对象  周围;
 
int mainThread()
{
 
 绘制 FPS_绘制(FindWindow(L"Valve001", 0), (DWORD)GetModuleHandleA("hl.exe") + 0x1820100);
 
 
    while (true)
    {
        HDC句柄 = GetDC(FPS_绘制.m_窗口句柄);
        周围.刷新周围数据_Cs();
        for (int i = 0; i < (int)周围.对象数量; i++)
        {
            if (周围.对象列表[i].死亡标志位 == 1)
            {
                continue;
            }
            if (FPS_绘制.世界坐标转屏幕坐标_非矩阵(屏幕坐标_j,
周围.对象列表[i].角度差_j.水平朝向, 周围.对象列表[i].角度差_j.高低朝向))
            {
                if (FPS_绘制.世界坐标转屏幕坐标_非矩阵(屏幕坐标_H,
周围.对象列表[i].角度差_H.水平朝向, 周围.对象列表[i].角度差_H.高低朝向))
                {
                    if (周围.对象列表[i].阵营 == 2)
                    {
                        画刷句柄 = CreateSolidBrush(文本颜色_常用);
                    }
                    else
                    {
                        画刷句柄 = CreateSolidBrush(文本颜色_亮红);
                    }
 
                    float 高度 = 屏幕坐标_j.y - 屏幕坐标_H.y;
                    float 宽度 = 高度 / 2;                      
                    FPS_绘制.画框(HDC句柄, 画刷句柄, (int)(屏幕坐标_H.x - 宽度/2),
(int)屏幕坐标_H.y-2, (int)宽度, (int)(高度+4), 1);
                    DeleteObject(画刷句柄);
 
                    char healthChar[255];
                    sprintf_s(healthChar, sizeof(healthChar), "%d", 周围.对象列表[i].Hp);
                    FPS_绘制.绘制字符串(HDC句柄, (int)屏幕坐标_j.x, (int)屏幕坐标_j.y,
文本颜色_常用, healthChar);
                    if (GetKeyState(VK_F2) & 1)
                    {
                        FPS_绘制.画线(HDC句柄, (int)屏幕坐标_j.x, (int)屏幕坐标_j.y);
                    }
                }
            }
        }
        Sleep(1);
        DeleteObject(HDC句柄);
    }
}
 
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        DisableThreadLibraryCalls(hModule);
        CreateThread(0, 0, (LPTHREAD_START_ROUTINE)mainThread, 0, 0, 0);
 
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
 

if (GetKeyState(VK_F2) & 1)
这里面学习一个快捷键指令 GetKeyState
每次按下切换他的状态   大于0 或则小于0
从而可以实现开关的功能

 

微调
发现效果是有了,但是并不精准
优点是:不需要矩阵 ,完全的数学公式可以计算出来,而且公式是固定的,
缺点:
但是不精准
有可视角的关系,运算的误差等等导致的,算出来的误差虽然不是特别大  
而且也可以通过调整的方式让其精确,但是不够完美.

我们可以选择微调可视角度,微调屏幕X,Y坐标等等方式来处理
反正这种方法想要完全的精密是不太可能的
但是达到基本效果是没问题的,虽然达到精密没有那么的重要,但是完美主义者
是一定会使用矩阵来计算的


那么我们继续学习矩阵,用矩阵的方式  绘制方框

 


绘制的几种方法和反外挂建议
我们非矩阵的方式绘制 是没有那么的精确的
在学习矩阵绘制之前,我们先来了解下绘制的几种方法



第一种 hook  d3d/opengl 
优点:不闪 ,代码简单
缺点:非常容易被检测
第二种 窗口上自行绘制,但是会闪
优缺点适中
第三种 自建透明窗口,覆盖游戏窗口,透明窗口上绘制
优点:稳定
确定:代码复杂,会闪
反外挂:无非就是针对外挂使用的函数进行检测
第四种,分屏透视,一个窗口正常游戏,另外一个窗口显示透视信息,主播一般用的
不要说很少一部分,我对陪玩代练主播等了解较多,大部分都是这样.
反外挂思路,同样是针对实现原理,很轻松的应对
只是后2种 属于外部绘制,检测比较困难
下面我们对几种方法一一了解



深入学习矩阵 (总结所有游戏矩阵特点 找到矩阵)
对象的世界坐标列向量
x
y
z
w(w为了兼容4*4矩阵以及可以NDC坐标转化而设计存在的,大家可以暂且不管)
可以通过被一个游戏矩阵(以后我们就叫他游戏矩阵吧)  乘  从而获得剪辑坐标,也可以叫裁剪坐标,都是翻译而来(暂且当成屏幕坐标也没问题,因为他到屏幕坐标转换及其简单)
在学习这个矩阵之前呢我们先来了解
行主序和列主序



行主序就是拿4*4矩阵的行来* 后面的矩阵,我们前面的例子  都是这样
a1  a2  a3  a4      *     x    =    a1 *x+ a2*y+ a3*z + a4*w
b1  b2  b3   b4            y          b1 *x+ b2*y+ b3*z + b4*w
c1   c2   c3   c4           z          c1 *x+ c2*y+ c3*z + c4*w
d1  d2   d3   d4            w        d1 *x+ d2*y+ d3*z + d4*w

而列主序就是 拿4*4 矩阵的列 来* 后面的矩阵,这个前面没有出现过 
a1  b1  c1  d1     *     x    =    a1 *x+ a2*y+ a3*z + a4*w
a2  b2  c2   d2           y          b1 *x+ b2*y+ b3*z + b4*w
a3   b3  c3  d3           z          c1 *x+ c2*y+ c3*z + c4*w
a4  b4  c4   d4            w        d1 *x+ d2*y+ d3*z + d4*w


那么我们现在了解下这个游戏矩阵都能对我们干嘛,给他拆分成几个功能
最后他是这几个功能的结合体而已



缩放位移矩阵


如果是行主序矩阵的话就是
 
(1不是固定的  应该写成Tw   )
如果是列主序矩阵的话就是
Sx  0    0    0
0    Sy  0    0
0    0    Sz  0
Tx  Ty  Tz   Tw


x
y
z
w
等于
x*Sx+w*Tx
y*Sy+w*Ty
z*Sz+w*Tz
w*Tw
对矩阵   x  y  z  w  进行了  缩放和位移
所以我们在这里知道   矩阵移动影响的主要是
Tx  Ty  Tz  Tw


结论一: 我们在走路做位移的情况下,行主序最后一列  列主序最后一行 Tx  Ty  Tz  Tw 会改变



当然不一定只有走路的时候会变
原因很简单,这个矩阵是各种操作的结合体如以下例子:只改变Y的情况下
 
只改变X的情况下

 
只改变Z的情况下
 
XYZ混合改变的情况下
 

旋转矩阵
简单证明下结论
 
假设旋转45度
 
用极限验证法,验证结果正确 
 
围绕Z轴 转动
 
结论二:只转动水平朝向的时候  行主序第三列不变 ,列主序第三行 不变化
列主序如下

 

围绕X 轴转动
 
结论三:只转动高低朝向的时候  行主序第一行不变 ,列主序第一列 不变化
列主序如下

 
围绕Y 轴转动  我们没有这种情况的时候
 
开倍镜
 
总结矩阵6条结论
第一结论:   行主序最后一列   列主序最后一行 走路 跳的状态会改变,不代表别的动作不改变
第二结论:   水平转动的情况  行主序第三列不变   列主序的话第三行不变
第三结论:   高低朝向改变的时候   行主序第一行不变   列主序的第一列不变
第四结论:   矩阵第一个值  -1 到1 的, 这个绝对吗  不绝对!
第五结论:   行主序 第一个行第3个元素 是固定的0    列主序 第一列的第三个元素是0  不绝对!
第六结论:   我们开倍镜 第一个值  会* 相应的倍数  不绝对!


 
通过找到的矩阵可以进一步完善结论

通过以上的规律  CE 搜到如下矩阵地址
矩阵地址
hl.exe+1820100
这个过程文字很啰嗦,只能视频里见了,无非根据以上结论 扫描而已
如果这个都扫不到,说明CE基础薄弱,需要加强练习


世界坐标 ->剪辑坐标->NDC坐标->屏幕坐标
那么我们来了解下坐标的转换过程
通过以上的矩阵
世界坐标  ----> 剪辑坐标

矩阵乘法
a0    a1     a2     a 3

a4    a5    a6     a7
a8     a9    a10   a11
a12   a13  a14    a15
*
x
y
z
w

得到:
剪辑坐标 x  = a0*x +a4*y + a8*z + a12*w
剪辑坐标 y  = a1*x +a5*y + a9*z + a13*w
剪辑坐标 z  = a2*x +a6*y + a10*z + a14*w
剪辑坐标 w  = a3*x +a7*y + a11*z + a15*w



世界坐标按照我们之前的矩阵法则转换成了平面的剪辑坐标
但是剪辑坐标是和分辨率没有直接关系的 ,需要我们进一步转换
剪辑坐标坐标系如下  正中心为0,0
 


剪辑坐标---->NDC坐标
矩阵的设计中w 是可以让剪辑坐标范围到-1和1的  也就成了NDC坐标
所以NDC坐标很好理解,就是 -1到1的平面坐标系     中心点为0,0
NDC .x =  剪辑坐标 x/剪辑坐标 w
NDC.y  =剪辑坐标y/剪辑坐标 w
NDC.z  =剪辑坐标z/剪辑坐标 w


DNC坐标---->屏幕坐标
有了DNC坐标我们可以很容易转换成屏幕坐标了
当然屏幕坐标不止一种形式
我们现在只以CS为例
后面会有其他类型


 
DNC.x / 1   =  屏幕坐标差.x  /    分辨率_宽 /2
屏幕坐标差.x   =  (分辨率_宽 /2) * DNC.x   
屏幕坐标.x  =  (分辨率_宽 /2) * DNC.x    +  分辨率_宽/2


DNC.y / 1   =  屏幕坐标差.y  /    分辨率_高 /2
屏幕坐标差.y =   -(分辨率_高 /2)*DNC.y
屏幕坐标.y=   -(分辨率_高 /2)*DNC.y + 分辨率_高 /2


矩阵绘制
最终代码如下
再用之前的代码   只把世界坐标转屏幕坐标_非矩阵() 改成世界坐标转屏幕坐标()  就可以实现功能了
其实也是我们之前的代码设计的比较合理
这样 只要替换一句命令,其他指令完全不影响


[C++] 纯文本查看 复制代码
bool 绘制::世界坐标转屏幕坐标(坐标结构_3 游戏坐标, 坐标结构_2& 屏幕坐标)
{
    取窗口信息();
    memcpy(&m_矩阵, (PBYTE*)(m_矩阵地址), sizeof(m_矩阵));
    坐标结构_4 裁剪坐标;
    裁剪坐标.x = 游戏坐标.x * m_矩阵[0] + 游戏坐标.y * m_矩阵[4] + 游戏坐标.z * m_矩阵[8] + m_矩阵[12];
    裁剪坐标.y = 游戏坐标.x * m_矩阵[1] + 游戏坐标.y * m_矩阵[5] + 游戏坐标.z * m_矩阵[9] + m_矩阵[13];
    裁剪坐标.z = 游戏坐标.x * m_矩阵[2] + 游戏坐标.y * m_矩阵[6] + 游戏坐标.z * m_矩阵[10] + m_矩阵[14];
    裁剪坐标.w = 游戏坐标.x * m_矩阵[3] + 游戏坐标.y * m_矩阵[7] + 游戏坐标.z * m_矩阵[11] + m_矩阵[15];
 
    if (裁剪坐标.w < 0.0f)
        return false;
 
    坐标结构_3 NDC;
    NDC.x = 裁剪坐标.x / 裁剪坐标.w;
    NDC.y = 裁剪坐标.y / 裁剪坐标.w;
    NDC.z = 裁剪坐标.z / 裁剪坐标.w;
 
    屏幕坐标.x = (m_分辨率宽 / 2 * NDC.x) + m_分辨率宽 / 2;
    屏幕坐标.y = -(m_分辨率高 / 2 * NDC.y) + m_分辨率高 / 2;
    return true;
}
 
 


这次不用微调就已经 精确了吧?
所以证明矩阵确实算的比较准确而且帮我们省掉了大量计算的时间

到这里,你的基础算过了
迎接后面真正的学习吧!



单机口袋西游和单机突袭进一步熟悉相关数据,矩阵,以及绘制

 

口袋西游:
用到的数据很简单
 这是一款普通的RPG类游戏,不设计到射击相关的功能,我们只是拿来做画框绘制练习。
人物数据:
[[[单机版基地址]+1C]+28]+3C X
[[[单机版基地址]+1C]+28]+40 Y
[[[单机版基地址]+1C]+28]+44 Z
[[[单机版基地址]+1C]+28]+560 X中心点
[[[单机版基地址]+1C]+28]+564 Y中心店
[[[单机版基地址]+1C]+28]+568 Z中心点
[[[单机版基地址]+1C]+28]+56C X中心点到对顶点的X差
[[[单机版基地址]+1C]+28]+570 Y中心点到对顶点的Y差
[[[单机版基地址]+1C]+28]+574 Z中心点到对顶点的Z差
[[[单机版基地址]+1C]+28]+578 立方体下顶点X坐标
[[[单机版基地址]+1C]+28]+57C 立方体下顶点Y坐标
[[[单机版基地址]+1C]+28]+580 立方体下顶点Z坐标
[[[单机版基地址]+1C]+28]+584 立方体上顶点X坐标
[[[单机版基地址]+1C]+28]+588 立方体上顶点Y坐标
[[[单机版基地址]+1C]+28]+58C 立方体上顶点Z坐标

周围遍历
[[[[[单机版基地址]+1C]+8]+20]+5C]  对象数量
[[[[[[[单机版基地址]+1C]+8]+20]+58]+N*4]+4]+138 周围对象血量(未被攻击过是0)
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+3C  周围对象X
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+40  周围对象Z
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+44  周围对象Y
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+ED  死亡标志1为死亡BYTE
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2C4 X中心点
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2C8 Z中心店
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2CC Y中心点
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2D0 X中心点到对顶点的X差
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2D4 Y中心点到对顶点的Y差
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2D8 Z中心点到对顶点的Z差
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2DC 立方体下顶点X坐标
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2E0 立方体下顶点Y坐标
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2E4 立方体下顶点Z坐标
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2E8 立方体上顶点X坐标
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2EC 立方体上顶点Y坐标
[[[[[[[单机版基地址]+1C]+8]+20]+1C]+N*4]+4]+2F0 立方体上顶点Z坐标

突袭:
这是一款类似于CS的单机游戏,在设计上相对于CS更加简单一些,数据也比较简单,适合新手练习。
这款游戏也可以联网,有一些数据需要在联网状态下才能精确的分析出来,在新课程中也做了详细的讲解。

人物数据:
[0x00587C0C]+28   X   
[0x00587C0C]+2C  Y
[0x00587C0C]+30   Z
[0x00587C0C]+34   水平朝向顺时针递增360  
[0x00587C0C]+38   竖直朝向-90到90   从下到上
[0x00587C0C]+EC    角色血量

周围遍历
00587C18     玩家数量
[[00587C10]+n*4]+4   X_head
[[00587C10]+n*4]+8   Y_head
[[00587C10]+n*4]+C   Z_head
[[00587C10]+n*4]+28   X   
[[00587C10]+n*4]+2C  Y
[[00587C10]+n*4]+30   Z
[[00587C10]+n*4]+50   身高
[[00587C10]+n*4]+34   水平朝向顺时针递增360  
[[00587C10]+n*4]+38   竖直朝向-90到90   从下到上
[[00587C10]+n*4]+EC    角色血量
在周围遍历中我们发现多了一个头部坐标,这也是其相对于CS设计的更加完善的地方。



游戏不分类型
RPG也好,回合制也好,FPS也好。
其实只是一个操作的区别而已,本质没有任何区别。
FPS的自瞄相当于Rpg的朝向怪物。
FPS的绘制相当于RPG的计算碰撞体  以方便躲避 或则自写寻路等等。
通过已有的数据,我们可以实现以下效果。

 
看到RPG游戏这样意外吗? 
因为游戏很古老,引擎也很古老,所以能够得到资源只允许我们做一个3D的立方体模型,
但是不管如何,RPG和FPS的本质是一样的,没有任何区别,只是我们研究的角度不同,要达到的效果也不同。



矩阵
下面我们来看看这两款游戏的矩阵数据
口袋西游矩阵  [D2E5D0]+E8



 
突袭矩阵   57AFE0

 
这都是标准的4*4矩阵,具体的分析方法在上面我们已经提到了,下面我们来看看封装数据的过程。

数据封装

[C++] 纯文本查看 复制代码
 
DWORD 口袋西游_周围基地址 = 0xD0DF1C;
void 周围对象::刷新周围数据_口袋西游()
{
        DWORD Temp = *(DWORD*)(*(DWORD*)(*(DWORD*)(*(DWORD*)口袋西游_周围基地址 + 0x1C)
+ 0x8) + 0x20);
        对象数量 = *(DWORD*)(Temp +  0x5C);
        DWORD 怪物基地址 = *(DWORD*)(Temp + 0x58);
        for (int i = 0; i < (int)对象数量; i++)
        {
                对象列表[i].X_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2DC);
                对象列表[i].Y_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2E0);
                对象列表[i].Z_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2E4);
                对象列表[i].X_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2E8);
                对象列表[i].Y_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2EC);
                对象列表[i].Z_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2F0);
                对象列表[i].阵营 = 1;
                对象列表[i].Hp = *(DWORD*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x138);
                对象列表[i].死亡标志位 = 0;
                 
        }
        对象数量++;
        对象列表[对象数量-1].X_j = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x578);
        对象列表[对象数量-1].Y_j = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x57C);
        对象列表[对象数量-1].Z_j = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x580);
        对象列表[对象数量-1].X_H = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x584);
        对象列表[对象数量-1].Y_H = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x588);
        对象列表[对象数量-1].Z_H = *(FLOAT*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x58C);
        对象列表[对象数量-1].阵营 = 2;
        对象列表[对象数量-1].Hp = *(DWORD*)(*(DWORD*)(*(DWORD*)(*(DWORD*)0xD0DF1C + 0x1C)
+ 0x28) + 0x288);
        对象列表[对象数量-1].死亡标志位 = 0;
}
 
DWORD 突袭_周围基地址 = 0x00587C10;
void 周围对象::刷新周围数据_突袭()
{
        对象数量 = *(DWORD*)0x00587C18;
        DWORD 怪物基地址 = *(DWORD*)突袭_周围基地址;
        for (int i = 1; i < (int)对象数量; i++)
        {
                对象列表[i].X_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x28);
                对象列表[i].Y_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x2C);
                对象列表[i].Z_j = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x30);
                对象列表[i].X_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x4);
                对象列表[i].Y_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0x8);
                对象列表[i].Z_H = *(FLOAT*)(*(DWORD*)(怪物基地址 + 0x4 * i) + 0xC);
                对象列表[i].阵营 = 2;
                对象列表[i].Hp = 100;
                对象列表[i].死亡标志位 = 0;
        }
}
 
 
 
 
 
 

上一页  [1] [2] 



热门文章
  • 解决Request 对象 错误 ASP 0104 :...
  • FPS游戏(UE4,U3D引擎)方框绘制,骨骼...
  • 如何把开始菜单图标都放到左边?Win...
  • Win11最低配置要求|微软最新系统Wi...
  • 电脑显卡坏了怎么修?显卡故障是什...
  • 磁盘出现问题怎么办?3个小技巧教你...
  • 惠普电脑怎么进入bios设置u盘启动?...
  • WannaCry恶意样本的分析
  • HarmonyOS一键优化(华为 荣耀通用)...
  • 【批处理】就0x11b共享打印机问题自...
  • HTTPS加密解析,HTTPS灵魂拷问
  • 文档背景水印怎么设置的?用这两种...
  • 锦州广厦电脑上门维修

    报修电话:13840665804  QQ:174984393 (联系人:毛先生)   
    E-Mail:174984393@qq.com
    维修中心地址:锦州广厦电脑城
    ICP备案/许可证号:辽ICP备2023002984号-1
    上门服务区域: 辽宁锦州市区
    主要业务: 修电脑,电脑修理,电脑维护,上门维修电脑,黑屏蓝屏死机故障排除,无线上网设置,IT服务外包,局域网组建,ADSL共享上网,路由器设置,数据恢复,密码破解,光盘刻录制作等服务

    技术支持:微软等