Beginner's Guide: Creating an ESP for Unity Games on Android with LGL Mod Menu

linkPart 1: Adding Overlay in Menu
linkPart 2: Display ESP Line to Players
linkDownload Links and More

Part 1: Adding Overlay in Menu

In this part, we will add an overlay to the LGL Mod Menu. This overlay will allow us to draw lines, text, and other elements on the screen. Before proceeding, ensure you have basic knowledge of setting up a mod menu, including installing AIDE and compiling the mod menu source. If you're new to this, visit this tutorial page by clicking here.

If you're ready, follow these steps:

  1. Download and Set Up the Mod Menu:
    • Download the LGL Mod Menu v4.0 source zip file by clicking here.
    • Extract the source file and open it in AIDE.
    • If you're following this tutorial exactly, remove arm64-v8a from Application.mk to compile only for armeabi-v7a. This is because we're creating ESP for a 32-bit game. If you're working on a 64-bit game, you don't need to remove it.
  2. Create the ESPView.java File:
    • Navigate to the java/com/android/support folder.
    • Create a new file named ESPView.java.
    • Paste the following code into ESPView.java:
    package com.android.support;
    import android.view.View;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.os.Handler;
    import android.graphics.Color;
    import android.graphics.PorterDuff;
    
    public class ESPView extends View {
    
        private Paint strokePaint;
        private int FrameDelay = 33;
    
        public ESPView(Context context) {
            super(context);
            strokePaint = new Paint();
            strokePaint.setStyle(Paint.Style.STROKE);
            strokePaint.setAntiAlias(true);
            setFocusableInTouchMode(false);
            final Handler framehandler = new Handler();
            framehandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    invalidate();
                    framehandler.postDelayed(this, FrameDelay);
                }
            }, FrameDelay);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            Menu.Draw(this, canvas);
        }
    
        public void DrawLine(Canvas cvs, int a, int r, int g, int b, float strokewidth, float fromX, float fromY, float toX, float toY) {
            strokePaint.setColor(Color.argb(a,r,g,b));
            strokePaint.setStrokeWidth(strokewidth);
            cvs.drawLine(fromX, fromY, toX, toY, strokePaint);
        }
    }
  3. Modify Menu.java:
    • Open Menu.java and create an instance variable for ESPView:
    • ESPView espview;
    • Instantiate espview:
    • espview = new ESPView(context);
    • Create a static native Draw function:
    • static native void Draw(ESPView espv, Canvas canvas);
    • Add espview to the WindowManager and create layout parameters for it:
    • mWindowManager = (WindowManager) getContext.getSystemService(getContext.WINDOW_SERVICE);
      
      WindowManager.LayoutParams espparams = new WindowManager.LayoutParams(MATCH_PARENT, MATCH_PARENT,
      iparams, (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE), -3);
      
      mWindowManager.addView(espview, espparams);
      mWindowManager.addView(rootFrame, vmParams);
    • Add code to remove espview when the menu is minimized or destroyed:
    • public void setVisibility(int view) {
          if (rootFrame != null) {
              rootFrame.setVisibility(view);
          }
          if (espview != null) {
              espview.setVisibility(view);
          }
      }
      
      public void onDestroy() {
          if (rootFrame != null) {
              mWindowManager.removeView(rootFrame);
          }
          if (espview != null) {
              mWindowManager.removeView(espview);
          }
      }
  4. Create Header Files in the JNI Folder:
    • Navigate to the jni folder and create a new folder named DrawESP.
    • Inside DrawESP, create two header files: DrawingManager.h and AadilTypes.h.
    • Paste the following code into DrawingManager.h:
    • #include "AadilTypes.h"
      
      bool EnableESP, ESPLine;
      int StartPos, EndPos;
      
      void DrawESP(AadilDrawing esp, int width, int height) {
          if (!EnableESP) return;
          if (ESPLine) {
              esp.DrawLine({255,0,0,255}, 2.0f, {StartPos, 0}, {EndPos, 1000});
          }
      }
    • Paste the following code into AadilTypes.h:
    • #include <jni.h>
      
      struct Vector2 {
          float x,y;
      };
      
      struct Color {
          float r,g,b,a;
      };
      
      class AadilDrawing {
      private:
          JNIEnv *jnienv;
          jobject espview;
          jobject canvas;
      
      public:
          AadilDrawing() {
              jnienv = nullptr;
              espview = nullptr;
              canvas = nullptr;
          }
      
          AadilDrawing(JNIEnv *env, jobject esp, jobject cvs) {
              this->jnienv = env;
              this->espview = esp;
              this->canvas = cvs;
          }
      
          bool isValid() const {
              return (jnienv && espview && canvas);
          }
      
          int getWidth() const {
              if (isValid()) {
                  jclass cvs = jnienv->GetObjectClass(canvas);
                  jmethodID width = jnienv->GetMethodID(cvs, "getWidth", "()I");
                  return jnienv->CallIntMethod(canvas, width);
              }
          }
      
          int getHeight() const {
              if (isValid()) {
                  jclass cvs = jnienv->GetObjectClass(canvas);
                  jmethodID height = jnienv->GetMethodID(cvs, "getHeight", "()I");
                  return jnienv->CallIntMethod(canvas, height);
              }
          }
      
          void DrawLine(Color color, float thickness, Vector2 from, Vector2 to) {
              if (isValid()) {
                  jclass cvs = jnienv->GetObjectClass(espview);
                  jmethodID drawline = jnienv->GetMethodID(cvs, "DrawLine", "(Landroid/graphics/Canvas;IIIIFFFFF)V");
                  jnienv->CallVoidMethod(espview, drawline, canvas, (int)color.a, (int)color.r, (int)color.g, (int)color.b,
                  thickness, from.x, from.y, to.x, to.y);
              }
          }
      };
  5. Modify Setup.h and main.cpp:
    • Rename Setup.cpp to Setup.h in the jni/Menu folder.
    • Remove the line referencing Setup.cpp in Android.mk.
    • Include Setup.h in main.cpp:
    • #include "Menu/Setup.h"
    • Include DrawingManager.h in Setup.h and create a variable for AadilDrawing:
    • #include "DrawESP/DrawingManager.h"
      AadilDrawing aadildrawing;
    • Create the JNI Draw function:
    • void Draw(JNIEnv *env, jobject cls, jobject espview, jobject cvs) {
          aadildrawing = AadilDrawing(env, espview, cvs);
          if (aadildrawing.isValid()) DrawESP(aadildrawing, aadildrawing.getWidth(), aadildrawing.getHeight());
      }
    • Register the Draw function in RegisterMenu:
    • {OBFUSCATE("Draw"), OBFUSCATE("(Lcom/android/support/ESPView;Landroid/graphics/Canvas;)V"), reinterpret_cast(Draw)},
  6. Add Features and Test:
    • Add the following features list to show toggles and seekbars in the menu:
    • const char *features[] = {
          OBFUSCATE("10_Toggle_Enable ESP"),
          OBFUSCATE("20_Toggle_ESP Line"),
          OBFUSCATE("30_SeekBar_Start Pos_0_2000"),
          OBFUSCATE("40_SeekBar_End Pos_0_2000"),
      };
    • Add switch cases to change variable values using views:
    • switch (featNum) {
          case 10:
              EnableESP = boolean;
              break;
          case 20:
              ESPLine = boolean;
              break;
          case 30:
              StartPos = value;
              break;
          case 40:
              EndPos = value;
              break;
      }
    • Compile the menu and check if the line appears correctly in the overlay.

