
1. 项目概述为什么用Python和TagUI来“挂”《咸鱼之王》如果你玩过《咸鱼之王》这类放置类手游肯定对“挂机”这个核心玩法又爱又恨。爱的是它解放了双手恨的是为了最大化收益你依然需要时不时点开游戏领取离线奖励、完成日常任务、挑战BOSS甚至半夜还得爬起来收个菜。这种重复、定时、规律的操作简直就是为自动化量身定做的场景。手动操作不仅枯燥还容易忘记影响游戏进度和资源积累。这时候RPA机器人流程自动化技术就能派上大用场。RPA的核心思想是模拟人在电脑前的操作——移动鼠标、点击、输入文字、识别屏幕内容。我们这次要做的就是用Python驱动一个轻量级但功能强大的RPA工具TagUI来打造一个专属的《咸鱼之王》桌面端挂机脚本。你可能会问为什么是PythonTagUI而不是纯Python图像识别或者更复杂的Selenium这里面的选择有讲究。首先Python是胶水语言生态丰富但纯用PyAutoGUI这类库做图像识别对新手来说调试复杂容错率低。而TagUI本身就是一个用JavaScript写的RPA工具但它提供了Python接口tagui包让我们可以用Python的逻辑来组织和控制自动化流程结合了两者的优势。TagUI内置了视觉识别通过SikuliX引擎和OCR光学字符识别功能这意味着我们不需要自己训练复杂的AI模型就能让脚本“看懂”屏幕上哪里是“领取”按钮哪里显示了“金币数量”。对于游戏界面这种元素相对固定、但可能因缩放或主题略有变化的场景视觉识别比基于元素坐标的绝对点击要稳定得多。这个项目的目标很明确编写一个脚本能自动完成《咸鱼之王》中的一系列日常挂机操作比如登录、领取各种奖励、挑战副本、使用道具等。最终实现“一键启动全天托管”让你从重复劳动中彻底解放出来把时间和精力花在更有趣的游戏策略或现实生活中。下面我们就从环境搭建开始一步步拆解如何实现这个自动化挂机机器人。2. 环境准备与核心工具链搭建工欲善其事必先利其器。在开始写代码之前我们需要把整个自动化环境搭建起来。这个过程稍微有点繁琐但每一步都是后续稳定运行的基础。2.1 Python环境与TagUI安装首先确保你的电脑上安装了Python。推荐使用Python 3.7到3.9版本兼容性最好。你可以从Python官网下载安装包记得安装时勾选“Add Python to PATH”这样可以在命令行中直接使用python命令。安装好Python后我们通过pip安装TagUI的Python包。打开你的命令行终端CMD或PowerShell输入以下命令pip install tagui这个命令会安装tagui这个Python库它本质上是TagUI核心工具的一个封装器。但是仅仅安装这个Python包是不够的因为它只是一个接口。TagUI的运行依赖于其原生环境包括Java和SikuliX。接下来是关键的一步安装TagUI原生环境。我们需要从TagUI的GitHub发布页面下载其预打包的完整版本。你可以手动去GitHub搜索“tagui releases”下载对应你操作系统Windows/macOS/Linux的ZIP包。这里我提供一个更自动化的方法在Python脚本里完成下载和设置。不过最稳妥的方式还是手动下载并解压到一个你容易找到的路径比如C:\tagui或D:\tools\tagui。注意TagUI的运行依赖Java运行时环境JRE。请确保你的系统已经安装了Java 8或更高版本。可以在命令行输入java -version来检查。如果没有安装需要先去Oracle官网或Adoptium网站下载安装。假设你已经将TagUI解压到了D:\tagui目录。接下来需要设置一个重要的环境变量TAGUI_HOME指向这个目录。同时将%TAGUI_HOME%添加到系统的PATH变量中以便在任意位置都能调用tagui命令。Windows系统设置环境变量步骤右键点击“此电脑” - “属性” - “高级系统设置” - “环境变量”。在“系统变量”部分点击“新建”变量名输入TAGUI_HOME变量值输入D:\tagui你的实际路径。在“系统变量”中找到Path变量双击编辑点击“新建”添加一行%TAGUI_HOME%。一路点击“确定”保存。完成这些后打开一个新的命令行窗口输入tagui并回车。如果看到TagUI的版本信息和帮助提示说明环境配置成功了。这一步至关重要后续Python脚本会调用这个命令行工具。2.2 辅助工具截图与坐标获取在编写视觉自动化脚本时我们经常需要知道屏幕上某个按钮或区域的坐标或者需要截取某个图像作为识别的模板。这里推荐两个小工具Windows自带截图工具按Win Shift S可以快速进行区域截图保存下来作为TagUI识别的参考图像。获取鼠标坐标工具对于需要精确定位点击的场景作为视觉识别的备选方案一个能实时显示鼠标坐标的小软件很有用。例如开源工具“MousePoint”或者一些编程IDE自带的工具。在Python中我们也可以用pyautogui库的pyautogui.position()函数来实时打印坐标但需要先写一小段代码。为了简化我们可以准备一个简单的Python脚本来辅助定位。创建一个叫mouse_locator.py的文件内容如下import pyautogui import time print(‘按CtrlC终止程序’) try: while True: x, y pyautogui.position() print(f‘X: {x:4d} Y: {y:4d}’, end‘\r’) time.sleep(0.1) except KeyboardInterrupt: print(‘\n程序结束’)运行这个脚本移动鼠标到游戏界面的“领取奖励”按钮上终端里就会实时显示该点的坐标。记下这个坐标可以在TagUI脚本中使用click (x, y)的方式进行绝对坐标点击作为视觉识别失败时的降级方案。但强烈建议以视觉识别为主因为游戏窗口位置可能会变动绝对坐标很容易失效。2.3 项目结构与初始化脚本建立一个清晰的项目文件夹管理你的脚本和资源文件。建议的目录结构如下xianyu_auto_bot/ ├── src/ │ ├── main.py # 主程序入口 │ ├── core/ │ │ ├── __init__.py │ │ ├── game_operator.py # 游戏操作核心类 │ │ └── image_templates/ # 存放所有截图模板图片 │ │ ├── login_button.png │ │ ├── receive_reward.png │ │ └── ... │ └── utils/ │ ├── __init__.py │ └── tools.py # 工具函数如日志、等待 ├── config.ini # 配置文件可选 └── requirements.txt # Python依赖列表在requirements.txt中目前只需要一行tagui在项目根目录下运行pip install -r requirements.txt来安装依赖。现在我们先创建一个最简单的测试脚本test_connection.py验证TagUI能否正常工作并尝试与游戏窗口交互import tagui as t def test_basic_operation(): # 初始化TagUIverboseTrue可以看到详细的执行日志 t.init(verboseTrue) # 尝试在屏幕上寻找“记事本”图标假设你桌面有 # 这里只是演示find API实际用游戏截图 if t.find(‘notepad_icon.png’): t.click(‘notepad_icon.png’) print(“找到并点击了记事本图标”) else: print(“未找到指定图片”) # 模拟按键AltF4关闭窗口 t.keyboard(‘[alt]f4’) # 结束TagUI进程 t.close() if __name__ ‘__main__’: test_basic_operation()运行这个脚本观察输出。如果看到TagUI启动并尝试寻找图片说明基础环境打通了。接下来我们就进入核心部分分析游戏界面和设计自动化流程。3. 游戏操作分析与自动化流程设计在动手写代码之前我们必须像产品经理一样仔细分析《咸鱼之王》的手游在桌面模拟器如雷电、夜神、MuMu模拟器上的操作流程。我们的脚本不是盲目点击而是要有清晰的逻辑判断和状态感知。3.1 核心挂机循环与状态判断一个完整的日常挂机循环可能包含以下模块我们需要为每个模块设计子流程登录与断线重连检测是否在登录界面执行登录操作。检测网络断线或异常弹窗并进行处理。领取离线与每日奖励进入游戏主城后检测并领取各种邮箱奖励、每日签到、离线挂机收益。挑战副本与BOSS导航到副本界面根据体力或挑战券情况选择指定副本进行挑战并等待战斗结束。使用道具与加速自动使用经验药水、金币袋等道具使用快速战斗加速券。循环与异常处理设定循环周期并在每个循环结束后检查是否有未处理的弹窗或任务然后等待一段时间后开始下一轮。这里最大的挑战是**“状态判断”**。脚本如何知道当前界面是主城还是副本如何知道奖励是否可领取我们主要依靠两种手段视觉模板匹配提前截取关键状态下的界面元素图片如灰色的“已领取”按钮和亮色的“可领取”按钮。通过t.find()函数返回的置信度或是否找到来判断。OCR文字识别读取屏幕特定区域的文字例如金币数量、体力值、倒计时等。TagUI内置了Tesseract OCR引擎可以通过t.read()函数实现。3.2 图像模板的采集与处理技巧图像模板是我们脚本的“眼睛”。截取模板的好坏直接决定了自动化的成功率。有几个关键原则区域精准只截取按钮或标志性图标本身尽量去掉多余的背景。例如只截取“领取”两个字所在的按钮区域而不是整个奖励框。多状态备份同一个按钮截取“可点击状态”亮色和“不可点击状态”灰色两种模板。脚本先找亮色模板如果找不到再判断是否找到了灰色模板从而得知状态。分辨率适配确保你的游戏模拟器窗口分辨率是固定的并且截取模板时的分辨率与运行脚本时一致。最好以“窗口化”模式运行模拟器并固定窗口大小。命名规范图片文件名要清晰如receive_reward_active.png可领取receive_reward_inactive.png已领取。处理技巧上TagUI的find函数可以设置匹配阈值threshold默认是0.9。如果游戏画面有轻微色差或抖动可以适当调低阈值比如0.8但过低会增加误识别风险。一个更稳健的做法是不仅判断是否找到还判断找到的位置是否在预期区域内。例如你知道“领取”按钮总是在屏幕右下角某个范围内出现那么即使find在其他地方找到了相似图案也可以忽略。3.3 流程的容错与等待机制自动化脚本最怕的就是“跑飞了”。因为网络延迟、游戏加载速度不同脚本必须要有足够的耐心和容错能力。显式等待在关键操作后比如点击进入副本后一定要用t.wait()等待足够长时间让界面加载完成。等待时间不宜用固定值最好配合视觉确认。例如等待直到“战斗开始”按钮出现或者最多等待10秒。循环查找与超时对于重要的元素使用while循环配合超时机制进行查找。下面是一个通用的安全点击函数示例def safe_click(image_path, timeout10, interval0.5): “”“安全地查找并点击图片超时则放弃”“” start_time time.time() while time.time() - start_time timeout: if t.find(image_path): t.click(image_path) t.wait(1) # 点击后等待一下 return True t.wait(interval) print(f“在{timeout}秒内未找到图片{image_path}”) return False异常状态恢复在流程中穿插一些“状态校准”操作。例如在每个循环开始时都先尝试回到游戏主城界面。可以设计一个back_to_home()函数其逻辑是连续点击“返回”按钮直到某个只有主城才有的标志性元素如“挂机”按钮出现为止。日志记录在关键步骤和发生异常时打印日志到文件或控制台。这有助于后期调试和排查脚本为何在某一步停止了。设计好这些框架性的思路和工具函数后我们就可以开始编写具体的游戏操作模块了。4. 核心模块代码实现与详解我们将把游戏操作封装到一个类中这样结构更清晰也便于维护和扩展。我们创建一个GameOperator类它将是整个自动化脚本的大脑。4.1 游戏操作类GameOperator的骨架首先在core/game_operator.py中搭建类的基本结构import tagui as t import time import os from pathlib import Path class GameOperator: def __init__(self, template_dir‘image_templates/’): “”“ 初始化游戏操作器 :param template_dir: 图像模板存放的目录路径 “”“ self.template_dir Path(__file__).parent / template_dir self.running False # 初始化TagUI设置可视化模式便于调试 t.init(visual_automationTrue, chrome_browserFalse) print(“游戏操作器初始化完成。”) def wait_and_find(self, image_name, timeout10, interval0.5): “”“等待直到找到某个图片返回找到的坐标超时返回None”“” image_path str(self.template_dir / image_name) start_time time.time() while time.time() - start_time timeout: location t.find(image_path) if location: return location time.sleep(interval) print(f“等待{timeout}秒未找到图像: {image_name}”) return None def safe_click(self, image_name, timeout10): “”“安全点击找到才点”“” if self.wait_and_find(image_name, timeout): t.click(str(self.template_dir / image_name)) # 点击后给一个基础等待时间让游戏响应 t.wait(1) return True return False def run(self): “”“启动主循环”“” self.running True print(“开始挂机循环...”) while self.running: try: self.daily_loop() # 每个大循环完成后等待一段时间例如5分钟再进行下一次 print(“一个日常循环完成等待5分钟后开始下一轮。”) t.wait(300) # 等待300秒即5分钟 except KeyboardInterrupt: print(“\n用户中断退出挂机。”) self.stop() except Exception as e: print(f“循环执行中出现未知错误: {e}”) # 可以选择记录日志、尝试恢复或停止 self.stop() def stop(self): “”“停止挂机”“” self.running False t.close() print(“游戏操作器已停止。”) def daily_loop(self): “”“定义一个日常挂机循环由子步骤组成”“” # 这里调用各个子功能模块 self.check_and_reconnect() self.claim_all_rewards() self.challenge_dungeon() self.use_items() # ... 其他功能这个类提供了基础框架初始化、安全的查找和点击方法、一个主运行循环和停止机制。visual_automationTrue参数会让TagUI在执行时显示一个红色的鼠标轨迹和点击提示对于调试非常有用正式运行时可以关闭。4.2 登录与断线重连模块这是脚本的起点也是最需要鲁棒性的地方。我们需要处理几种情况游戏完全关闭、停留在登录界面、游戏中但断线重连弹窗出现。class GameOperator: # ... 接上文代码 ... def check_and_reconnect(self): “”“检查游戏状态并确保处于登录后的主界面”“” print(“[状态检查] 检查游戏连接状态...”) # 情况1检测是否在登录界面例如有“进入游戏”按钮 if self.wait_and_find(‘login_button.png’, timeout5): print(“检测到登录界面执行登录...”) self.safe_click(‘login_button.png’) # 登录后等待长时间加载 t.wait(15) # 等待直到进入主城出现主城特有标志如‘home_marker.png’ if not self.wait_and_find(‘home_marker.png’, timeout30): print(“登录后未能进入主界面可能登录失败。”) # 这里可以加入重试逻辑或发送通知 return False # 情况2检测是否有断线重连弹窗例如“重新连接”按钮 elif self.wait_and_find(‘reconnect_button.png’, timeout3): print(“检测到断线重连弹窗尝试重连...”) self.safe_click(‘reconnect_button.png’) t.wait(10) # 等待重连 # 重连后再次确认是否回到主界面 if not self.wait_and_find(‘home_marker.png’, timeout20): print(“重连后未返回主界面。”) return False # 情况3假设已在主界面尝试点击一下屏幕中央消除可能的意外弹窗 else: print(“未发现登录/断线界面尝试清理弹窗...”) t.click(‘500, 300‘) # 点击一个相对安全的位置坐标需根据你的窗口调整 t.wait(1) # 再次确认主界面标志 if self.wait_and_find(‘home_marker.png’, timeout5): print(“确认已在游戏主界面。”) return True else: print(“无法确认主界面状态尝试返回主页。”) self.force_back_to_home() return self.wait_and_find(‘home_marker.png’, timeout15) is not None def force_back_to_home(self, max_retries10): “”“强制返回主页通过连续点击返回按钮”“” print(“尝试强制返回主页...”) for i in range(max_retries): # 点击返回按钮图片‘back_button.png’需要你截取游戏的返回键 if t.find(str(self.template_dir / ‘back_button.png’)): t.click(str(self.template_dir / ‘back_button.png’)) t.wait(1) # 每点一次检查是否已回到主页 if self.wait_and_find(‘home_marker.png’, timeout3): print(f“点击{i1}次返回键后回到主页。”) return True # 如果找不到返回按钮也可能已经在主页或者被其他界面遮挡 t.wait(1) print(“强制返回主页失败。”) return False这个模块体现了“防御式编程”的思想不假设任何状态而是主动检测并尝试恢复到一个已知的稳定状态游戏主界面。这是整个脚本稳定运行的基石。4.3 自动领取奖励模块奖励领取是挂机收益的主要来源。我们需要遍历所有可能的奖励入口。class GameOperator: # ... 接上文代码 ... def claim_all_rewards(self): “”“领取所有可领取的奖励”“” print(“[奖励领取] 开始领取奖励...”) # 1. 领取离线挂机奖励通常在主界面有明显按钮 self._claim_reward_by_image(‘offline_reward_button.png’, ‘离线奖励’) # 2. 打开邮箱/邮件界面 if self.safe_click(‘mailbox_button.png’, timeout5): t.wait(3) # 等待邮箱界面加载 # 在邮箱界面内点击“一键领取” self._claim_reward_by_image(‘claim_all_mail_button.png’, ‘邮件奖励’) # 领取后关闭邮箱界面 t.wait(2) self.safe_click(‘mail_close_button.png’) # 3. 领取每日签到奖励可能需要导航到活动界面 if self.safe_click(‘activity_button.png’, timeout5): t.wait(3) self._claim_reward_by_image(‘daily_checkin_button.png’, ‘每日签到’) # 签到界面可能有点击关闭或自动关闭这里加一个通用返回 self.force_back_to_home() # 4. 领取其他活动奖励例如七日登录、在线礼包等 # 思路类似导航到对应界面 - 查找领取按钮 - 点击 - 返回 # 这里可以用一个列表来配置需要检查的活动 activity_list [‘seven_day_button.png’, ‘online_gift_button.png’] for activity_img in activity_list: if self.safe_click(activity_img, timeout3): t.wait(2) # 假设活动界面内的领取按钮有通用命名 self._claim_reward_by_image(‘claim_in_activity.png’, f‘活动{activity_img}’) self.force_back_to_home() t.wait(1) print(“[奖励领取] 所有奖励领取流程结束。”) def _claim_reward_by_image(self, image_name, reward_name): “”“内部方法通过图片识别领取特定奖励”“” if self.safe_click(image_name, timeout5): print(f“ 成功领取{reward_name}”) t.wait(2) # 领取奖励后的通用等待 # 有时领取会有二次确认弹窗 if self.wait_and_find(‘confirm_button.png’, timeout2): self.safe_click(‘confirm_button.png’) t.wait(1) return True else: print(f“ 未找到或无法领取{reward_name}”) return False这个模块采用了“配置化”的思想将需要检查的活动按钮图片放在一个列表里循环处理。这样以后要增加新的奖励领取项只需要更新这个列表和对应的图片模板即可不需要修改核心逻辑。4.4 副本挑战与战斗循环模块挑战副本是消耗体力、获取资源的核心。这里逻辑稍复杂因为涉及界面导航、次数判断和战斗等待。class GameOperator: # ... 接上文代码 ... def challenge_dungeon(self, dungeon_type‘normal’, times3): “”“ 挑战指定类型的副本 :param dungeon_type: 副本类型如‘normal’普通副本‘boss’BOSS战 :param times: 挑战次数 “”“ print(f“[副本挑战] 准备挑战{dungeon_type}副本{times}次...”) # 步骤1导航到副本界面 if not self.safe_click(‘dungeon_entrance_button.png’, timeout5): print(“ 无法进入副本界面跳过此模块。”) return t.wait(3) # 等待副本界面加载 # 步骤2根据类型选择副本标签页 if dungeon_type ‘boss’: self.safe_click(‘boss_tab_button.png’) t.wait(2) # 假设挑战当前显示的BOSS target_image ‘challenge_boss_button.png’ else: # 普通副本 self.safe_click(‘normal_tab_button.png’) t.wait(2) # 这里简化处理挑战第一个可挑战的副本 target_image ‘challenge_normal_button.png’ # 步骤3循环挑战指定次数 for i in range(times): print(f“ 开始第{i1}次挑战...”) if not self.safe_click(target_image, timeout5): print(f“ 第{i1}次挑战失败可能体力不足或次数用完。”) break # 步骤4处理挑战确认如果有 if self.wait_and_find(‘challenge_confirm_button.png’, timeout3): self.safe_click(‘challenge_confirm_button.png’) # 步骤5等待战斗开始并完成 self._wait_for_battle_complete() # 步骤6战斗结束处理结果界面点击“继续”或“返回” t.wait(3) # 等待结算界面出现 self.safe_click(‘battle_continue_button.png’) # 点击继续 # 或者点击返回取决于游戏设计 # self.safe_click(‘battle_back_button.png’) t.wait(2) # 战斗间隔等待 # 步骤7每次挑战后检查是否还在副本界面如果不在则返回 if not t.find(str(self.template_dir / ‘dungeon_entrance_button.png’)): print(“ 意外退出副本界面尝试重新进入...”) self.force_back_to_home() if not self.safe_click(‘dungeon_entrance_button.png’, timeout5): break # 如果连入口都找不到了就终止循环 # 步骤8无论完成与否最后都尝试返回主界面 print(“[副本挑战] 挑战流程结束返回主界面。”) self.force_back_to_home() def _wait_for_battle_complete(self, timeout60): “”“等待一场战斗结束通过检测战斗界面消失或‘胜利’标志出现”“” print(“ 进入战斗等待...”) start_time time.time() # 方法1检测战斗界面特有的元素如暂停按钮是否消失 battle_marker str(self.template_dir / ‘battle_pause_button.png’) # 方法2检测战斗结束标志如胜利图标是否出现 victory_marker str(self.template_dir / ‘victory_flag.png’) while time.time() - start_time timeout: # 如果战斗结束标志出现说明打完了 if t.find(victory_marker): print(“ 检测到战斗胜利。”) t.wait(2) # 等待胜利动画 return True # 如果战斗界面标志消失了也可能说明打完了比如快速跳过 if not t.find(battle_marker): print(“ 战斗界面已消失判断战斗结束。”) t.wait(1) return True time.sleep(2) # 每2秒检查一次 print(f“ 战斗等待超时{timeout}秒强制跳出。”) return False这个模块的关键在于_wait_for_battle_complete函数。等待战斗结束不能使用固定的sleep因为战斗时长可能因阵容、关卡而变化。这里采用了混合判断既检测“胜利”标志的出现也检测“战斗界面”元素的消失双重保险并设置了超时机制防止脚本卡死。4.5 使用道具与加速模块自动使用经验药水、金币袋以及使用快速战斗加速券可以进一步提升挂机效率。class GameOperator: # ... 接上文代码 ... def use_items(self): “”“使用背包中的消耗品”“” print(“[使用道具] 开始检查并使用道具...”) # 打开背包界面 if not self.safe_click(‘bag_button.png’, timeout5): print(“ 无法打开背包跳过道具使用。”) return t.wait(2) # 切换到消耗品标签页图片需要根据游戏截取 self.safe_click(‘consumable_tab_button.png’) t.wait(1) # 定义需要使用的道具列表图片文件名 使用后确认按钮图片 items_to_use [ (‘exp_potion.png’, ‘use_confirm_button.png‘), # 经验药水 (‘gold_bag.png’, ‘use_confirm_button.png‘), # 金币袋 (‘quick_battle_ticket.png’, ‘use_confirm_button.png‘), # 加速券 ] for item_img, confirm_img in items_to_use: print(f“ 尝试使用道具{item_img}”) # 点击道具图标 if self.safe_click(item_img, timeout3): t.wait(1) # 点击使用按钮假设使用按钮是固定的‘use_button.png’ if self.safe_click(‘use_button.png’, timeout2): t.wait(1) # 处理使用确认弹窗 if self.wait_and_find(confirm_img, timeout2): self.safe_click(confirm_img) print(f“ 成功使用。”) t.wait(1) # 使用后界面可能还在道具详情点击空白处关闭 t.click(‘400, 300‘) # 点击一个空白区域坐标 t.wait(1) else: print(f“ 未找到使用按钮可能该道具无法使用或已用完。”) else: print(f“ 未找到道具或道具数量为0。”) # 关闭背包界面 self.safe_click(‘bag_close_button.png’) print(“[使用道具] 道具使用流程结束。”)这个模块的实现相对直接核心是遍历一个预定义的道具列表。这里有一个细节使用道具后游戏可能会弹出确认窗口也可能直接使用。我们的代码处理了这两种情况。点击空白处关闭详情窗口是一个实用的技巧可以避免界面堆叠。5. 主程序整合、调度与优化各个功能模块已经就绪现在我们需要一个主程序来把它们串联起来并加入更智能的调度和健壮性处理。5.1 主程序入口与配置化我们在src/main.py中创建主程序。为了让脚本更灵活我们可以引入一个简单的配置文件比如config.ini来控制行为。# main.py import time import signal import sys from core.game_operator import GameOperator def main(): print(“ 《咸鱼之王》自动化挂机脚本启动 ”) print(“提示请确保游戏模拟器窗口已打开并置于前台。”) print(“按 CtrlC 可以安全停止脚本。\n”) # 创建游戏操作实例 bot GameOperator(template_dir‘core/image_templates/’) # 注册信号处理使CtrlC能优雅退出 def signal_handler(sig, frame): print(‘\n接收到中断信号正在停止...’) bot.stop() sys.exit(0) signal.signal(signal.SIGINT, signal_handler) try: # 首先尝试将游戏恢复到主界面状态 if not bot.check_and_reconnect(): print(“初始状态恢复失败脚本退出。”) bot.stop() return # 启动主循环 bot.run() except Exception as e: print(f“主程序运行出错: {e}”) bot.stop() if __name__ ‘__main__’: main()这个主程序非常简单初始化、设置优雅退出、执行一次状态恢复然后启动无限循环。真正的调度逻辑在GameOperator.run()和daily_loop()方法里。我们可以让循环更智能。比如不是固定每5分钟执行全套日常而是根据游戏内活动的冷却时间动态调整。这需要OCR读取屏幕上的时间信息。这里提供一个扩展思路在daily_loop()末尾使用OCR识别游戏内某个日常活动的刷新倒计时然后根据这个时间来计算下一次执行循环的等待时间。5.2 异常处理与日志增强目前的异常处理还比较基础。在生产环境中我们需要更完善的日志记录以便排查问题。 在GameOperator类中我们可以增加一个日志记录器import logging class GameOperator: def __init__(self, template_dir‘image_templates/’, log_file‘bot.log’): # ... 其他初始化 ... self.setup_logging(log_file) def setup_logging(self, log_file): “”“配置日志记录”“” logging.basicConfig( levellogging.INFO, format‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’, handlers[ logging.FileHandler(log_file, encoding‘utf-8’), logging.StreamHandler() # 同时输出到控制台 ] ) self.logger logging.getLogger(‘XianYuBot’) # 然后将所有print替换为self.logger.info/warning/error # 例如print(“开始挂机循环...”) - self.logger.info(“开始挂机循环...”)同时在daily_loop和各个子函数中用try...except包裹可能出错的部分记录错误但不轻易让整个脚本崩溃而是尝试恢复或跳过当前任务。def daily_loop(self): tasks [ (‘状态检查’, self.check_and_reconnect), (‘领取奖励’, self.claim_all_rewards), (‘副本挑战’, lambda: self.challenge_dungeon(‘normal’, 3)), (‘使用道具’, self.use_items), ] for task_name, task_func in tasks: self.logger.info(f“开始执行任务: {task_name}”) try: task_func() except Exception as e: self.logger.error(f“任务[{task_name}]执行失败: {e}”, exc_infoTrue) # 失败后尝试回到主界面避免状态混乱 self.force_back_to_home() self.logger.info(f“任务[{task_name}]执行完毕”) t.wait(2) # 任务间短暂间隔5.3 性能优化与稳定性技巧运行时间长了可能会遇到一些性能或稳定性问题。这里分享几个优化点图像识别加速TagUI的find函数默认搜索全屏比较耗时。如果知道目标大致的屏幕区域可以使用t.find(‘image.png’, region(x, y, width, height))来限定搜索范围大幅提升速度。降低CPU占用在等待循环中如wait_and_find使用time.sleep(interval)而不是密集循环。interval可以设为0.5秒或1秒避免不必要的CPU消耗。模板图片管理图片模板不要太多、太大。定期检查并更新模板因为游戏更新可能会导致界面变化。可以写一个简单的脚本在启动时检查所有模板图片是否存在。模拟器窗口聚焦确保脚本运行时游戏模拟器窗口是激活状态。可以配合使用pygetwindow这样的库来查找并聚焦模拟器窗口但注意不要和TagUI的视觉操作冲突。计划任务如果你希望电脑在特定时间比如凌晨自动运行脚本可以使用Windows的“任务计划程序”或macOS/Linux的cron来定时启动你的Python脚本。6. 常见问题排查与实战心得即使代码写得再严谨在实际运行中还是会遇到各种意想不到的问题。下面是我在开发和长期运行这类脚本中积累的一些常见问题与解决思路。6.1 图像识别失败问题排查表问题现象可能原因排查步骤与解决方案始终找不到图片1. 模板图片路径错误或不存在。2. 游戏画面与截图时状态不同如亮度、主题。3. 游戏窗口被遮挡、最小化或不在前台。4. 屏幕分辨率/缩放比例与截图时不一致。1. 打印image_path确认路径正确检查文件是否存在。2. 重新截取模板确保游戏状态一致如关闭特效。3. 确保游戏窗口最大化且未被遮挡。运行脚本前手动点击一下窗口。4. 固定模拟器窗口大小和系统显示缩放设置为100%。偶尔识别失败时好时坏1. 游戏画面有动态特效如光效、闪烁。2. 网络延迟导致界面加载慢。3. TagUI匹配阈值过高。1. 截取模板时避开动态区域或等待特效消失后再操作。2. 在关键操作前增加t.wait()等待时间。3. 在t.find()中尝试调低threshold参数例如t.find(‘img.png’, threshold0.8)。点击位置偏移1. 识别到的坐标不是按钮中心。2. 屏幕缩放导致坐标计算错误。1. TagUI默认点击找到区域的中心。如果偏移检查模板图片是否包含了非按钮区域。2. 确保TagUI和Python脚本在相同的DPI感知环境下运行。对于高分辨率屏可能需要调整。误识别点错地方1. 模板特征不够独特与其他界面元素相似。2. 阈值设置过低。1. 重新截取更具独特性的模板比如包含按钮上的文字和部分背景。2. 适当提高threshold值或结合region参数限定搜索范围。6.2 TagUI与Python交互的坑t.init()失败最常见的原因是TAGUI_HOME环境变量未正确设置或者TagUI原生包缺失文件。请确保按照第二部分的环境配置步骤操作并在命令行中直接运行tagui命令能成功。脚本执行后无反应检查是否以管理员身份运行某些系统设置可能阻止自动化工具控制鼠标键盘。尝试以管理员身份运行你的Python脚本或IDE。t.keyboard()输入中文无效TagUI对中文输入支持不佳。如果需要在游戏内输入文字一个变通方法是先用t.click()点击输入框然后使用pyperclip库将中文复制到剪贴板再用t.keyboard(‘[ctrl]v’)进行粘贴。脚本在循环中内存缓慢增长长时间运行后如果发现内存占用越来越高可能是TagUI的Java进程没有完全释放。确保在脚本结束或异常退出时都调用了t.close()来清理资源。在主程序中使用try...finally块是个好习惯。6.3 针对《咸鱼之王》的特定技巧弹窗处理这类游戏经常有各种突然弹出的活动公告、奖励提示、网络重连框。我们的check_and_reconnect模块处理了部分但可能不够。一个更激进但有效的方法是在每次关键操作如点击前都先扫描屏幕中上部是否存在常见的弹窗关闭按钮比如一个小的“X”如果存在就立刻点击关闭。可以把这个逻辑写成一个dismiss_popups()函数在循环中频繁调用。“跳过”与“加速”很多战斗有跳过动画或加速的按钮。在_wait_for_battle_complete函数中可以加入检测并点击“跳过”按钮的逻辑能节省大量时间。体力与资源监控通过OCR读取体力值、金币数量可以实现更智能的决策。例如当体力大于100时才去挑战副本当金币少于某个数值时优先使用金币袋。这需要你截取显示数字的区域图片并调试t.read()函数的准确性可能需要调整Tesseract的语言包或预处理图片。6.4 我的实战心得与建议从小功能开始逐步集成不要一开始就想做一个全自动的完美脚本。先实现“自动领取离线奖励”这一个小功能跑通了再增加“领取邮件”像搭积木一样慢慢完善。这样调试起来目标明确容易成功。准备两套操作逻辑以视觉识别为主以绝对坐标点击为辅。对于位置极其固定、且没有替代元素的按钮比如模拟器窗口右上角的关闭按钮可以记录其坐标当视觉识别连续失败时用坐标点击作为保底。在safe_click函数里可以加入这个降级逻辑。给脚本加上“眼睛”和“大脑”“眼睛”就是截图对比但“大脑”更重要。脚本应该能判断当前处于什么状态而不是机械地执行一系列点击。多利用find函数的返回值是否找到来做条件判断这是实现智能化的关键。做好“失控”的准备自动化脚本可能会因为游戏更新、网络卡顿而“跑飞”比如不停点击一个错误的地方。在开发阶段可以把鼠标移到屏幕角落比如(10,10)作为紧急停止的热点并在脚本里周期性检查鼠标是否在那个位置如果是就暂停脚本。或者更简单随时准备按CtrlC终止程序。尊重游戏规则最后也是最重要的了解游戏的使用条款。自动挂机脚本可能违反某些游戏的服务协议存在封号风险。本项目主要用于学习和研究RPA自动化技术请合理使用控制挂机时长避免影响游戏公平性或自身账号安全。这个项目从环境搭建到核心模块实现再到优化排错完整地展示了一个实用RPA脚本的诞生过程。它不仅仅适用于《咸鱼之王》其设计思路和代码框架经过调整可以迁移到很多具有固定界面的桌面应用自动化任务上。希望这份超详细的实战指南能帮你打开桌面自动化的大门。