非矩阵画线

先说好,因为是是通过数学大致推出坐标,大致画线,所以实际上并不准确,但是作为新手学习下原理也是不错的

代码已经能跑了,当然不是很完美,但是简单定位是没问题了,但是因为精度实在太差……..我去学矩阵了😂

Dll入口函数

首先在Dll入口函数创建线程,Sleep看自己电脑是否受得了

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
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include "周围对象.h"
#include "绘制.h"

DWORD WINAPI ThreadFunc(LPVOID lpParam)
{
HWND hwnd = FindWindowA(NULL, "Counter-Strike");
绘制 FPS绘制(hwnd);
while (TRUE)
{
for (int i = 0; i < 5; i++)
{
FPS绘制.世界坐标转为屏幕坐标_非矩阵(*FPS绘制.目标周围对象.对象列表[i], FPS绘制.目标屏幕坐标结构[i]);
FPS绘制.目标周围对象.刷新周围数据();
FPS绘制.画线(FPS绘制.目标屏幕坐标结构[i].x坐标, FPS绘制.目标屏幕坐标结构[i].y坐标,FPS绘制.目标周围对象.对象列表[i]->阵营标志);
}
Sleep(50);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule); //避免触发DLL_THREAD_ATTACH,DLL_THREAD_DETACH
HANDLE hThread;
DWORD threadId;
// 创建线程
hThread = CreateThread(
NULL, // 默认安全属性
0, // 默认堆栈大小
ThreadFunc, // 线程函数的入口地址
NULL, // 传递给线程函数的参数
0, // 线程创建标志
&threadId // 接收线程标识符
);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

创建两个类,一个是周围对象类,一个是绘制类

周围对象类

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
#pragma once
#define Π 3.1415927
struct 朝向结构
{
float 水平朝向;
float 垂直朝向;
};
struct 对象结构
{
float 对象重心的x坐标;
float 对象重心的y坐标;
float 对象重心的z坐标;


float 对象脚的x坐标;
float 对象脚的y坐标;
float 对象脚的z坐标;

float 对象头的x坐标;
float 对象头的y坐标;
float 对象头的z坐标;

int 血量;

unsigned int 死亡标志;
unsigned int 阵营标志;


朝向结构 对象的重心朝向;
朝向结构 对象脚的朝向;
朝向结构 对象头的朝向;

朝向结构 对象重心的角度差;
朝向结构 对象脚的角度差;
朝向结构 对象头的角度差;
};
struct 玩家自身坐标和朝向
{
float 玩家的x坐标;
float 玩家的y坐标;
float 玩家的z坐标;
朝向结构 玩家的朝向结构;
};
class 周围对象
{
public:
玩家自身坐标和朝向 自身坐标和朝向;
对象结构* 对象列表[32];
unsigned int 对象数量;

public:
void 刷新周围数据();
周围对象()
{
for (int i = 0; i < 32; i++)
{
对象列表[i] = new 对象结构;
}
}

private:
void 计算朝向(对象结构& 对象);

void 获取自身坐标和朝向();
};

具体实现:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "周围对象.h"
#include "pch.h"
#include <iostream>



void 周围对象::获取自身坐标和朝向()
{
//首先拿一下cstrike基地址
unsigned int cstrike基地址 = (unsigned int)GetModuleHandleA("cstrike.exe");
this->自身坐标和朝向.玩家的x坐标 = *(float*)(cstrike基地址 + 0x19E3F28);
this->自身坐标和朝向.玩家的y坐标 = *(float*)(cstrike基地址 + 0x19E3F2C);
this->自身坐标和朝向.玩家的z坐标 = *(float*)(cstrike基地址 + 0x19E3F30);

this->自身坐标和朝向.玩家的朝向结构.垂直朝向 = *(float*)(cstrike基地址 + 0x1B59CAC);
this->自身坐标和朝向.玩家的朝向结构.水平朝向 = *(float*)(cstrike基地址 + 0x1B59CB0);
}

void 周围对象::计算朝向(对象结构& 对象)
{
this->获取自身坐标和朝向();
if (对象.对象重心的x坐标 > this->自身坐标和朝向.玩家的x坐标 && 对象.对象重心的y坐标 > this->自身坐标和朝向.玩家的y坐标)
{
//说明这是在第一象限
//开始计算角度
对象.对象的重心朝向.水平朝向 = atan2(对象.对象重心的y坐标 - this->自身坐标和朝向.玩家的y坐标, 对象.对象重心的x坐标 - this->自身坐标和朝向.玩家的x坐标) / Π * 180;
}

else if (对象.对象重心的x坐标 < this->自身坐标和朝向.玩家的x坐标 && 对象.对象重心的y坐标 > this->自身坐标和朝向.玩家的y坐标)
{
//说明这是在第二象限
对象.对象的重心朝向.水平朝向 = 180-atan2(对象.对象重心的y坐标 - this->自身坐标和朝向.玩家的y坐标, this->自身坐标和朝向.玩家的x坐标 - 对象.对象重心的x坐标) / Π * 180;
}

else if (对象.对象重心的x坐标 < this->自身坐标和朝向.玩家的x坐标 && 对象.对象重心的y坐标 < this->自身坐标和朝向.玩家的y坐标)
{
//说明这是在第三象限
对象.对象的重心朝向.水平朝向 =180+atan2(this->自身坐标和朝向.玩家的y坐标 - 对象.对象重心的y坐标, this->自身坐标和朝向.玩家的x坐标 - 对象.对象重心的x坐标) / Π * 180;
}

else if (对象.对象重心的x坐标 > this->自身坐标和朝向.玩家的x坐标 && 对象.对象重心的y坐标 < this->自身坐标和朝向.玩家的y坐标)
{
//说明这是在第四象限
对象.对象的重心朝向.水平朝向 = 360- atan2(this->自身坐标和朝向.玩家的y坐标 - 对象.对象重心的y坐标, 对象.对象重心的x坐标 - this->自身坐标和朝向.玩家的x坐标) / Π * 180;
}


float 水平面距离 = sqrt(pow(对象.对象重心的x坐标, 2) + pow(对象.对象重心的y坐标, 2));

if (对象.对象重心的z坐标 > this->自身坐标和朝向.玩家的z坐标)
{
对象.对象的重心朝向.垂直朝向 = atan2(对象.对象重心的z坐标 - this->自身坐标和朝向.玩家的z坐标, 水平面距离) / Π * 180;
}
else
{
对象.对象的重心朝向.垂直朝向 = atan2(this->自身坐标和朝向.玩家的z坐标 - 对象.对象重心的z坐标, 水平面距离) / Π * 180;
}

if (this->自身坐标和朝向.玩家的朝向结构.水平朝向 - 对象.对象的重心朝向.水平朝向 < -180)
{
对象.对象重心的角度差.水平朝向 = this->自身坐标和朝向.玩家的朝向结构.水平朝向 - 对象.对象的重心朝向.水平朝向 + 360;
}
else if (this->自身坐标和朝向.玩家的朝向结构.水平朝向 - 对象.对象的重心朝向.水平朝向 > 180)
{
对象.对象重心的角度差.水平朝向 = this->自身坐标和朝向.玩家的朝向结构.水平朝向 - 对象.对象的重心朝向.水平朝向 - 360;
}
else
{
对象.对象重心的角度差.水平朝向 = this->自身坐标和朝向.玩家的朝向结构.水平朝向 - 对象.对象的重心朝向.水平朝向;
}


对象.对象重心的角度差.垂直朝向 = 0-this->自身坐标和朝向.玩家的朝向结构.垂直朝向 - 对象.对象的重心朝向.垂直朝向;

}

void 周围对象::刷新周围数据()
{
//首先拿一下cstrike基地址
unsigned int cstrike基地址 = (unsigned int)GetModuleHandleA("cstrike.exe");

DWORD 人物坐标结构体基地址 = 0x2F5A5C4;

for (int i = 1; i < 32; i++) //这里从1开始是因为下标为1才是其他玩家的坐标
{
this->对象列表[i-1]->对象重心的y坐标 = *((float*)(人物坐标结构体基地址 + 0x24c * i + 0x18c));
this->对象列表[i-1]->对象重心的x坐标 = *((float*)(人物坐标结构体基地址 + 0x24c * i + +0x188));
this->对象列表[i-1]->对象重心的z坐标 = *((float*)(人物坐标结构体基地址 + 0x24c * i + 0x190));
}

DWORD 死亡标志位结构体基地址 = 0x1A25654;
for (int i = 1; i < 32; i++)
{
if (*(DWORD*)(死亡标志位结构体基地址 + 0x68 + 0x68 * i + 0x4) == 0)//判定为敌方
{
this->对象列表[i - 1]->阵营标志 = 1; //这里记录1代表敌方
}
else
{
this->对象列表[i - 1]->阵营标志 = 0; //这里记录0代表队友
}

this->对象列表[i - 1]->死亡标志 = *(DWORD*)(死亡标志位结构体基地址 + 0x68 + 0x68 * i );
}

for (int i = 1; i < 32; i++)
{
this->计算朝向(*对象列表[i - 1]);
}
}

绘制类:

头文件

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
#pragma once
#include "pch.h"
#include <iostream>
#include "绘制.h"
#include "周围对象.h"
struct 屏幕坐标结构
{
float x坐标;
float y坐标;
};

class 绘制
{
public:
HWND 目标窗口句柄; //锁定在哪个窗口上画框
RECT 内窗口; //一般指显示游戏界面
RECT 外窗口; //可能在内窗口外面有一个外窗口,例如菜单啥的
unsigned int 分辨率_宽;
unsigned int 分辨率_高;
int 外窗口宽;
int 外窗口高;
float 矩阵[16];
DWORD 矩阵地址;
HDC hdc;
struct 屏幕坐标结构 目标屏幕坐标结构[32] = { 0 };
周围对象 目标周围对象;


public:
绘制(HWND 窗口句柄, DWORD 矩阵地址)
{
this->目标窗口句柄 = 窗口句柄;
this->矩阵地址 = 矩阵地址;
}
绘制(HWND 窗口句柄)
{
目标窗口句柄 = 窗口句柄;
this->hdc = GetDC(this->目标窗口句柄);
}
public:
bool 世界坐标转为屏幕坐标_非矩阵(对象结构 目标对象结构, 屏幕坐标结构& 目标屏幕坐标结构);
bool 世界坐标转为屏幕坐标_矩阵();
void 画线(int x, int y,DWORD 阵营);
void 绘制矩形框(RECT 矩形);
void 绘制字符串(int x, int y);

private:
void 获取窗口信息();

};

具体实现:

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include "绘制.h"
#include <cmath> // 包含 tan 函数
void 绘制::获取窗口信息()
{
if (!GetClientRect(this->目标窗口句柄, &this->内窗口))//获取的是内部窗口的信息,这样可以获取左上,右下的坐标,分辨率就是宽和高占的像素
{
DWORD Error = GetLastError();
MessageBox(0, L"错误", L"获取内窗口属性失败", 0);
}
this->分辨率_宽 = this->内窗口.right - this->内窗口.left;
this->分辨率_高 = this->内窗口.bottom - this->内窗口.top;//注意这里是bottom减去top

if (!GetWindowRect(this->目标窗口句柄, &this->外窗口))
{
MessageBox(0, L"错误", L"获取外窗口属性失败", 0);
}

this->外窗口宽 = this->外窗口.right - this->外窗口.left;
this->外窗口高 = this->外窗口.bottom - this->外窗口.top;//注意这里是bottom减去top
}

void 绘制::绘制矩形框(RECT 矩形)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(this->目标窗口句柄, &ps);

// 创建一个绿色的画笔
HPEN hPen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0));
HGDIOBJ oldPen = SelectObject(hdc, hPen);

// 创建一个空白的画刷
HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);
HGDIOBJ oldBrush = SelectObject(hdc, hBrush);

// 绘制矩形边框
Rectangle(hdc, 矩形.left, 矩形.top, 矩形.right, 矩形.bottom);

// 恢复原来的画笔和画刷
SelectObject(hdc, oldPen);
SelectObject(hdc, oldBrush);

// 清理
DeleteObject(hPen);
EndPaint(this->目标窗口句柄, &ps);
}

void 绘制::画线(int x,int y,DWORD 阵营) //默认从客户区的最下面中间开始连线
{
获取窗口信息();
HPEN hPen;
// 创建一个绿色的画笔 队友
if (阵营 == 0)
{
hPen = CreatePen(PS_SOLID, 10, RGB(0, 255, 0));

}
else//敌人
{
hPen = CreatePen(PS_SOLID, 10, RGB(255, 0, 0));
}
HGDIOBJ oldPen = SelectObject(hdc, hPen);
//连线
MoveToEx(hdc, this->分辨率_宽 / 2, this->分辨率_高*3/4, NULL);
// 从起始位置绘制到 (200, 200)
LineTo(hdc, x, y);

}

void 绘制::绘制字符串(int x,int y)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(this->目标窗口句柄, &ps);

// 要绘制的字符串
TCHAR* text = (TCHAR*)L"Hello, Win32 API!";