You can download the modified source file by clicking here.

linkDownload Links and More
Part 2: Display ESP Line to Players

This tutorial walks you through creating an ESP hack for Unity games like Dude Theft Wars. You’ll learn how to draw lines to players by finding their in-game positions, converting coordinates to the screen, and adjusting for different resolutions. The video also covers preventing crashes when players die by cleaning up memory properly. Whether you’re a beginner or modding other Unity games, these step-by-step methods make it easy to follow. By the end, you’ll have a working ESP that tracks enemies smoothly!

If you're ready, follow these steps:

  1. First, get the object instance (objects are players, vehicles, etc. where ESP elements appear).
    • To get the object instance, find the object class first.
    • Example (using Dude Theft Wars Game), The NPC class is:
      public class ManAI : MonoBehaviour
    • After finding the class, locate the method that runs continuously (usually contains "Update").
      // RVA: 0x6C9C58 Offset: 0x6C9C58 VA: 0x6C9C58
      private void Update() { }
    • Hook this method in DrawingManager.h file to get the player instance:
      void (*old_npcupdate)(void *instance);
      void npcupdate(void *instance) {
          if (instance) {
          
          }
          return old_npcupdate(instance);
      }
    • Initialize the hook in main.cpp file.
      HOOK(targetLibName, str2Offset(OBFUSCATE("0x6C9C58")), npcupdate, old_npcupdate);
  2. Store the unique instances.
    • We can use unordered_set to store unique instances.
    • In the DrawingManager.h file include and heading and namespace:
      #include <unordered_set>
      
      using namespace std;
    • Create a players list variable:
      unordered_set<void *> playerslist;
    • Inside the hook, add this line:
      playerslist.insert(instance);
  3. Add required methods:
    • In DrawingManager.h file, add these methods:
      void *(*get_transform)(void *object);
      Vector3 (*get_position)(void *transform);
      void *(*get_maincamera)();
      Vector3 (*get_worldtoscreenpoint)(void *camera, Vector3 worldpos);
      int (*get_gamewidth)();
      int (*get_gameheight)();
    • Cast them with correct offsets:
      get_transform = (void *(*)(void *)) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1A9A5DC")));
      get_position = (Vector3 (*)(void *)) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1AA3338")));
      get_maincamera = (void *(*)()) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1A95558")));
      get_worldtoscreenpoint = (Vector3 (*)(void *, Vector3)) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1A94CB4")));
      get_gamewidth = (int (*)()) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1A8FA90")));
      get_gameheight = (int (*)()) getAbsoluteAddress(targetLibName, str2Offset(OBFUSCATE("0x1A8FAD8")));
  4. Add screen conversion and ESP drawing functions:
    • In DrawingManager.h file, add these functions:
      Vector2 convertToDeviceScreen(float posX, float posY, float devicewidth, float deviceheight, float gamewidth, float gameheight) {
          return Vector2(posX*devicewidth/gamewidth, deviceheight-(posY*deviceheight/gameheight));
      }
      
      void DrawESP(AadilDrawing esp, int width, int height) {
          int playerssize = playerslist.size();
          if (!EnableESP || playerssize < 1) return;
          if (playerssize > 30) {
              playerslist.clear();
              return;
          }
          void *camera = get_maincamera();
          if (!camera) return;
          int gamewidth = get_gamewidth();
          int gameheight = get_gameheight();
          Vector2 DrawFrom = Vector2(width/2, 0);
          for (void *player : playerslist) {
              if (player) {
                  int health = *(int *)((uintptr_t) player + 0xC);
                  bool isAlive = *(bool *)((uintptr_t) player + 0x24);
                  if (health < 1 || !isAlive) continue;
                  void *transform = get_transform(player);
                  if (transform) {
                      Vector3 worldpos = get_position(transform);
                      Vector3 gamescreenpos = get_worldtoscreenpoint(camera, worldpos);
                      if (gamescreenpos.z > 1) {
                          Vector2 DrawTo = convertToDeviceScreen(gamescreenpos.x, gamescreenpos.y, width, height, gamewidth, gameheight);
                          if (ESPLine) {
                              esp.DrawLine(Color(255,0,0,255), 2.2f, DrawFrom, DrawTo);
                          }
                      }
                  }
              }
          }
      }
  5. Clean up instances properly:
    • Update NPC Hook - Only track living players:
      void (*old_npcupdate)(void *instance);
      void npcupdate(void *instance) {
      if (instance && EnableESP) {
              int health = *(int *)((uintptr_t) instance + 0xC);
              bool isAlive = *(bool *)((uintptr_t) instance + 0x24);
              if (health > 0 && isAlive) playerslist.insert(instance);
          }
          return old_npcupdate(instance);
      }
    • Search for Disable and Destroy method in class:
      // RVA: 0x6C86E0 Offset: 0x6C86E0 VA: 0x6C86E0
      private void OnDisable() { }
      
      // RVA: 0x6C87C8 Offset: 0x6C87C8 VA: 0x6C87C8
      private void OnDestroy() { }
    • Hook both methods and add playerslist.clear() code inside hook:
      void (*old_npcondisable)(void *instance);
      void npcondisable(void *instance) {
          if (instance) {
              playerslist.clear();
          }
          return old_npcondisable(instance);
      }
      
      void (*old_npcondestroy)(void *instance);
      void npcondestroy(void *instance) {
          if (instance) {
              playerslist.clear();
          }
          return old_npcondestroy(instance);
      }
    • Initialize the both hooks in main.cpp file:
      HOOK(targetLibName, str2Offset(OBFUSCATE("0x6C86E0")), npcondisable, old_npcondisable);
      HOOK(targetLibName, str2Offset(OBFUSCATE("0x6C87C8")), npcondestroy, old_npcondestroy);

You can download the modified source file by clicking here.

linkDownload Links and More

Comments

Popular posts from this blog

Among Us v2025.6.10 Fake Impostor Mod Menu Apk v17 [ESP, Teleport, Skins Unlocked, Free Chat etc.] || By Aadil Mods

Step-by-Step Guide to Obtain Key of Mod Menus by Aadil Mods

Mini Militia v5.6.0 Aimbot/ESP Mod Menu Apk v5.1 | Speed Hack, Wall Hack, God Mod, Teleport etc.