Python游戏自动化基础与模拟器:用pywin32与win32gui把窗口操作改到TaoToken
1. 从窗口句柄到模拟器自动化Python 游戏自动化入门的第一道坎很多人第一次接触 Python 游戏自动化脑子里想的都是“识别画面、自动点击、解放双手”结果真正动手才发现第一步就卡住了模拟器窗口到底怎么定位鼠标点过去为什么点偏了截图截出来为什么是黑屏或者错位这些问题不解决后面写再多识别逻辑都是空中楼阁。我自己刚开始折腾模拟器自动化的时候用的就是 pywin32 这套 Windows API 方案。它的核心思路很朴素每个窗口在操作系统里都有一个唯一的整数编号叫窗口句柄HWND。拿到这个句柄你就能查它的位置、把它激活到前台、截它的画面、往它上面发鼠标键盘消息。模拟器本质上就是一个 Windows 窗口所以这套方法天然适用。这篇文章要交付的是一个能跑通的闭环从安装 pywin32到用 win32gui 找到模拟器窗口句柄再到激活窗口、截图、模拟点击和滑动最后把脚本里需要调用大模型做画面理解或决策的 API endpoint 改到 TaoToken 并验证连通性。适合谁看适合有 Python 基础、想入门游戏自动化、但被窗口操作卡住的同学。你不需要懂太多 Windows 底层跟着代码走就行。先说清楚一个概念句柄就像窗口的身份证号。名字可以重复你叫“雷电模拟器”别人装个别的模拟器也可能叫类似的名字但句柄是唯一的整数。操作系统靠这个编号给窗口发消息我们靠这个编号精准操作目标窗口。理解了这一点后面的 FindWindow、SetForegroundWindow、ClientToScreen 就都好理解了。2. TaoToken 前置准备把 API endpoint 统一到 https://taotoken.net/api在游戏自动化里什么时候会用到 API举几个真实场景你截了一张模拟器画面想让模型帮你判断“现在屏幕上有没有出现某个按钮”或者你想让模型根据当前局面给出下一步点击坐标又或者你在写一个 Agent 式的自动化流程需要模型做多步决策。这些都需要调用大模型接口。问题在于很多同学本地脚本里 endpoint 写得很乱今天用这个地址明天换那个地址密钥也散落在各个文件里。我的做法是统一到一个入口TaoToken 的 API 地址是https://taotoken.net/api官网是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。你可以在控制台创建 API Key然后所有脚本都读同一个环境变量。具体操作路径是这样的先打开官网注册登录进入控制台页面创建密钥。控制台地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理页是https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。创建完把 Key 复制出来不要硬编码在代码里用环境变量存。我习惯在项目根目录放一个.env文件内容大概是这样TAOTOKEN_API_KEYsk-你的密钥 TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在 Python 里用os.getenv读取。这样做的好处是以后换机器、换项目只要改环境变量就行代码不用动。如果你用的是 Cline、CC Switch 这类工具配置逻辑也一样Base URL 填https://taotoken.net/apiKey 填你创建的密钥Model ID 填你实际要用的模型名。这三件套缺一不可很多人报 401 就是因为 Key 没填对或者 Base URL 写成了别的地址。还有一点要注意TaoToken 是合规的 API 聚合入口不是让你去搞什么灰色操作。你用它来调用模型做画面理解、文本决策都是正常的开发行为。密钥要保管好不要提交到公开仓库。3. 可复制配置pywin32 环境搭建与窗口操作代码片段这一节直接给可复制的配置和代码。先装依赖pip install pywin32 numpy opencv-python装完可以用pip list确认 pywin32 在列表里。如果安装过程中报错多半是 Python 版本和 pywin32 版本不匹配换一个稳定版本即可。接下来是窗口查找与激活的代码。我把它保存为window_utils.py后面所有脚本都 import 它import time import win32gui import win32con import win32api import win32ui import numpy as np def activate_window(title): 根据窗口标题查找并激活窗口返回句柄找不到返回 0 hwnd win32gui.FindWindow(None, title) if hwnd 0: print(f未找到窗口{title}) return 0 if win32gui.GetForegroundWindow() ! hwnd: win32gui.SetForegroundWindow(hwnd) time.sleep(1) return hwnd def activate_emulator(title雷电模拟器): return activate_window(title) def capture_screen(hwnd): 根据窗口句柄截图返回 numpy 数组h, w, 4 left, top, right, bottom win32gui.GetWindowRect(hwnd) w, h right - left, bottom - top desktop win32gui.GetDesktopWindow() dc win32gui.GetWindowDC(desktop) mfc_dc win32ui.CreateDCFromHandle(dc) save_dc mfc_dc.CreateCompatibleDC() save_bit_map win32ui.CreateBitmap() save_bit_map.CreateCompatibleBitmap(mfc_dc, w, h) save_dc.SelectObject(save_bit_map) save_dc.BitBlt((0, 0), (w, h), mfc_dc, (left, top), win32con.SRCCOPY) signed_ints_array save_bit_map.GetBitmapBits(True) im_opencv np.frombuffer(signed_ints_array, dtypeuint8) im_opencv.shape (h, w, 4) save_dc.DeleteDC() win32gui.DeleteObject(save_bit_map.GetHandle()) win32gui.ReleaseDC(hwnd, dc) return im_opencv def click_desktop(x, y): win32api.SetCursorPos((x, y)) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) def click_window(hwnd, x, y): desktop_coord win32gui.ClientToScreen(hwnd, (x, y)) click_desktop(*desktop_coord) def swipe_up(hwnd, start_x, start_y, distance): desktop_coord win32gui.ClientToScreen(hwnd, (start_x, start_y)) win32api.SetCursorPos(desktop_coord) time.sleep(0.1) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) time.sleep(0.1) for i in range(1, distance 1, 10): track (start_x, start_y - i) dc win32gui.ClientToScreen(hwnd, track) win32api.SetCursorPos(dc) time.sleep(0.01) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)如果你用 Cline 或 CC Switch 做辅助开发配置里同样要写全三件套。比如 Cline 的 MCP 配置片段{ mcpServers: { taotoken: { url: https://taotoken.net/api, apiKey: sk-你的密钥, model: 你的模型ID } } }Codex 的auth.json也是类似逻辑Base URL 指向https://taotoken.net/apiKey 填进去Model ID 写清楚。这三样少一个都会报错。4. 验证请求截图 API 连通性测试跑通代码写好了得验证。第一步验证窗口操作打开模拟器运行下面这段import window_utils as wu hwnd wu.activate_emulator(雷电模拟器) print(句柄, hwnd) if hwnd: img wu.capture_screen(hwnd) print(截图形状, img.shape) wu.click_window(hwnd, 326, 15)如果打印出的句柄是一个非零整数截图形状类似(1280, 720, 4)并且鼠标点到了模拟器右上角菜单说明窗口操作链路通了。这里有个坑电脑显示设置的“缩放与布局”必须是 100%否则截图会错位。我踩过这个坑截图出来总是偏一块查了半天才发现是缩放问题。第二步验证 API 连通性。写一个最小请求import os import requests api_key os.getenv(TAOTOKEN_API_KEY) base_url os.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api) headers { Authorization: fBearer {api_key}, Content-Type: application/json } payload { model: 你的模型ID, messages: [ {role: user, content: 回复两个字连通} ] } resp requests.post(f{base_url}/v1/chat/completions, headersheaders, jsonpayload, timeout30) print(状态码, resp.status_code) print(返回, resp.text[:200])状态码 200 且返回内容里有模型回复就说明 endpoint 改到 TaoToken 后连通正常。如果返回 401检查 Key 是否复制完整、有没有多余空格如果返回 404检查 Base URL 是不是写成了https://taotoken.net/api而不是别的路径。验证模型对话也可以直接在模型对话页测试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite。把截图和 API 结合起来就是一个完整的“截图 → 发给模型 → 模型返回决策 → 脚本执行点击”的闭环。你可以先让模型描述截图里有什么确认它能看懂画面再逐步让它输出坐标。5. 本篇常见错误排查401、local proxy failed、reading choices、OAuth这一节对照真实报错来排。第一个高频错误是401 Unauthorized。原因通常是 Key 没填、填错、或者环境变量没生效。排查方法在 Python 里print(os.getenv(TAOTOKEN_API_KEY))看是不是 None。如果是 None说明.env没加载需要手动load_dotenv()或者直接在终端 export。第二个是local proxy failed。这个报错通常出现在你本地网络环境有代理设置但请求走不通。注意这里说的是本地开发环境的网络配置问题不是让你去搞什么特殊网络工具。排查方法检查系统代理设置确认请求能正常到达https://taotoken.net/api。如果你在公司内网可能需要找网管确认出口策略。第三个是reading choices相关报错比如KeyError: choices。这通常是因为返回体不是标准的 chat completions 格式可能是请求路径写错了或者模型 ID 不存在。排查方法把resp.text完整打印出来看返回的 JSON 结构。如果里面有error字段按错误信息改。第四个是 OAuth 相关报错。如果你用 Claude Code 或类似工具接入配置里可能需要填 Anthropic 兼容的 endpoint。这时候 Base URL 依然用https://taotoken.net/api但路径可能要根据工具要求调整。Claude Code 的接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有详细的配置说明。如果工具提示 OAuth 失败先确认你用的是 API Key 模式而不是 OAuth 模式两者不能混。还有一个容易忽略的窗口句柄返回 0。这不一定是代码问题可能是模拟器窗口标题变了。比如某些版本标题是“雷电模拟器-1”或者带其他后缀。解决方法用win32gui.EnumWindows遍历所有窗口打印标题找到准确的标题字符串。6. 长期编码与 Agent 场景把自动化脚本接到 Coding Plan如果你只是偶尔跑个脚本按上面的方式用 API Key 就够了。但如果你在做一个长期的游戏自动化项目或者想把它做成一个能持续迭代的 Agent那建议了解一下 Coding Plan。Coding Plan 的入口是https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite它更适合需要长期调用、多轮对话、代码辅助的场景。为什么提这个因为游戏自动化写到后面你会发现单纯靠硬编码坐标很脆弱。模拟器一更新、分辨率一改坐标全废。更好的做法是让模型参与决策截图发给模型模型返回“点击某个区域”或“执行某个动作”脚本再翻译成鼠标操作。这种模式对 API 的稳定性和调用量都有要求Coding Plan 在这类场景下更合适。另外如果你用 Claude Code 做开发辅助它的 Anthropic 兼容接入方式在文档里有说明https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-code-anthropicutm_campaignrewrite。配置时同样记住三件套Base URL、Key、Model ID。Base URL 统一用https://taotoken.net/api不要自己拼别的路径。最后给一个实用建议把窗口操作和 API 调用解耦。窗口操作放在window_utils.pyAPI 调用放在ai_client.py主脚本只负责编排流程。这样以后换模型、换模拟器改动范围都很小。我试过把截图函数和 API 请求写在一个文件里后来想换模型时改得头大解耦之后清爽很多。到这里从环境搭建到首个自动化脚本跑通的闭环就完成了。你可以先跑通截图和点击再接入 API 做画面理解最后把整个流程串起来。遇到报错就对照第 5 节排查大部分问题都能定位到具体原因。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →