Game Development 10 min read

Python-Based Non-Intrusive Recoil Compensation for PUBG Using Image Recognition

This article explains how to create a Python script that automatically compensates weapon recoil in PUBG by capturing the screen, recognizing equipment with OpenCV and SSIM, and moving the mouse via pynput and pydirectinput without modifying the game memory.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Python-Based Non-Intrusive Recoil Compensation for PUBG Using Image Recognition

The tutorial describes a non‑intrusive method to control recoil in PUBG by using image recognition instead of memory manipulation, ensuring the approach remains undetectable.

Prerequisites : knowledge of the game's various weapons, their recoil patterns, and basic Python libraries such as pynput , pyautogui , opencv , and pydirectinput .

1. Listening to Keyboard and Mouse

import pynput.keyboard as keyboard

# Listener for keyboard events
def listen_keybord():
    listener = keyboard.Listener(on_press=onPressed, on_release=onRelease)
    listener.start()

The listener runs asynchronously; long‑running handlers should be off‑loaded to avoid blocking.

2. Handling Key Releases

def onRelease(key):
    try:
        if '1' == key.char:
            c_equipment.switch = 1  # main weapon 1
        elif '2' == key.char:
            c_equipment.switch = 2
        elif '3' == key.char:
            c_equipment.switch = 3  # pistol, no recoil compensation
        elif '4' == key.char:
            c_equipment.switch = 3  # knife
        elif '5' == key.char:
            c_equipment.switch = 3  # grenade
    except AttributeError:
        if 'tab' == key.name:
            asyncHandle()  # open backpack and start screen capture
        elif 'num_lock' == key.name:
            changeOpen()   # toggle script on/off
        elif 'shift' == key.name:
            c_contants.hold = False

Special keys like tab trigger asynchronous equipment detection, while num_lock controls the script state.

3. Capturing the Screen

import pyautogui

def adaptive_binarization(img):
    # Adaptive thresholding for binarization
    maxval = 255
    blockSize = 3
    C = 5
    img2 = cv2.adaptiveThreshold(img, maxval, cv2.ADAPTIVE_THRESH_MEAN_C,
                                 cv2.THRESH_BINARY, blockSize, C)
    return img2

def shotCut(x, y, w, h):
    im = pyautogui.screenshot(region=[x, y, w, h])
    screen = cv2.cvtColor(numpy.asarray(im), cv2.COLOR_BGR2GRAY)
    temp = adaptive_binarization(screen)
    return temp

def saveScreen():
    screen1 = shotCut(1780, 125, 614, 570)
    cv2.imwrite("./resource/shotcut/screen.bmp", screen1)

The captured region corresponds to the equipment panel; the image is converted to grayscale and binarized for easier comparison.

4. Preparing Reference Images

Before comparison, a collection of template images for weapon names, stocks, grips, and muzzle devices must be stored in a directory (e.g., ./resource/guns/ ).

5. Cropping the Captured Panel

# Load the previously saved screenshot
screen = cv2.imread("./resource/shotcut/screen.bmp", 0)
# Crop the weapon‑1 name area (y:0‑40, x:45‑125)
screenWepon1 = screen[0:40, 45:125]
# Compare with reference images to obtain the name
w1Name = compareAndGetName(screenWepon1, "./resource/guns/")

6. Image Comparison Using SSIM

def compareAndGetName(screenImg, dir):
    content = os.listdir(dir)
    name = 'none'
    max_score = 0
    for fileName in content:
        curWepone = cv2.imread(dir + fileName, 0)
        res = calculate_ssim(numpy.asarray(screenImg), numpy.asarray(curWepone))
        if max_score < res and res > 0.5:
            max_score = res
            name = fileName[:-4]
    return name

The Structural Similarity Index (SSIM) measures how closely the cropped screenshot matches each template; the highest score above 0.5 is selected.

7. SSIM Helper Function

def calculate_ssim(img1, img2):
    if not img1.shape == img2.shape:
        raise ValueError('Input images must have the same dimensions.')
    if img1.ndim == 2:
        return ssim(img1, img2)
    elif img1.ndim == 3:
        if img1.shape[2] == 3:
            ssims = []
            for i in range(3):
                ssims.append(ssim(img1, img2))
            return numpy.array(ssims).mean()
        elif img1.shape[2] == 1:
            return ssim(numpy.squeeze(img1), numpy.squeeze(img2))
    else:
        raise ValueError('Wrong input image dimensions.')

After obtaining the weapon name, the script can also retrieve attached accessories using the same workflow.

8. Moving the Mouse to Compensate Recoil

def moveMouse():
    curWepone = getCurrentWepone()
    if curWepone.name == 'none':
        return
    basic = curWepone.basic
    speed = curWepone.speed
    startTime = round(time.perf_counter(), 3) * 1000
    for i in range(curWepone.maxBullets):
        if not canFire():
            break
        holdK = 1.0
        if c_contants.hold:
            holdK = curWepone.hold
        moveSum = int(round(basic[i] * curWepone.k * holdK, 2))
        while True:
            if moveSum > 10:
                pydirectinput.move(xOffset=0, yOffset=10, relative=True)
                moveSum -= 10
            elif moveSum > 0:
                pydirectinput.move(xOffset=0, yOffset=moveSum, relative=True)
                moveSum = 0
            elapsed = (round(time.perf_counter(), 3) * 1000 - startTime)
            if not canFire() or elapsed > (i + 1) * speed + 10:
                break
            time.sleep(0.01)

The function reads the recoil pattern (e.g., a list of y‑axis offsets) for the current weapon, applies a hold‑modifier if the player is aiming, and moves the mouse in small increments to match the required compensation within each bullet interval.

9. Final Remarks

The complete source code is hosted on Gitee (https://gitee.com/lookoutthebush/PUBG). Readers are encouraged to clone the repository, test the script, and adapt the recoil data for other weapons or games.

PythonImage RecognitionopencvpynputGame AutomationPUBGRecoil Compensation
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.