简介:本资源是一个面向《阴阳师》手游玩家的Python自动化辅助脚本项目,专为希望提升日常副本效率、减少重复操作的中高级玩家设计。项目基于OpenCV等图像识别技术实现界面元素精准定位与交互逻辑控制,覆盖魂十一单人速刷、困二十八层挂机、源赖光经验副本自动挑战及御灵副本智能通关四大核心场景,显著降低操作门槛并释放碎片时间。压缩包共9个文件(4个.py主逻辑与UI模块、3个.txt配置与说明文件、1个.ui界面定义、1个.md文档),总大小仅18KB,轻量易部署,结构清晰便于二次开发与调试。目前已有86人学习下载,读者可直接获取完整可运行脚本、环境依赖清单(requirements.txt)、图形界面封装(form.ui)、多副本状态识别策略及模块化任务调度逻辑(yys_work.py + widget.py + linklist.py),具备良好的工程实践参考价值。

1. 项目缘起:从手动“肝”到自动“刷”的转变

如果你也玩过《阴阳师》这类回合制养成游戏,一定对“肝”这个字深有体会。每天上线,重复着“魂十一”、“困二十八”、“经验副本”、“御灵副本”这些日常任务,一刷就是几个小时,手指点得发麻,眼睛看得发酸,但为了提升式神、获取御魂,又不得不做。这种重复性极高的操作,不仅消耗时间,更消磨游戏的乐趣。作为一名有多年开发经验的玩家,我一直在想,能不能用技术解放双手,把我们从这种机械劳动中解脱出来?这就是我启动这个“阴阳师自动化辅助脚本”项目的初衷。

这个项目的核心目标非常明确:利用Python编程和图像识别技术,模拟玩家在游戏内的操作,实现日常副本的自动化运行。具体来说,它要能稳定、高效地完成几个核心场景: 魂十一的单人速刷 、 困二十八层的高效挂机 、 源赖光经验副本的自动挑战 以及 御灵副本的智能通关 。这几乎覆盖了玩家日常“搬砖”的绝大部分需求。它不是外挂,不修改游戏内存,不发送异常封包,其原理仅仅是“看”屏幕、“想”策略、“点”鼠标,模拟一个真实玩家的操作逻辑,因此从行为模式上讲,风险相对可控。当然,任何自动化操作都需谨慎,并仅建议在个人单机环境下用于学习与研究。

接下来,我将从技术选型、核心模块拆解、实战避坑指南以及未来优化空间四个维度,为你完整复盘这个项目的构建过程。无论你是想学习图像识别在自动化领域的应用,还是想为自己心爱的游戏打造一个“后勤管家”,相信这篇超过五千字的详实记录都能给你带来启发。

2. 技术栈选型:为什么是Python + 图像识别?

在项目启动前,技术选型是首要问题。市面上实现自动化操作的技术路线不少,比如基于游戏内存读取的“真·外挂”,基于安卓ADB指令的操控,或是基于Windows API的键鼠模拟。经过权衡,我最终选择了 “Python + 图像识别” 这条路径,原因如下:

2.1 选择Python的核心考量

Python并非执行效率最高的语言,但在自动化脚本领域,它拥有无与伦比的生态优势和开发效率。

  • 丰富的库支持 : PyAutoGUI 提供了跨平台的鼠标键盘控制; Pillow (PIL) 是图像处理的基石; OpenCV 和 PyTorch/TensorFlow 为高级图像识别提供可能; NumPy 让数值计算变得简单。这些库成熟、稳定,文档齐全,能极大降低开发门槛。
  • 快速原型开发 :自动化脚本需要频繁调整点击坐标、等待时间、识别阈值等参数。Python的交互式特性(如Jupyter Notebook)和简洁语法,允许我们快速修改代码并看到效果,进行“试错式”开发,这对调试脚本逻辑至关重要。
  • 跨平台潜力 :虽然本项目主要针对Windows端的桌面版阴阳师,但PyAutoGUI等库也支持macOS和Linux。核心的图像识别逻辑可以相对容易地迁移,为后续适配其他平台(如模拟器)留有余地。

2.2 图像识别 vs. 其他方案

  • 与内存挂对比 :直接读取游戏内存数据无疑是最精准、最高效的方式,能直接获取怪物血量、技能CD、战斗结果等状态。但这需要逆向工程,触及游戏核心数据,风险极高,极易被检测并封号,且违反用户协议。我们追求的是“辅助”而非“破坏”,因此首先排除。
  • 与ADB操控对比 :通过ADB(Android Debug Bridge)控制安卓模拟器,可以获取屏幕截图并模拟点击。这种方式稳定,且不依赖前台窗口。但它的局限性在于,必须运行在模拟器内,对于直接使用Windows桌面版客户端的玩家不够友好。此外,ADB的截图和点击速度有时会成为瓶颈。
  • 与纯坐标点击对比 :有些简单脚本通过记录固定的屏幕坐标进行点击。这种方式极其脆弱,一旦游戏窗口位置改变、分辨率调整、UI更新,脚本就会完全失效,毫无健壮性可言。

因此,图像识别成为了平衡“安全性”、“通用性”和“智能性”的最佳选择。 它的工作原理是:脚本实时捕获游戏窗口的截图,在截图中寻找预先准备好的“模板图片”(比如“挑战”按钮、胜利后的“结算图标”、特定怪物的形象等),找到后计算出该图案在屏幕上的位置,然后驱动鼠标去点击。只要游戏UI逻辑不变,即使窗口位置移动,脚本也能通过“看图找茬”的方式准确定位到目标。

注意 :图像识别脚本的稳定性高度依赖于游戏UI的稳定性。如果游戏版本大更新,按钮样式或位置发生改变,就需要更新对应的模板图片。这是采用此方案必须接受的维护成本。

3. 核心模块深度拆解:脚本如何“看见”并“思考”

整个自动化脚本可以看作一个状态机,它循环执行“截图 -> 分析 -> 决策 -> 操作”这个过程。下面我们深入每个模块的细节。

3.1 环境感知模块:精准截图与图像预处理

一切始于“看见”。我们首先要能稳定地获取游戏画面。

import pyautogui
import cv2
from PIL import ImageGrab

def capture_game_window(window_title="阴阳师"):
    # 方案一:使用PyAutoGUI定位窗口并截图(推荐)
    try:
        window = pyautogui.getWindowsWithTitle(window_title)[0]
        if window.isMinimized:
            window.restore()
        window.activate() # 激活窗口,确保在最前
        # 获取窗口位置和大小
        left, top, width, height = window.left, window.top, window.width, window.height
        screenshot = pyautogui.screenshot(region=(left, top, width, height))
        return cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR), (left, top)
    except IndexError:
        raise Exception(f"未找到标题包含'{window_title}'的窗口")

    # 方案二:使用ImageGrab指定区域(需已知坐标)
    # region = (0, 0, 1920, 1080) # 示例坐标,需自行调整
    # screenshot = ImageGrab.grab(bbox=region)

这里有两个关键点:

  1. 窗口激活 : window.activate() 非常重要。有些游戏在后台时渲染会停止或降低频率,导致截图内容停滞。确保窗口在前台能获得实时画面。
  2. 坐标转换 : pyautogui.screenshot 返回的是PIL图像,而OpenCV处理需要numpy数组的BGR格式。 cv2.cvtColor 完成了这个转换。同时,我们记录下窗口左上角的坐标 (left, top) ,这样识别出的图片坐标(相对于截图)才能转换为全局屏幕坐标。

图像预处理 是为了提高识别成功率。原始截图可能因为游戏特效、亮度变化而产生干扰。

def preprocess_image(image):
    # 转换为灰度图,大部分识别不需要颜色信息,且能提升速度
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 可选:二值化,使特征更突出
    # _, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
    # 可选:降噪
    # denoised = cv2.medianBlur(gray, 3)
    return gray

对于阴阳师这类UI对比度较高的游戏,通常转换为灰度图就足够了。二值化在背景复杂时有用,但也可能丢失细节,需要根据实际模板测试。

3.2 决策中枢模块:基于模板匹配的状态判断

这是脚本的“大脑”。它通过比对预先准备好的模板图片,来判断当前游戏处于什么状态,并决定下一步做什么。

首先,我们需要一个可靠的模板匹配函数:

