pythong实现窗口移动点击
Python自动化控制微信小游戏《疯狂水世界》技术笔记
1. 概述
本文档整理了关于使用Python对微信小游戏《疯狂水世界》进行自动化操作的技术探讨,涵盖窗口控制、模拟点击、图像识别、抓包分析及虚拟环境使用等内容。
2. 环境配置
2.1 虚拟环境
虚拟环境是一个独立的Python运行环境,包含独立的Python解释器、pip工具及第三方库。
虚拟环境目录结构:
G:\ProgramFiles\pythonvenv\shuiyx\
├── Scripts\
│ ├── python.exe # 虚拟环境的Python解释器
│ ├── pip.exe # 虚拟环境的pip
│ └── activate.bat # 激活脚本
└── Lib\
└── site-packages\ # 所有安装的第三方库核心逻辑:使用哪个路径的python.exe,就对应哪个环境。虚拟环境中的python.exe自动关联该环境Lib\site-packages下的所有库。
直接使用虚拟环境运行脚本:
G:\ProgramFiles\pythonvenv\shuiyx\Scripts\python.exe s.py2.2 依赖库安装
在虚拟环境中安装所需库:
pip install pywin32
pip install pyautogui
pip install pillow
pip install opencv-python
pip install pytesseract注意:win32gui、win32con、win32api都是pywin32库的组成部分,无需单独安装。
3. 窗口控制
3.1 查找游戏窗口
import win32gui
def find_game_window():
"""查找疯狂水世界窗口"""
def callback(hwnd, hwnds):
if win32gui.IsWindowVisible(hwnd):
title = win32gui.GetWindowText(hwnd)
if "疯狂水世界" in title or "微信小游戏" in title:
hwnds.append(hwnd)
return True
hwnds = []
win32gui.EnumWindows(callback, hwnds)
return hwnds[0] if hwnds else None3.2 获取窗口位置和大小
def get_window_info():
"""获取当前窗口的位置和大小"""
hwnd = find_game_window()
if not hwnd:
return None
rect = win32gui.GetWindowRect(hwnd)
x, y = rect[0], rect[1]
width = rect[2] - rect[0]
height = rect[3] - rect[1]
print(f"窗口位置: ({x}, {y})")
print(f"窗口大小: {width}x{height}")
return {'x': x, 'y': y, 'width': width, 'height': height}3.3 移动窗口并调整大小
import win32gui
import win32con
import win32api
import time
def move_and_resize_window(x, y, width, height):
"""移动窗口到指定位置并调整大小"""
hwnd = find_game_window()
if not hwnd:
print("未找到窗口")
return False
# 如果窗口最小化,先恢复
if win32gui.IsIconic(hwnd):
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
time.sleep(0.3)
# 将窗口置于前台
try:
win32gui.SetForegroundWindow(hwnd)
except:
pass
# 执行移动和缩放
win32gui.MoveWindow(hwnd, x, y, width, height, True)
# 验证结果
time.sleep(0.2)
rect = win32gui.GetWindowRect(hwnd)
actual_x, actual_y = rect[0], rect[1]
actual_w = rect[2] - rect[0]
actual_h = rect[3] - rect[1]
print(f"窗口已移到 ({actual_x}, {actual_y}),大小 {actual_w}x{actual_h}")
return True3.4 保存和恢复窗口配置
使用JSON配置文件保存窗口位置,便于下次恢复:
import json
import os
CONFIG_FILE = "window_config.json"
def save_config(x, y, width, height):
"""保存窗口配置到文件"""
config = {
'x': x,
'y': y,
'width': width,
'height': height,
'saved_at': time.strftime('%Y-%m-%d %H:%M:%S')
}
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config, f, indent=2, ensure_ascii=False)
def load_config():
"""从文件加载窗口配置"""
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return None4. 模拟点击
4.1 基于屏幕坐标的点击
使用pyautogui库:
import pyautogui
# 点击指定坐标
pyautogui.click(x, y)
# 带随机延迟的点击(模拟人类操作)
import random
time.sleep(random.uniform(0.2, 0.5))
pyautogui.click(x, y)4.2 后台发送点击消息
使用win32api向指定窗口发送鼠标消息,不依赖前台光标位置:
import win32api
import win32con
def send_click_to_window(hwnd, x, y):
"""
向指定窗口发送点击消息
x, y: 相对于窗口客户区的坐标
"""
# 将坐标转换为lParam
lParam = win32api.MAKELONG(x, y)
# 发送鼠标按下消息
win32api.SendMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
time.sleep(0.05)
# 发送鼠标弹起消息
win32api.SendMessage(hwnd, win32con.WM_LBUTTONUP, 0, lParam)5. 图像识别与数值判断
5.1 截取指定区域
import pyautogui
from PIL import Image
def capture_region(region):
"""
截取屏幕指定区域
region: (x, y, width, height)
"""
screenshot = pyautogui.screenshot(region=region)
return screenshot5.2 OCR识别数字
使用pytesseract库识别图片中的数字:
import pytesseract
from PIL import Image
# 设置Tesseract路径(如果不在系统PATH中)
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
def get_number_from_region(region):
"""从指定区域识别数字"""
screenshot = pyautogui.screenshot(region=region)
screenshot = screenshot.convert('L') # 转为灰度图
# 只识别数字
config = '--psm 8 --oem 3 -c tessedit_char_whitelist=0123456789.'
text = pytesseract.image_to_string(screenshot, config=config)
import re
numbers = re.findall(r'\d+\.?\d*', text)
if numbers:
return float(numbers[0])
return None5.3 带图像预处理的识别(提高准确率)
import cv2
import numpy as np
def get_number_with_preprocessing(region):
"""带图像预处理的数字识别"""
screenshot = pyautogui.screenshot(region=region)
img = np.array(screenshot)
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
# 二值化
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 放大图像
resized = cv2.resize(binary, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
text = pytesseract.image_to_string(
resized,
config='--psm 8 -c tessedit_char_whitelist=0123456789.'
)
return text.strip()5.4 条件判断与点击
def check_and_click(value_region, click_pos, threshold):
"""
识别数值,如果小于阈值则点击
"""
current_value = get_number_from_region(value_region)
if current_value is not None:
print(f"当前数值: {current_value}")
if current_value < threshold:
print(f"数值 {current_value} 小于阈值 {threshold},执行点击")
pyautogui.click(click_pos[0], click_pos[1])
return True
return False6. 大地图滚动
6.1 通过鼠标拖拽滚动地图
def scroll_map(dx, dy, duration=0.5):
"""
通过鼠标拖拽滚动地图
dx, dy: 拖拽偏移量
"""
pyautogui.dragRel(dx, dy, duration=duration, button='left')
time.sleep(0.5) # 等待滚动完成6.2 带工厂定位的滚动方案
如果多个工厂分布在地图不同位置,可以预先配置每个工厂的滚动偏移量:
FACTORY_SCROLL = {
"工厂A": (0, 0),
"工厂B": (-500, 0),
"工厂C": (-1000, 0),
}
def scroll_to_factory(factory_name):
"""滚动到指定工厂位置"""
if factory_name in FACTORY_SCROLL:
dx, dy = FACTORY_SCROLL[factory_name]
pyautogui.dragRel(dx, dy, duration=0.3, button='left')
time.sleep(0.5)7. 自动化逻辑架构
7.1 数据结构设计
使用嵌套字典定义工厂和产品位置:
GAME_LAYOUT = {
"工厂A": {
"products": [
{"name": "产品1", "value_region": (100, 200, 50, 30), "click_pos": (120, 250)},
{"name": "产品2", "value_region": (160, 200, 50, 30), "click_pos": (180, 250)},
# ... 共7-8个产品
]
},
"工厂B": {
"products": [
# ...
]
},
# ... 共8-9个工厂
}7.2 单个工厂处理逻辑
def process_factory_once(factory_name, factory_data, product_counts):
"""
对一个工厂执行一轮操作:
遍历所有产品,如果未满则点击一次
返回是否所有产品都已满
"""
print(f"进入 {factory_name}")
# 滚动到该工厂
scroll_to_factory(factory_name)
all_full = True
for product in factory_data["products"]:
key = f"{factory_name}_{product['name']}"
current_count = product_counts.get(key, 0)
if current_count < 6: # 最大队列6个
# 识别数值并判断是否需要点击
value = get_number_from_region(product["value_region"])
if value is None or value < 55:
pyautogui.click(product["click_pos"][0], product["click_pos"][1])
product_counts[key] = current_count + 1
all_full = False
time.sleep(random.uniform(0.2, 0.5))
else:
print(f"产品 {product['name']} 已满")
return all_full7.3 多工厂并行轮询逻辑
核心思想是每次只处理最多3个工厂,每个工厂各点一轮产品,然后等待生产完成:
def main_loop():
"""主循环:轮询处理多个工厂"""
MAX_PARALLEL = 3
MAX_QUEUE = 6
PRODUCE_INTERVAL = 15 # 每个产品生产时间,需实测
product_counts = defaultdict(int)
factory_list = list(FACTORIES.keys())
round_num = 1
while True:
print(f"第 {round_num} 轮循环")
# 选择本轮需要处理的工厂(最多3个且未满)
factories_to_process = []
for factory in factory_list:
if len(factories_to_process) >= MAX_PARALLEL:
break
# 检查该工厂是否全部已满
all_full = True
for product in FACTORIES[factory]["products"]:
key = f"{factory}_{product['name']}"
if product_counts.get(key, 0) < MAX_QUEUE:
all_full = False
break
if not all_full:
factories_to_process.append(factory)
if not factories_to_process:
print("所有工厂已满")
break
# 逐个处理工厂
for factory_name in factories_to_process:
process_factory_once(factory_name, FACTORIES[factory_name], product_counts)
# 等待生产
wait_time = PRODUCE_INTERVAL + random.uniform(-2, 3)
print(f"等待 {wait_time:.1f} 秒")
time.sleep(max(1, wait_time))
round_num += 18. 批处理运行
8.1 直接使用虚拟环境Python运行
创建run.bat文件:
@echo off
G:\ProgramFiles\pythonvenv\shuiyx\Scripts\python.exe s.py
pause8.2 带菜单选择的批处理
@echo off
chcp 65001 >nul
title 疯狂水世界 - 窗口控制
echo ========================================
echo 疯狂水世界 - 窗口控制脚本
echo ========================================
echo.
echo [1] 保存当前窗口配置
echo [2] 恢复到保存的窗口配置
echo [3] 运行完整自动化
echo [0] 退出
echo.
set /p choice="请选择 (0-3): "
if "%choice%"=="1" goto save
if "%choice%"=="2" goto restore
if "%choice%"=="3" goto full
if "%choice%"=="0" exit
:save
G:\ProgramFiles\pythonvenv\shuiyx\Scripts\python.exe s.py save
pause
exit
:restore
G:\ProgramFiles\pythonvenv\shuiyx\Scripts\python.exe s.py
pause
exit
:full
G:\ProgramFiles\pythonvenv\shuiyx\Scripts\python.exe full_auto.py
pause
exit9. 抓包分析方案
9.1 工具
- Charles:中间人代理抓包工具
- Burp Suite:请求重放与调试工具
9.2 基本流程
- 配置Charles代理(默认端口8888)
- 手机设置Wi-Fi代理为电脑IP和8888端口
- 安装并信任Charles SSL证书(用于解密HTTPS)
- 启用SSL代理(监听
*:443) - 在游戏中执行点击操作,捕获相关请求
- 分析请求URL、Headers和Body
- 使用Python的requests库重放请求
9.3 风险提示
- 直接发送网络请求是游戏公司严厉打击的作弊行为
- 封号风险极高
- 如果请求参数包含加密签名(如sign),需要逆向分析生成规则
10. Cheat Engine内存修改方案
10.1 原理
Cheat Engine通过扫描和修改进程内存数据来实现修改。
10.2 局限性
- 《疯狂水世界》是联网游戏,关键数据存储在服务器端
- 本地内存修改后,服务器会下发正确数据覆盖
- 微信小游戏平台有代码加固和动态反作弊机制
10.3 结论
对于《疯狂水世界》这类联网小游戏,内存修改方案基本无效且风险极高,不建议尝试。
11. 风险提示
所有自动化方案均存在以下风险:
- 违反微信小游戏用户协议
- 可能导致游戏账号被封禁
- 建议使用小号测试,不在主账号上尝试
- 操作频率加入随机延迟可降低被检测风险
12. 附录:常用函数速查
| 功能 | 函数/方法 |
|---|---|
| 查找窗口 | win32gui.FindWindow() / win32gui.EnumWindows() |
| 移动窗口 | win32gui.MoveWindow() |
| 获取窗口位置 | win32gui.GetWindowRect() |
| 恢复最小化窗口 | win32gui.ShowWindow(hwnd, win32con.SW_RESTORE) |
| 屏幕截图 | pyautogui.screenshot() |
| 模拟点击 | pyautogui.click(x, y) |
| 鼠标拖拽 | pyautogui.dragRel(dx, dy) |
| OCR识别 | pytesseract.image_to_string() |
| 发送后台点击 | win32api.SendMessage(hwnd, WM_LBUTTONDOWN, ...) |
| 获取屏幕分辨率 | win32api.GetSystemMetrics(win32con.SM_CXSCREEN) |