本篇內容包括:
1. 透視實現的方法介紹
2. 通過程序名獲取程序id和程序控制程式碼
3. 通過程序id獲取程序中的模組資訊(模組大小,模組地址,模組控制程式碼)
4. 讀取遊戲記憶體(人物ViewMatrix,敵人座標,敵人生命值,敵人陣營)
5. 三維座標轉二維座標(遊戲內人物座標轉換成螢幕上的座標)
6. glfw+imgui 在螢幕上的繪製直線
請先依據前兩篇,對偏移、基址有基本瞭解,並且設定好了glfw+imgui的環境,在上篇建立好的工程中建立CPP檔案和同名.h檔案
實現效果:
一般有兩種方式,一種是外掛,一種是內掛,外掛是在建立一個透明視窗,在透明視窗上畫線,讓滑鼠事件透過視窗,透明視窗覆蓋在遊戲視窗上。內掛是通過DLL注入,HOOK遊戲中的繪製函數,在遊戲繪製人物的時候繪製自己的線。還剩一種比較少用,但也可以實現,找到人物模型ID,在渲染到人物模型的時候關掉渲染緩衝(應該是叫這個?),使人物模型在牆模型前面渲染,導致可以直接看到人物。本篇文章採用的是外掛的形式,根據上篇文章已經可以建立出一個覆蓋在螢幕上的透明視窗。
變數名起的挺明白的,就不寫註釋了
DWORD g_process_id = NULL; HANDLE g_process_handle = NULL; UINT_PTR g_local_player = NULL; UINT_PTR g_player_list_address = NULL; UINT_PTR g_matrix_address = NULL; UINT_PTR g_angle_address = NULL; HWND g_game_hwnd = NULL; module_information engine_module; module_information client_module; module_information server_module; float g_client_width; float g_client_height;
#define dwViewMatrix 0x4DCF254 #define dwLocalPlayer 0xDC14CC #define dwClientState 0x58CFDC #define dwEntityList 0x4DDD93C #define dwClientState_ViewAngles 0x4D90 #define m_vecOrigin 0x138 #define m_bDormant 0xED #define m_lifeState 0x25F #define m_iHealth 0x100 #define m_iTeamNum 0xF4
void GetWindowSize() { HDC hdc = GetDC(nullptr); g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES); g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES); ReleaseDC(nullptr, hdc); }
void error(const char*text) { MessageBoxA(nullptr, text, nullptr, MB_OK); exit(-1); } bool is_error() { return GetLastError() != 0; }
使用CreateToolhelp32Snapshot函數,建立程序快照,遍歷系統快照中的程序名,遍歷到process_name,則返回該程序的程序ID
DWORD get_process_id(const char*process_name) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (is_error()) error("CreateToolhelp32Snapshot失敗"); PROCESSENTRY32 process_info; ZeroMemory(&process_info, sizeof(process_info)); process_info.dwSize = sizeof(process_info); char target[1024]; ZeroMemory(target, 1024); strncpy_s(target, process_name, strlen(process_name)); _strupr(target); bool state = Process32First(snap, &process_info); while (state) { if (strncmp(_strupr(process_info.szExeFile), target, strlen(target)) == 0) { return process_info.th32ProcessID; } state = Process32Next(snap, &process_info); } CloseHandle(snap); return 0; }
通過程序ID獲取程序控制程式碼
HANDLE get_process_handle(DWORD process_id) { HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id); if (is_error()) error("get_process_handle失敗"); return process_handle; }
可以發現偏移都是由 client.dll+xxxxx 此種形式構成,所以需要獲取模組的地址
先建立一個模組結構體,需要獲取模組的模組大小,模組地址,模組控制程式碼
class module_information { public: HANDLE module_handle; char module_name[1024]; char *module_data; UINT_PTR module_address; int module_size; void alloc(int size) { module_size = size; module_data = (char *)VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (is_error())error("申請記憶體失敗"); } void release() { if (module_data)VirtualFree(module_data, 0, MEM_RELEASE); module_data = nullptr; } };
傳入程序ID和需要獲取的模組名,CreateToolhelp32Snapshot建立模組快照,遍歷快照,比對模組名,獲取模組資訊
void get_moduel_info(DWORD process_id, const char *name, OUT module_information&info) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process_id); if (is_error())error("建立快照錯誤"); MODULEENTRY32 module_info; ZeroMemory(&module_info, sizeof(module_info)); module_info.dwSize = sizeof(module_info); char target[1024]; ZeroMemory(target, 1024); strncpy(target, name, strlen(name)); _strupr(target); bool status = Module32First(snap, &module_info); while (status) { if (strncmp(_strupr(module_info.szModule), target, sizeof(target)) == 0) { info.module_address = (UINT_PTR)module_info.modBaseAddr; info.module_handle = module_info.hModule; info.alloc(module_info.modBaseSize); DWORD size = read_memory(g_process_handle, info.module_address, info.module_data, info.module_size);//TODO CloseHandle(snap); return; } status = Module32Next(snap, &module_info); } error("未找到模組"); return; }
例如之前得到 上下角度 = [[engine.dll+58CFDC]+00004D90] ,則可以
ReadProcessMemory(g_process_handle, (LPVOID)(engine.dll+58CFDC), recv, size, &readsize);
ReadProcessMemory(g_process_handle, (LPVOID)recv, recv, size, &readsize);
函數的使用方法:ReadProcessMemory(控制程式碼,地址,讀到哪裡,讀多少,具體讀了多少);
則可以讀到上下角度
通過ReadProcessMemory函數讀取記憶體,對這個函數進行打包,方便使用(好吧,我承認這個打包的很爛,幾乎沒有方便使用)
DWORD read_memory(HANDLE process, DWORD address, void *recv, int size) { DWORD readsize; ReadProcessMemory(process, (LPVOID)address, recv, size, &readsize); return readsize; if (is_error())error("讀取記憶體失敗"); }
重寫了一個我覺得比較好用的,各位可以酌情對其進行改寫
template<class T> T ReadMem(HANDLE ProcessHandle, UINT_PTR Address, int size) { T Reader; ReadProcessMemory(ProcessHandle, (LPVOID)Address, &Reader, size, NULL); return Reader; }
建立兩個結構體來儲存二維座標,一個用來儲存三維座標
struct Vec2 {
public: float x, y; }; struct Vec3 { public: float x, y, z; };
傳入一個三維座標和視角矩陣,算出人物在螢幕上的座標 VecScreen
bool WorldToScreen(const Vec3& VecOrgin, Vec2& VecScreen, float* Matrix) { VecScreen.x = VecOrgin.x *Matrix[0] + VecOrgin.y*Matrix[1] + VecOrgin.z*Matrix[2] + Matrix[3]; VecScreen.y = VecOrgin.x *Matrix[4] + VecOrgin.y*Matrix[5] + VecOrgin.z*Matrix[6] + Matrix[7]; float w = VecOrgin.x*Matrix[12] + VecOrgin.y*Matrix[13] + VecOrgin.z*Matrix[14] + Matrix[15]; if (w < 0.01f) { return false; } Vec2 NDC; NDC.x = VecScreen.x / w; NDC.y = VecScreen.y / w; VecScreen.x = (g_client_width / 2 * NDC.x) + (NDC.x + g_client_width / 2); VecScreen.y = (g_client_height / 2 * NDC.y) + (NDC.y + g_client_height / 2); ConvertToRange(VecScreen); return true; } void ConvertToRange(Vec2 &Point) { Point.x /= g_client_width; Point.x *= 2.0f; Point.x -= 1.0f; Point.y /= g_client_height; Point.y *= 2.0f; Point.y -= 1.0f; }
使用glVertex2f函數,第一個glVertex2f是開始的位置,第二個glVertex2f是結束的位置
void DrawLine(Vec2& start, Vec2& end) { glLineWidth(1.2); glBegin(GL_LINES); glColor4f(255, 255, 255, 100); glVertex2f(start.x, start.y); glVertex2f(end.x, end.y); glEnd(); }
void init_address(const char*process_name) { std::cout << "請先啟動遊戲"<< std::endl; DWORD process_id = get_process_id(process_name); HANDLE process_handle = get_process_handle(process_id); g_process_id = process_id; //將pid儲存到全域性變數 g_process_handle = process_handle;//將process_handle儲存到全域性變數 //獲取模組資訊 get_moduel_info(process_id, "engine.dll", engine_module); get_moduel_info(process_id, "client.dll", client_module); get_moduel_info(process_id, "server.dll", server_module); UINT_PTR temp_address; float Matrix[16]; UINT_PTR matrix_address = client_module.module_address + dwViewMatrix; //獲取視角矩陣地址 g_matrix_address = matrix_address; //將視角矩陣地址儲存到全域性變數 //獲取人物視角地址 ReadProcessMemory(g_process_handle, (LPVOID)(engine_module.module_address + 0x58CFDC), &temp_address, 4, NULL);//[engine.dll + 58CFDC]+00004D90 g_angle_address = temp_address + dwClientState_ViewAngles; //獲取本地人物地址 [client.dll+0xDC04CC]+100 = 生命值 ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &temp_address, 4, NULL); g_local_player = temp_address; //[g_local_player+100] = 生命值 //獲得ENtitylist地址 [client.dll+0x4DDC90C + i *0x10]+100 = 敵人生命值 g_player_list_address = client_module.module_address + dwEntityList; }
先說一下整體的思路:
通過程序名(csgo.exe)獲取程序ID
↓
通過程序ID獲取程序控制程式碼、client.dll模組的資訊
↓
通過程序控制程式碼讀取人物視角矩陣地址、本地人物物件地址、敵人物件地址 並儲存到全域性變數(初始化完成)
↓
獲得螢幕大小儲存在全域性變數、建立透明視窗
↓
迴圈遍歷敵人物件,通過地址讀取到人物的視角矩陣、敵人的位置
↓
在迴圈中將敵人的位置結合矩陣,轉換成2D座標
↓
再回圈中在透明視窗上把算出來的座標畫出來
再寫一段虛擬碼出來幫助理解,程式碼貼在後面
int main { 獲取視角矩陣地址、獲取本地人物地址、獲取敵人物件地址 獲取螢幕解析度 根據螢幕解析度建立視窗 while(1)訊息迴圈 {
清除畫的線 獲得視角矩陣,因為會變,所以需要不停的獲取 for(int i=0;i<64;i++)因為遊戲人數最大為64 { 獲得自己的陣營 獲取當前敵人物件 根據物件獲取人物血量、陣營、生存狀態、敵人是否有效 如果敵人血量<=0 或者 敵人陣營=自己陣營 或者 無效 或者 敵人物件為空 或者 敵人生存狀態
則遍歷下一個物件 獲得敵人的位置 將敵人的座標轉換為2D座標 畫線 } } }
GetImformation.h
#pragma once struct Vec2 {public: float x, y; }; struct Vec3 { public: float x, y, z; }; bool is_error(); void error(const char*text); class module_information { public: HANDLE module_handle; char module_name[1024]; char *module_data; UINT_PTR module_address; int module_size; void alloc(int size) { module_size = size; module_data = (char *)VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (is_error())error("申請記憶體失敗"); } void release() { if (module_data)VirtualFree(module_data, 0, MEM_RELEASE); module_data = nullptr; } }; void init_address(const char*process_name); DWORD get_process_id(const char*process_name); HANDLE get_process_handle(DWORD process_id); void ConvertToRange(Vec2 &Point); bool WorldToScreen(const Vec3& VecOrgin, Vec2& VecScreen, float* Matrix); void get_moduel_info(DWORD process_id, const char *name, OUT module_information&info); DWORD read_memory(HANDLE process, DWORD address, void *recv, int size); template<class T> T ReadMem(HANDLE ProcessHandle, UINT_PTR Address, int size) { T Reader; ReadProcessMemory(ProcessHandle, (LPVOID)Address, &Reader, size, NULL); return Reader; }
GetImformation.cpp
#include<Windows.h> #include<TlHelp32.h> #include"GetIMformation.h" DWORD g_process_id = NULL; HANDLE g_process_handle = NULL; UINT_PTR g_local_player = NULL; UINT_PTR g_player_list_address = NULL; UINT_PTR g_matrix_address = NULL; UINT_PTR g_angle_address = NULL; HWND g_game_hwnd = NULL; module_information engine_module; module_information client_module; module_information server_module; float g_client_width; float g_client_height; #define dwViewMatrix 0x4DCF254 #define dwLocalPlayer 0xDC14CC #define dwClientState 0x58CFDC #define dwEntityList 0x4DDD93C #define dwClientState_ViewAngles 0x4D90 #define m_vecOrigin 0x138 #define m_bDormant 0xED #define m_lifeState 0x25F #define m_iHealth 0x100 #define m_iTeamNum 0xF4 //獲取模組資訊 void get_moduel_info(DWORD process_id, const char *name, OUT module_information&info) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process_id); if (is_error())error("建立快照錯誤"); MODULEENTRY32 module_info; ZeroMemory(&module_info, sizeof(module_info)); module_info.dwSize = sizeof(module_info); char target[1024]; ZeroMemory(target, 1024); strncpy(target, name, strlen(name)); _strupr(target); bool status = Module32First(snap, &module_info); while (status) { if (strncmp(_strupr(module_info.szModule), target, sizeof(target)) == 0) { info.module_address = (UINT_PTR)module_info.modBaseAddr; info.module_handle = module_info.hModule; info.alloc(module_info.modBaseSize); //DWORD size = read_memory(g_process_handle, info.module_address);//TODO DWORD size = read_memory(g_process_handle, info.module_address, info.module_data, info.module_size);//TODO CloseHandle(snap); return; } status = Module32Next(snap, &module_info); } error("未找到模組"); return; } void error(const char*text) { MessageBoxA(nullptr, text, nullptr, MB_OK); exit(-1); } bool is_error() { return GetLastError() != 0; } DWORD read_memory(HANDLE process, DWORD address, void *recv, int size) { DWORD readsize; ReadProcessMemory(process, (LPVOID)address, recv, size, &readsize); return readsize; if (is_error())error("讀取記憶體失敗"); } HANDLE get_process_handle(DWORD process_id) { HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id); if (is_error()) error("get_process_handle失敗"); std::cout << "程序控制程式碼為:" << std::hex << process_handle << std::endl; return process_handle; } DWORD get_process_id(const char*process_name) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (is_error()) error("CreateToolhelp32Snapshot失敗"); PROCESSENTRY32 process_info; ZeroMemory(&process_info, sizeof(process_info)); process_info.dwSize = sizeof(process_info); char target[1024]; ZeroMemory(target, 1024); strncpy_s(target, process_name, strlen(process_name)); _strupr(target); bool state = Process32First(snap, &process_info); while (state) { if (strncmp(_strupr(process_info.szExeFile), target, strlen(target)) == 0) { CloseHandle(snap); return process_info.th32ProcessID; } state = Process32Next(snap, &process_info); } CloseHandle(snap); MessageBoxA(NULL, "查詢程序id失敗", "提示", MB_OK); return 0; } void GetWindowSize() { HDC hdc = GetDC(nullptr); g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES); g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES); ReleaseDC(nullptr, hdc); } void init_address(const char*process_name) { std::cout << "請先啟動遊戲"<< std::endl; DWORD process_id = get_process_id(process_name); HANDLE process_handle = get_process_handle(process_id); g_process_id = process_id; //獲取模組資訊 g_process_handle = process_handle; get_moduel_info(process_id, "engine.dll", engine_module); get_moduel_info(process_id, "client.dll", client_module); get_moduel_info(process_id, "server.dll", server_module); //TODO 要寫一個特徵碼定址,獲取視角矩陣資訊 UINT_PTR temp_address; float Matrix[16]; UINT_PTR matrix_address = client_module.module_address + dwViewMatrix; g_matrix_address = matrix_address; //獲取 人物視角地址 ReadProcessMemory(g_process_handle, (LPVOID)(engine_module.module_address + 0x58CFDC), &temp_address, 4, NULL);//[engine.dll + 58CFDC]+00004D90 g_angle_address = temp_address + 0x00004D90; //獲取本地人物地址 [client.dll+0xDC04CC]+100 = 生命值 ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &temp_address, 4, NULL); g_local_player = temp_address; //[g_local_player+100] = 生命值 temp_address = 0; //獲得ENtitylist地址 [client.dll+0x4DDC90C + i *0x10]+100 = 敵人生命值 g_player_list_address = client_module.module_address + dwEntityList; } bool WorldToScreen(const Vec3& VecOrgin, Vec2& VecScreen, float* Matrix) { VecScreen.x = VecOrgin.x *Matrix[0] + VecOrgin.y*Matrix[1] + VecOrgin.z*Matrix[2] + Matrix[3]; VecScreen.y = VecOrgin.x *Matrix[4] + VecOrgin.y*Matrix[5] + VecOrgin.z*Matrix[6] + Matrix[7]; float w = VecOrgin.x*Matrix[12] + VecOrgin.y*Matrix[13] + VecOrgin.z*Matrix[14] + Matrix[15]; if (w < 0.01f) { return false; } Vec2 NDC; NDC.x = VecScreen.x / w; NDC.y = VecScreen.y / w; VecScreen.x = (g_client_width / 2 * NDC.x) + (NDC.x + g_client_width / 2); VecScreen.y = (g_client_height / 2 * NDC.y) + (NDC.y + g_client_height / 2); ConvertToRange(VecScreen); return true; } void ConvertToRange(Vec2 &Point) { Point.x /= g_client_width; Point.x *= 2.0f; Point.x -= 1.0f; Point.y /= g_client_height; Point.y *= 2.0f; Point.y -= 1.0f; }
main.cpp
#include <stdio.h> #include<cstdlib> #include<Windows.h> #include<iostream> #include<cmath> #include <GLFW/glfw3.h> #include "imgui/imgui.h" #include "imgui/imgui_impl_glfw.h" #include "imgui/imgui_impl_opengl3.h" #include "GetIMformation/GetIMformation.h" //宣告外部變數 extern DWORD g_process_id; extern HANDLE g_process_handle; extern UINT_PTR g_local_player; extern UINT_PTR g_player_list_address; extern UINT_PTR g_matrix_address; extern UINT_PTR g_angle_address; extern HWND g_game_hwnd; extern module_information engine_module; extern module_information client_module; extern module_information server_module; extern float g_client_width; extern float g_client_height; void DrawLine(Vec2& start, Vec2& end) { glLineWidth(1.2); glBegin(GL_LINES); glColor4f(255, 255, 255, 100); glVertex2f(start.x, start.y); glVertex2f(end.x, end.y); glEnd(); } void ShowMenu(GLFWwindow* Window) { glfwSetWindowAttrib(Window, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); } void HideMenu(GLFWwindow* Window) { glfwSetWindowAttrib(Window, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); } static void glfw_error_callback(int error, const char* description) { fprintf(stderr, "Glfw Error %d: %s\n", error, description); } void GetWindowSize() { HDC hdc = GetDC(nullptr); g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES); g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES); ReleaseDC(nullptr, hdc); } int main(int, char**) { /////////////////////////功能性程式碼//////////////////////////////////////////////////////////////////////////////////////////// GetWindowSize(); init_address("csgo.exe"); UINT_PTR temp_address; /////////////////////////功能性程式碼//////////////////////////////////////////////////////////////////////////////////////////// // Setup window glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; GLFWmonitor* monitor = glfwGetPrimaryMonitor(); //###########################設定視窗########################### auto glsl_version = "#version 130"; int Height = glfwGetVideoMode(monitor)->height; int Width = glfwGetVideoMode(monitor)->width; glfwWindowHint(GLFW_FLOATING, true); glfwWindowHint(GLFW_RESIZABLE, false); glfwWindowHint(GLFW_MAXIMIZED, true); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, true); //###########################設定視窗########################### GLFWwindow* window = glfwCreateWindow(Width, Height, "titile", nullptr, nullptr); if (window == nullptr) return 1; glfwSetWindowAttrib(window, GLFW_DECORATED, false); //設定沒有標題列 ShowWindow(GetConsoleWindow(), SW_HIDE); glfwMakeContextCurrent(window); glfwSwapInterval(1); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); bool bMenuVisible = true; bool Dormant; int EntityTeamNum; int lifestate; int blood; int iTeamNum; float temp_pos[3]; float Matrix[16]; Vec2 LineOrigin; Vec2 ScreenCoord; Vec3 EntityLocation; LineOrigin.x = 0.0f; LineOrigin.y = -1.0f; UINT_PTR Entity; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); if (GetAsyncKeyState(VK_F11) & 1) { bMenuVisible = !bMenuVisible; if (bMenuVisible) ShowMenu(window); else HideMenu(window); } //介面設計 if (bMenuVisible) { ImGui::Text("USE F11 TO Hiden/Show"); ImGui::Text(""); if (ImGui::Button("exit")) return 0; } ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &g_local_player, 4, nullptr); if(g_local_player!=0) { ScreenCoord.x = 0.0f; ScreenCoord.y = -1.0f; g_angle_address = ReadMem<UINT_PTR>(g_process_handle, (engine_module.module_address + dwClientState), 4)+ dwClientState_ViewAngles; ReadProcessMemory(g_process_handle, (LPCVOID)(client_module.module_address + dwViewMatrix), Matrix, sizeof(float) * 16, nullptr); for (short int i = 0; i < 64; ++i) { ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &g_local_player, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(g_local_player + m_iTeamNum), &iTeamNum, 4, nullptr); //獲取敵人實體 ReadProcessMemory(g_process_handle, (LPCVOID)(client_module.module_address + dwEntityList + i * 0x10), &Entity, sizeof(float), nullptr); ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_bDormant), &Dormant, sizeof(bool), nullptr); ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_lifeState), &lifestate, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(Entity + m_iTeamNum), &EntityTeamNum, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(Entity + m_iHealth), &blood, 4, nullptr); if ((Entity == NULL) || (Entity == g_local_player) || (EntityTeamNum == iTeamNum) || (blood <= 0) || lifestate || Dormant) continue; ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_vecOrigin), &temp_pos, 12, nullptr); EntityLocation.x = temp_pos[0], EntityLocation.y = temp_pos[1], EntityLocation.z = temp_pos[2]; if (!WorldToScreen(EntityLocation, ScreenCoord, Matrix)) continue; if (true) { DrawLine(LineOrigin, ScreenCoord); } } } // Rendering ImGui::Render(); int display_w, display_h; glfwGetFramebufferSize(window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); return 0; }
至此一個簡單的透視就寫完了,本系列也完結力....
後記:
感覺有些地方邏輯講的不是很清晰,有問題可以在評論區裡提
裡面寫的偏移全都是寫死的,等CSGO一更新就不能用了,解決這個問題的方法,就是寫一個特徵碼查詢,但不能保證所有人都是來學思路和方法的,對於直接copy程式碼的,起碼偏移要自己找一下。
方框、自瞄、防閃其實也寫了,但感覺不是很適合在這裡發,如果後面想發了,再寫一個補充篇吧
因為是從一堆功能中抽出一個小透,所以可能報錯,少點什麼東西,能自己補的自己補一下,不能的評論區說一下,我來補