// 在 (50, 50) 位置绘制字符串
TextOut(hdc, x, y, text, lstrlen(text));

EndPaint(this->目标窗口句柄, &ps);
}

bool 绘制::世界坐标转为屏幕坐标_非矩阵(对象结构 目标对象结构, 屏幕坐标结构& 目标屏幕坐标结构)
{
获取窗口信息();
FLOAT 高低可视角度 = (FLOAT)(atan2(分辨率_高, 分辨率_宽) * 180 / Π);
if (fabs(目标对象结构.对象重心的角度差.水平朝向) > 45 || fabs(目标对象结构.对象重心的角度差.垂直朝向) > 高低可视角度)
{
目标屏幕坐标结构.x坐标 = (float)(this->分辨率_宽 / 2);
目标屏幕坐标结构.y坐标 = (float)(this->分辨率_高 * 3 / 4);
return false;//不在屏幕范围内
}
int 水平差 = (int)(tan(目标对象结构.对象重心的角度差.水平朝向 * Π / 180) * (分辨率_宽 / 2));
目标屏幕坐标结构.x坐标 = (float)(分辨率_宽 / 2 + 水平差);

int 高度差 = (int)(tan(目标对象结构.对象重心的角度差.垂直朝向 * Π / 180) * (分辨率_宽) / 2);
目标屏幕坐标结构.y坐标 = (float)(分辨率_高 / 2 + 高度差);

}

世界坐标转为屏幕坐标_非矩阵:

这里讲讲世界坐标转为屏幕坐标_非矩阵的方法:

1723433832687

直接上图吧