def find_template(screenshot, template_path, threshold=0.8):
    """
    在screenshot中寻找template_path指定的模板。
    threshold: 匹配置信度阈值,越高要求越严格。
    返回匹配到的最高置信度及中心点坐标(相对于screenshot),未找到返回None。
    """
    template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
    if template is None:
        raise FileNotFoundError(f"模板图片未找到: {template_path}")
    screen_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
    result = cv2.matchTemplate(screen_gray, template, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    if max_val >= threshold:
        h, w = template.shape
        center_x = max_loc[0] + w // 2
        center_y = max_loc[1] + h // 2
        return max_val, (center_x, center_y)
    else:
        return None

cv2.matchTemplate 提供了多种匹配方法, TM_CCOEFF_NORMED (归一化相关系数匹配)对光照变化有一定鲁棒性,效果较好。 threshold (阈值)是这个函数的核心参数,需要反复调试。设得太高(如0.95),轻微的画面抖动或特效就可能导致匹配失败;设得太低(如0.7),则可能匹配到错误的相似区域。

基于这个函数,我们可以构建状态判断逻辑。以“魂十一”单人挑战为例,一个简化的状态机如下:

def decide_action(screenshot, window_pos):
    # 状态1:是否在副本选择界面?(寻找“魂十一”标签的模板)
    state = check_state(screenshot, ['template_team_up.png', 'template_soul_11.png'])
    if state == "IN_SELECTION":
        click_on_template(screenshot, 'template_challenge.png', window_pos)
        return "CLICKED_CHALLENGE"

    # 状态2:是否在队伍配置界面?(寻找“准备”按钮)
    state = check_state(screenshot, ['template_ready.png'])
    if state == "IN_TEAM_SETUP":
        click_on_template(screenshot, 'template_ready.png', window_pos)
        return "CLICKED_READY"

    # 状态3:是否在战斗中?(寻找战斗中特有的UI,如倒计时、技能图标)
    state = check_state(screenshot, ['template_in_battle.png'])
    if state == "IN_BATTLE":
        # 战斗中可以执行策略,比如检测是否该标记目标,或者单纯等待
        pyautogui.sleep(15) # 估算战斗时间,等待
        return "IN_BATTLE_WAITING"

    # 状态4:是否战斗胜利?(寻找胜利的“结算”或“勋章”图标)
    state = check_state(screenshot, ['template_victory.png', 'template_chest.png'])
    if state == "VICTORY":
        # 连续点击屏幕,快速跳过奖励动画
        for _ in range(5):
            pyautogui.click(button='left')
            pyautogui.sleep(0.5)
        return "VICTORY_CLICKING"

    # 状态5:是否体力不足?(寻找体力不足的弹窗)
    state = check_state(screenshot, ['template_no_ap.png'])
    if state == "NO_AP":
        # 执行处理逻辑,比如使用勾玉补充,或者停止脚本
        handle_no_ap()
        return "STOP_SCRIPT"
    # ... 更多状态判断
    return "UNKNOWN_STATE"

这个 decide_action 函数会循环执行。关键在于 check_state 函数的实现。它应该按优先级顺序检查多个模板。例如,胜利图标和战斗图标可能不会同时出现,但为了保险,可以先检查是否有“体力不足”这种需要立即处理的异常状态,再检查常规流程状态。

3.3 执行器模块:稳健的鼠标操作与防呆设计

操作模块看似简单,就是 pyautogui.click(x, y) ,但里面有很多细节关乎脚本的稳定性。

  • 随机化操作 :完全固定的点击点和时间间隔是“机器人”的典型特征。加入随机性可以模拟人类操作。
    import random
    import time
    
    def smart_click(center_x, center_y, window_pos):
        # 将截图内的坐标转换为全局屏幕坐标
        global_x = window_pos[0] + center_x
        global_y = window_pos[1] + center_y
        # 在目标点附近随机一个偏移量(例如±5像素)
        offset_x = random.randint(-5, 5)
        offset_y = random.randint(-5, 5)
        target_x = global_x + offset_x
        target_y = global_y + offset_y
        # 移动鼠标,加入随机延迟
        pyautogui.moveTo(target_x, target_y, duration=random.uniform(0.1, 0.3))
        # 点击前稍作停顿,模拟反应时间
        time.sleep(random.uniform(0.05, 0.15))
        pyautogui.click()
        # 点击后也随机等待一下
        time.sleep(random.uniform(0.1, 0.3))
    
  • 操作失败重试与超时 :不能因为一次识别或点击失败就让脚本卡死。每个关键操作步骤都应该有重试机制和超时判断。
    def click_with_retry(template_path, max_retries=5, timeout=30):
        start_time = time.time()
        for i in range(max_retries):
            screenshot, pos = capture_game_window()
            result = find_template(screenshot, template_path)
            if result:
                confidence, center = result
                smart_click(center[0], center[1], pos)
                return True # 点击成功
            # 未找到模板,等待一段时间再试
            time.sleep(1)
            if time.time() - start_time > timeout:
                print(f"超时:未找到模板 {template_path}")
                return False
        print(f"重试{max_retries}次后失败:未找到模板 {template_path}")
        return False
    
  • 异常状态监听 :脚本需要能处理游戏中的各种弹窗(如网络重连、公告、活动提示)。这需要准备一套覆盖常见干扰项的模板库,并在主循环中定期进行“巡检”。

4. 分场景实战与避坑指南

有了核心框架,针对不同的副本,我们需要定制化的策略和模板。这里分享几个关键场景的实现心得和踩过的坑。

4.1 魂十一单人速刷:速度与稳定的博弈

魂十一是御魂主要产出地,追求的是速刷。自动化脚本在这里的挑战在于:

  • 战斗节奏快 :从进入副本到战斗结束,时间很短。要求状态判断必须迅速准确。
  • 结算动画 :胜利后的结算动画有多个可跳过节点,点击太快或太慢都会影响效率。

我的策略 :

  1. 模板准备 :准备高对比度的“挑战”按钮、“准备”按钮、战斗中的“自动”图标、胜利后的“勋章”图标以及“再次挑战”按钮的模板。特别注意,“再次挑战”按钮在结算界面可能出现的位置有多个(取决于是否有掉落),需要选取一个最稳定的特征点。
  2. 循环逻辑 :
    开始 -> 识别并点击“挑战” -> 识别并点击“准备” -> 等待(估算战斗时间,或识别“胜利”图标)-> 识别并点击“再次挑战” -> 循环
    
  3. 关键避坑点 :
    • “自动”战斗状态 :在进入战斗后,脚本应能检测是否已开启“自动”战斗。如果没有,需要点击技能区域或“自动”按钮。这里不能依赖固定坐标,因为队伍配置不同,式神位置会变。一个可行的方案是识别“自动”二字的小图标,或者直接识别式神脚下的绿色“自动”光圈(作为备选模板)。
    • 网络延迟容忍 :在点击“准备”或“再次挑战”后,游戏会有网络通信过程。脚本必须等待界面稳定(新UI元素加载出来)后再进行下一轮识别,否则极易误点。我的经验是加入一个 pyautogui.sleep(1.5) 的基础等待,再结合模板识别确认。
    • 体力检测 :必须在主循环中嵌入体力检测。一旦弹出体力不足的窗口,脚本应能识别并执行预设操作(如使用勾玉补充、使用饭团、或停止脚本并通知用户)。

4.2 困二十八层高效挂机:持久战的稳定性

困二十八是经验副本,通常挂机时间很长,对脚本的稳定性要求极高。

  • 挑战 :战斗时间更长,场景单一,但需要防止长时间运行后出现的各种意外(如游戏弹窗、模拟器/客户端卡顿、Windows系统通知)。
  • 策略 :与魂十一类似,但等待时间更长。这里可以引入更宽松的超时机制。例如,如果在预计战斗时间结束后仍未检测到胜利图标,可以尝试点击屏幕中央(防止游戏因未操作而进入待机),或者重启检测循环。
  • 重要技巧——心跳检测 :我设计了一个“心跳”线程,每隔一段时间(比如10分钟)就执行一次 capture_game_window ,并尝试找一个必定存在的UI元素(如屏幕右上角的“金币”图标)进行匹配。如果连续多次匹配失败,可能意味着游戏窗口丢失、最小化或卡死。此时脚本可以尝试重新激活窗口,或者记录日志并安全暂停,而不是一直死循环。

4.3 御灵副本智能通关:动态决策的尝试

御灵副本的机制稍微复杂,BOSS会召唤小怪,有时需要集火BOSS,有时需要清理小怪。纯模板匹配的“无脑”点击在这里效率不高。

  • 进阶策略 :我们可以尝试简单的“动态决策”。例如:
    1. 在战斗界面,定期截图。
    2. 使用多个模板同时识别屏幕中是否存在“BOSS图标”和“小怪图标”。
    3. 如果识别到多个小怪,且小怪模板的匹配数量和位置符合“威胁较大”(比如三个小怪都出现了),则决策为“点击群体技能图标”或“点击小怪”。
    4. 如果只有BOSS或小怪威胁不大,则决策为“点击BOSS”或“使用单体技能”。
  • 实现难点 :这需要更精准的模板(小怪和BOSS在不同角度下的图像),以及一个简单的决策权重系统。 cv2.matchTemplate 可以设置阈值来筛选出所有匹配位置,通过 cv2.minMaxLoc 只能找到最佳匹配点,对于多目标,需要使用 np.where(result >= threshold) 来获取所有位置,然后通过聚类去除相邻的重复框。
    def find_all_templates(screenshot, template_path, threshold=0.7):
        template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE)
        screen_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY)
        result = cv2.matchTemplate(screen_gray, template, cv2.TM_CCOEFF_NORMED)
        locations = np.where(result >= threshold)
        points = []
        for pt in zip(*locations[::-1]): # 交换宽高,因为返回的是(col, row)
            points.append(pt)
        # 简单的非极大值抑制,去除过于接近的重复框
        points = non_max_suppression(points, template.shape)
        return points # 返回所有匹配到的左上角坐标列表
    
    这个功能实现起来代码量会大增,且非常依赖模板质量和阈值调优,是项目中的一个进阶挑战。

5. 工程化与优化:让脚本从玩具变成工具

一个能跑起来的脚本和一个能稳定挂机数小时的脚本,中间隔着工程化的鸿沟。

5.1 配置与模板管理 不要将模板图片路径、点击坐标、等待时间等参数硬编码在代码里。应该使用配置文件(如JSON、YAML)来管理。

{
  "templates": {
    "challenge": "./assets/challenge_button.png",
    "ready": "./assets/ready_button.png",
    "victory": "./assets/victory_medal.png"
  },
  "thresholds": {
    "challenge": 0.85,
    "ready": 0.9,
    "victory": 0.8
  },
  "delays": {
    "after_click": 1.5,
    "battle_wait": 18
  }
}

这样,当游戏更新UI时,我们只需要替换 assets 文件夹下的图片,并微调配置文件中的阈值和延迟,而无需修改核心代码。

5.2 日志与错误处理 完善的日志系统是调试和监控的基石。记录脚本的状态转换、识别结果、操作记录以及发生的异常。

import logging
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s - %(message)s',
                    handlers=[logging.FileHandler('bot.log'), logging.StreamHandler()])

try:
    result = find_template(screenshot, template_path)
    if result:
        logging.info(f"成功匹配到 {template_path}, 置信度: {result[0]:.2f}")
        smart_click(...)
    else:
        logging.warning(f"未匹配到 {template_path}, 当前阈值: {threshold}")
