Last active
May 26, 2026 21:14
-
-
Save daverave1212/7d33bfcb67292ddb210d235d763341d7 to your computer and use it in GitHub Desktop.
A Python script for dragging camera in RTS games that don't have that feature (e.g. Age of Empires 2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ''' | |
| What This Is | |
| This is a python script that enables you to drag-move the camera with the middle mouse button. | |
| How To Install (Windows) | |
| 1. Install python (https://www.python.org/ and go to Downloads and Download for Windows) | |
| 2. Open windows search, search for Command Prompt. Right click it > Run as administrator > Yes | |
| 3. Type the following command: | |
| pip install mouse keyboard | |
| How To Run (Windows) | |
| 1. Save this script in C:/ | |
| 2. Open windows search, search for Command Prompt. Right click it > Run as administrator > Yes | |
| 3. Type the following commands: | |
| cd C:/ | |
| python .\camera_drag.py | |
| You can run the script while the game is running or before. | |
| To end the script, press the End key on your keyboard, or close the Command Prompt. | |
| IMPORTANT: For any game you play, make sure you have ENABLED ARROW KEYS to move the camera, and the MIDDLE MOUSE BUTTON is NOT set to anything else! | |
| In your game's settings, you can also change the arrows scroll speed if the camera drag moves too slowly or too fast for you. | |
| How It Works | |
| It detects your middle mouse clicks and does some simple math to press the arrow keys as long as you drag the camera. | |
| If It Doesn't Work | |
| If this doesn't work, check the Command Prompt for any errors. Copy all the code and paste it to ChatGPT or your favorite AI, and tell him what error you got in the console. It will help you fix it. | |
| ''' | |
| import time | |
| import threading | |
| import ctypes | |
| import keyboard | |
| import mouse | |
| # ========================= | |
| # SETTINGS | |
| # ========================= | |
| SCROLL_SPEED = 10000 # camera units per second (tune this) | |
| DEADZONE = 2 | |
| LOOP_SLEEP = 1 / 60 # 60 loops per second | |
| VK_MBUTTON = 0x04 | |
| # ========================= | |
| running = True | |
| dragging = False | |
| last_pressed = False | |
| start_x = 0 | |
| start_y = 0 | |
| cam_x = 0.0 | |
| cam_y = 0.0 | |
| def current_milli_time(): | |
| return round(time.time() * 1000) | |
| def is_middle_down(): | |
| return ctypes.windll.user32.GetAsyncKeyState(VK_MBUTTON) & 0x8000 != 0 | |
| def release_all(): | |
| keyboard.release("left") | |
| keyboard.release("right") | |
| keyboard.release("up") | |
| keyboard.release("down") | |
| def press_only(x, y): | |
| # horizontal | |
| if x > DEADZONE: | |
| keyboard.press("left") | |
| keyboard.release("right") | |
| elif x < -DEADZONE: | |
| keyboard.press("right") | |
| keyboard.release("left") | |
| else: | |
| keyboard.release("left") | |
| keyboard.release("right") | |
| # vertical | |
| if y > DEADZONE: | |
| keyboard.press("up") | |
| keyboard.release("down") | |
| elif y < -DEADZONE: | |
| keyboard.press("down") | |
| keyboard.release("up") | |
| else: | |
| keyboard.release("up") | |
| keyboard.release("down") | |
| last_frame_timestamp = current_milli_time() | |
| def loop(): | |
| global dragging, last_pressed | |
| global start_x, start_y | |
| global cam_x, cam_y | |
| global last_frame_timestamp | |
| while running: | |
| this_frame_timestamp = current_milli_time() | |
| pressed = is_middle_down() | |
| # --- start drag --- | |
| if pressed and not last_pressed: | |
| dragging = True | |
| start_x, start_y = mouse.get_position() | |
| cam_x = 0 | |
| cam_y = 0 | |
| # --- end drag --- | |
| if not pressed and last_pressed: | |
| dragging = False | |
| release_all() | |
| last_pressed = pressed | |
| # --- main logic --- | |
| if dragging: | |
| mx, my = mouse.get_position() | |
| # desired offset from start | |
| target_x = mx - start_x | |
| target_y = my - start_y | |
| # remaining distance we still need to "consume" | |
| diff_x = target_x - cam_x | |
| diff_y = target_y - cam_y | |
| # step toward target based on speed | |
| step = SCROLL_SPEED * (this_frame_timestamp - last_frame_timestamp) / 1000 | |
| # clamp step so we don't overshoot | |
| if abs(diff_x) < step: | |
| cam_x = target_x | |
| else: | |
| cam_x += step if diff_x > 0 else -step | |
| if abs(diff_y) < step: | |
| cam_y = target_y | |
| else: | |
| cam_y += step if diff_y > 0 else -step | |
| press_only(diff_x, diff_y) | |
| last_frame_timestamp = this_frame_timestamp | |
| time.sleep(LOOP_SLEEP) | |
| thread = threading.Thread(target=loop, daemon=True) | |
| thread.start() | |
| print("Drag-scroll running. You can now enter the game.") | |
| print("Hold MIDDLE MOUSE and drag.") | |
| print("Press END to exit.") | |
| keyboard.wait("end") | |
| running = False | |
| release_all() | |
| print("Stopped cleanly.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment