Introduction:

Now that we’ve covered how to capture entities and transform their positions from 3D world space to 2D screen space, it’s time to put everything together and build our final ESP overlay using ImGui.

In Part 5, we captured all entity positions by hooking the game’s Z-axis function. Each entity that accessed this function was stored in an unordered set, giving us a complete list of active objects in the game world.

Then, in Part 6, we learned how to convert these 3D positions into screen coordinates using the World to Screen function. This transformation allows us to take an entity’s world position and figure out exactly where it should appear on the player’s monitor.

With both pieces in place, the entity list and the ability to map world positions to screen coordinates we can now iterate through all captured entities, run each one through our World to Screen function, and draw a box, snapline, or any other visual indicator on top of them using ImGui.

Code Explanation:

For this one i will be using the DirectX library to make it simpler.

Let’s begin:

Step 1: Reading Entities and Their Data

ReadProcessMemory(hProcess, (BYTE*)entityAddr, &curEntity, sizeof(curEntity), nullptr); float curHealth = 0; ReadProcessMemory(hProcess, (BYTE*)curEntity + 0x1974, &curHealth, sizeof(curHealth), nullptr); Vec3 curEntPos = { 0, 0, 0 }; ReadProcessMemory(hProcess, (BYTE*)curEntity + 0x120, &curEntPos, sizeof(curEntPos), nullptr);
  • Purpose: Pull information from the target process about each entity.
  • curEntity: Pointer to the current entity accessing the Z-axis function.
  • curHealth: Health value at offset 0x1974 to filter out dead entities.
  • curEntPos: World position at offset 0x120

entityAddr is the stash address of our trampoline hook where it stashs addresses which have accessed the z axis.
This is inside a while loop btw.

Step 2: Filtering Unique and Relevant Entities

if (curEntity != 0 && curHealth != 0 && LocalPlayerAddr != curEntity && uniqueEntities.find(curEntity) == uniqueEntities.end()) { uniqueEntities.insert(curEntity); std::cout << "New unique entity found: " << std::hex << curEntity << std::endl; }
  • Purpose: Only store entities that are alive, not the local player, and haven’t already been inserted.
  • This ensures your unordered set uniqueEntities only contains valid targets for the ESP.

Step 3: Toggle ESP

if (GetAsyncKeyState(VK_F3) & 1) { ESP = !ESP; }
  • Purpose: Simple toggle using the F3 key to enable or disable the ESP overlay at runtime.

Step 4: Loop Through Entities and Get Screen Positions

for (const auto& entity : uniqueEntities) { ReadProcessMemory(hProcess, (BYTE*)entity + 0x120, &curEntPos, sizeof(curEntPos), nullptr); XMMATRIX viewProj; ReadProcessMemory(hProcess, (BYTE*)baseAddr + 0x1EC6BC0, &viewProj, sizeof(viewProj), nullptr); Vec2 screenFeet = { 0 , 0 }; WorldToScreen(curEntPos, screenFeet, viewProj, 1920, 1080); Vec2 screenHead = { 0 , 0 }; curEntPos.z = 173.f WorldToScreen(curEntPos, screenHead, viewProj, 1920, 1080);
  • Purpose: Iterate over all captured entities and get their current world positions.
  • View-projection matrix: Needed to convert the world position to screen coordinates.
  • WorldToScreen: Converts (x, y, z) → (screenX, screenY) for rendering.

Step 5: Drawing Snaplines Using ImGui

// Height of the box float h = screenFeet.y - screenHead.y; // Width proportional to height float w = h / 2.0f; // Top-left and bottom-right corners ImVec2 topLeft(screenHead.x - w / 2.0f, screenHead.y); ImVec2 bottomRight(screenHead.x + w / 2.0f, screenFeet.y); ImGui::GetBackgroundDrawList()->AddRect( topLeft, bottomRight, IM_COL32(255, 0, 0, 255f), 0.0f, 0, 2.0f );
  • Purpose: Draw a line from the bottom center of the screen to each entity’s screen position.
  • Screen boundary check: Ensures you don’t draw off-screen.
  • ImGui draw list: AddLine draws directly on the overlay background.

TL;DR of the Code Flow

  1. Read entity pointers from memory.
  2. Filter only relevant entities and store them in an unordered set.
  3. Toggle ESP with a keypress.
  4. Loop through entities, read their world positions.
  5. Convert positions to 2D screen coordinates.
  6. Draw boxes or snaplines with ImGui at the screen positions.

Result:

IDA-screenshot

Closing Thoughts:

All this reversing just because the inverse of the camera world matrix multiplied with the projection matrix didn’t yield the correct VP matrix. From 3D graphics to tracing matrices, to reversing SIMD instructions, to detour hooks, to trampolines, to ESP, we’ve done a lot. There’s honestly way more to how an “ESP” really works than what I’ve covered here, but I’m just going to end it here, I’m sick of writing all this.