except Exception as e:
    logging.error(f"在处理 {template_path} 时发生错误: {e}", exc_info=True)

当脚本在半夜挂机出错时,查看第二天的日志文件就能快速定位问题。

5.3 性能优化

  • 截图区域优化 :不要每次都截取全屏。根据模板可能出现的大致区域,只截取屏幕的一部分,可以大幅提升处理速度。例如,结算图标通常出现在屏幕中下方。
  • 模板图片优化 :模板图片应尽可能小,只包含特征鲜明的部分,去除无关背景。使用PNG格式(支持透明背景)有时比JPG更好。
  • 识别频率优化 :在等待战斗的长时间段内,不需要高频进行图像识别。可以采用“休眠+间歇性检测”的策略。

5.4 用户体验 为脚本增加一个简单的GUI控制界面(可以用Tkinter、PyQt实现)是非常有价值的。界面可以显示当前状态(“运行中”、“已停止”、“遇到错误”)、日志预览,以及提供“开始”、“暂停”、“停止”按钮和配置修改入口。这能让脚本更像一个产品,而非一段冷冰冰的代码。

回顾整个项目,从构思到实现,最大的收获不是做出了一个能“代肝”的工具,而是系统地实践了“感知-决策-执行”这一自动化核心框架。图像识别技术就像项目的“眼睛”,而健壮的逻辑和异常处理则是“大脑”和“小脑”。每一个等待时间的微调,每一个识别阈值的测试,都是对细节的打磨。这个过程让我对Python在自动化领域的应用、OpenCV的基本操作以及软件鲁棒性设计有了更深的理解。当然,我必须再次强调,此类脚本应严格用于个人学习与研究,并充分尊重游戏运营方的规则,避免对游戏环境和其他玩家造成影响。技术的乐趣在于创造和解决问题的过程本身,这才是这个项目带给我的最大价值。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

更多推荐