ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python模拟鼠标实现像素画全自动绘制:图像量化与路径规划

Python模拟鼠标实现像素画全自动绘制:图像量化与路径规划 分享一套《明日方舟》基建画板全自动绘制像素画的完整脚本方案目前已经迭代到 V1.2。本版本在前一版的基础上加入了颜色抖动匹配、蛇形绘制路径、断点续绘和延迟容错机制解决了不少“画到一半就偏色”、“鼠标坐标整体偏移”的实际问题。如果你平时喜欢在游戏里做点小创作又不想把时间全耗在手工点像素上那这篇教程会很适合你。文章会尽量把原理拆开讲从图像取色、网格坐标换算再到模拟鼠标点击每一个环节都给出可直接运行的代码示例并附上常见报错的排查思路。1. 背景与核心概念1.1 什么是“全自动像素画绘制”先明确概念。这里说的“在游戏中绘制像素画”指的是利用游戏内置的“画板”或“基建涂鸦”功能在固定尺寸的网格区域中一格一格填充颜色最终拼出一张完整图片。这个功能本质上就是一个受限的像素画编辑器只不过操作方式比较基础只能通过鼠标点击来选色、填色。随着图片尺寸变大比如 32×32、64×64甚至更大手工操作就会变得非常痛苦。因为每一格都需要从色板中选择正确颜色将鼠标移动到对应位置点击一次可能还要重复点击两次确认换下一个位置继续。一张 64×64 的图就是 4096 个网格假设每格平均耗时 1.5 秒光填色就需要 100 分钟以上而且人眼长时间盯屏幕非常容易疲劳更别说中途出现颜色看错、点错位置这种低级失误。于是就有了脚本方案把“看图取色 移动鼠标 点击填充”这一整套动作交给程序自动完成。1.2 自动绘制的完整流程自动绘制脚本的核心闭环可以拆解为四个步骤图像预处理把任意图片缩放到目标网格尺寸并转换成游戏色板支持的颜色集合。坐标映射将像素坐标换算为游戏窗口内的屏幕坐标。路径规划决定先画哪一格、后画哪一格避免鼠标来回乱跳。自动执行通过模拟鼠标移动和点击逐格完成绘制。V1.2 版本在这个闭环上做了三个重要升级颜色匹配从“绝对匹配”升级为“色差最小匹配”即使源图和游戏色板有偏差也能自动选择最近颜色绘制路径从“逐行单向扫描”升级为“蛇形往返扫描”减少了鼠标空跑路程速度提升明显增加了中断恢复功能脚本意外退出后可以根据日志从上次进度继续。下面会一个一个环节展开并给出完整代码。1.3 适用读者这篇文章适合以下几类读者想在自己的游戏账号中实现“自动绘画”的玩家希望做一个有一定工程性的小工具正在学习 Python 图像处理和 GUI 自动化的开发者想找一个有趣的练手项目已经实现过基础版本但对颜色匹配、绘制路径、异常恢复等细节有优化需求的人。本文示例基于 Python 实现不依赖复杂框架核心库只用到 Pillow、NumPy、pyautogui代码逻辑也比较清晰新手跟着做也可以跑通。2. 环境准备与版本说明在正式写代码之前先把开发环境准备好。2.1 开发环境清单为了减少环境兼容性问题本文按以下环境描述类别推荐配置操作系统Windows 10/1164 位Python 版本Python 3.9 及以上游戏窗口《明日方舟》官方 PC 客户端窗口模式运行屏幕分辨率1920×1080高 DPI 缩放画面需要调整缩放系数图像处理库Pillow、NumPy自动化库pyautogui、pygetwindow开发 IDEVS Code 或 PyCharm 均可注意如果你使用的不是 Windows而是 macOS 或 Linuxpygetwindow的窗口管理能力会受限坐标获取方式也需要相应调整。本文以 Windows 环境为例来写其他系统可以参考同样的思路适配。2.2 依赖库安装打开终端执行以下命令安装依赖pip install pillow numpy pyautogui pygetwindow opencv-python这里解释一下每个库的作用pillow负责读取图片、缩放图片、转换颜色模式。numpy用于处理像素矩阵实现高效的逐像素颜色匹配。pyautogui模拟鼠标移动、点击是自动化执行的核心模块。pygetwindow获取游戏窗口句柄从而计算窗口在屏幕中的位置。opencv-python用于图像降噪和边缘平滑对颜色过渡复杂的图片有很好的预处理效果。安装完成后可以运行下面这段代码验证环境import PIL import numpy import pyautogui import pygetwindow print(Pillow version:, PIL.__version__) print(NumPy version:, numpy.__version__) print(pyautogui version:, pyautogui.__version__)如果能正常输出版本信息说明环境基本可用。2.3 项目目录结构为了让代码便于维护建议创建一个干净的目录结构arknights_pixel_art/ ├── main.py # 主入口脚本 ├── color_quantize.py # 颜色匹配/量化模块 ├── path_planner.py # 绘制路径规划模块 ├── executor.py # 鼠标自动化执行模块 ├── config.json # 参数配置色板、延迟、坐标校准 ├── images/ │ └── input.png # 原始输入图片 ├── logs/ │ └── progress.json # 断点续绘状态文件 └── output/ └── preview.png # 模拟预览图片下面各节的代码会按照这个结构分别写进对应文件。这样独立模块清晰后续调试任何一个环节都很方便。3. 核心原理拆解3.1 尺寸缩放与网格映射游戏里的“画板”是固定网格尺寸的。例如某个区域可能只支持 32×32 网格也可能支持 64×64。实际操作时你需要先确认自己的画板尺寸然后把源图片缩放成同样的宽高。图像缩放不是简单地把图片压扁而是要同时考虑内容保真度。缩放公式目标尺寸 (画板宽度, 画板高度)使用 Pillow 可以非常方便地完成这个操作from PIL import Image def resize_to_grid(image_path, grid_width, grid_height, output_path): 将输入图片缩放为 grid_width x grid_height 大小的网格图。 img Image.open(image_path) img img.resize((grid_width, grid_height), Image.LANCZOS) img.save(output_path) return img这里使用Image.LANCZOS重采样算法适合缩小带有复杂纹理的图片可以保留更多细节。如果图片本身颜色比较平整用默认的双线性算法也可以。缩放完成后图片的每一个像素点就对应画板上的一个格子。这个映射关系是后续所有逻辑的基础。3.2 颜色量化与色差匹配游戏画板通常只提供有限的固定颜色比如 32 色、64 色而现实图片的颜色往往是几十万种。直接使用原图颜色是画不出来的必须做颜色量化Color Quantization即把每个像素映射到色板中最接近的颜色。这里最常用的算法是最小欧氏距离匹配。在 RGB 颜色空间中每种颜色都可以看作一个三维向量两个颜色的差异就是向量距离distance sqrt((r1-r2)^2 (g1-g2)^2 (b1-b2)^2)距离越小说明颜色越接近。实际计算时为了性能可以不用开平方直接比较平方和。下面是完整实现import numpy as np # 游戏色板示例这里以 16 色为例实际请替换为游戏内置色板 PALETTE [ (255, 255, 255), # 白色 (0, 0, 0), # 黑色 (255, 0, 0), # 红色 (0, 255, 0), # 绿色 (0, 0, 255), # 蓝色 (255, 255, 0), # 黄色 (255, 0, 255), # 品红 (0, 255, 255), # 青色 (128, 128, 128), # 灰色 (255, 128, 0), # 橙色 (128, 0, 255), # 紫色 (0, 128, 128), # 深青 (128, 128, 0), # 橄榄色 (128, 0, 0), # 深红 (0, 128, 0), # 深绿 (0, 0, 128), # 深蓝 ] def find_nearest_color(pixel, paletteNone): 返回像素在色板中最接近的颜色索引。 if palette is None: palette PALETTE pixel np.array(pixel, dtypenp.int16) palette_arr np.array(palette, dtypenp.int16) # 计算每个色板颜色的欧氏距离平方 diff palette_arr - pixel dist_sq np.sum(diff ** 2, axis1) nearest_index int(np.argmin(dist_sq)) return nearest_index def quantize_image(img, paletteNone): 将 RGBA 图片量化到指定色板返回颜色索引矩阵和预览图片。 if palette is None: palette PALETTE img img.convert(RGB) pixels np.array(img, dtypenp.int16) h, w, _ pixels.shape # 预计算色板数组 palette_arr np.array(palette, dtypenp.int16) # 展平像素并批量计算距离 flat_pixels pixels.reshape(-1, 3) diff flat_pixels[:, None, :] - palette_arr[None, :, :] dist_sq np.sum(diff ** 2, axis2) idx np.argmin(dist_sq, axis1) idx_matrix idx.reshape(h, w) # 生成预览图像 preview_pixels palette_arr[idx].reshape(h, w, 3) preview_img Image.fromarray(preview_pixels.astype(np.uint8)) return idx_matrix, preview_img这里有个需要注意的地方色板顺序必须和游戏内色板的顺序保持一致否则即使颜色匹配正确点击选色时也会选错。quantize_image返回两个东西idx_matrix一个二维数组每个元素代表该位置使用的色板索引preview_img生成的预览图片方便你事先在本地查看绘制效果。通过生成预览图可以提前确认颜色还原度是否符合预期避免脚本跑了一半才发现颜色效果不对。3.3 坐标换算从网格到屏幕颜色索引矩阵生成后第二步就是把网格坐标换算成游戏窗口内的屏幕坐标。这一步是整个脚本最容易出错的地方因为涉及三个坐标系画板网格坐标例如(0, 0)是画板左上角(31, 31)是右下角。游戏窗口坐标相对于窗口客户区左上角的位置。屏幕全局坐标相对于显示器左上角的位置。pyautogui使用的是屏幕全局坐标所以需要做一次换算公式如下screen_x window_left left_margin col * cell_width cell_width / 2 screen_y window_top top_margin row * cell_height cell_height / 2其中left_margin和top_margin是画板左上角到窗口客户区左上角的偏移cell_width和cell_height是每一格的实际像素宽度和高度。由于每个显示器的分辨率和游戏窗口大小不同最稳的方式是做一个“校准模式”启动脚本后先让用户手动点击画板的左上角和右下角通过这两个点计算出每个格子的实际尺寸。核心代码如下import pyautogui def calibrate_origin(): 手动校准请用户依次点击画板左上角和右下角。 print(请将鼠标移动到画板左上角5 秒后自动获取坐标...) pyautogui.sleep(5) x1, y1 pyautogui.position() print(请将鼠标移动到画板右下角5 秒后自动获取坐标...) pyautogui.sleep(5) x2, y2 pyautogui.position() return (x1, y1), (x2, y2)得到画板左上角和右下角坐标后就可以计算出每一格的尺寸def calc_grid_parameters(tl, br, grid_width, grid_height): tl 是左上角坐标br 是右下角坐标。 left, top tl right, bottom br cell_width (right - left) / grid_width cell_height (bottom - top) / grid_height return { left: left, top: top, cell_width: cell_width, cell_height: cell_height, }然后根据网格位置(row, col)计算屏幕坐标def grid_to_screen(row, col, grid_params): left grid_params[left] top grid_params[top] cell_w grid_params[cell_width] cell_h grid_params[cell_height] x left col * cell_w cell_w / 2 y top row * cell_h cell_h / 2 return int(x), int(y)这一小段是整个脚本精确度的关键。如果你发现绘制的点有规律地偏移半格、一格大概率就是这里计算有误或者校准点时没对准。3.4 绘制路径规划早期的简单脚本是“逐行从左到右扫描”画完第一行再画第二行。但这种方式的缺点很明显如果画板网格很大鼠标从每行末尾回到下一行开头需要穿越很长距离而且频繁长距离移动容易触发误操作或卡顿。V1.2 改用蛇形路径Snake Pattern也就是第一行从左到右第二行从右到左第三行再从左到右。这样每次换行时鼠标只移动一格的距离大幅降低了无效移动。路径规划代码如下def generate_snake_path(grid_width, grid_height, start_row0): 生成蛇形绘制路径。 返回的路径元素为 (row, col)。 path [] for row in range(start_row, grid_height): if row % 2 0: # 偶数行从左到右 for col in range(grid_width): path.append((row, col)) else: # 奇数行从右到左 for col in range(grid_width - 1, -1, -1): path.append((row, col)) return path3.5 自动执行模块有了路径和坐标自动执行模块就很简单了。每一格需要两步动作点击色板中的目标颜色点击画板中对应网格位置。这里需要注意的是点击颜色后游戏可能需要极短的响应时间因此必须在两次点击之间加入稳定延时否则会出现“颜色还没切换成功就已经点击了网格”的问题。执行器核心代码如下import pyautogui import time # 配置示例 DELAY_CLICK_COLOR 0.3 # 点击色板后的等待时间 DELAY_CLICK_GRID 0.15 # 点击网格后的等待时间 DELAY_BETWEEN_PIXELS 0.2 SAFETY_STOP_HOTKEY esc # 紧急停止热键 def execute_draw(idx_matrix, grid_params, palette_buttons, delay_colorDELAY_CLICK_COLOR, delay_gridDELAY_CLICK_GRID, delay_pixelDELAY_BETWEEN_PIXELS, progressNone): idx_matrix: 颜色索引矩阵 grid_params: 网格坐标参数 palette_buttons: 记录每个颜色索引对应的屏幕坐标 progress: 断点续绘信息包含已完成的行数 grid_h, grid_w idx_matrix.shape start_row 0 if progress and row in progress: start_row progress[row] 1 path generate_snake_path(grid_w, grid_h, start_rowstart_row) for step, (row, col) in enumerate(path): if pyautogui.keyboard.pressable_keys and pyautogui.is_pressed(SAFETY_STOP_HOTKEY): print(检测到 ESC已停止绘制。) save_progress(row, col) return color_idx int(idx_matrix[row][col]) color_pos palette_buttons[color_idx] grid_pos grid_to_screen(row, col, grid_params) # 点击色板选色 pyautogui.click(color_pos[0], color_pos[1]) time.sleep(delay_color) # 点击网格填色 pyautogui.click(grid_pos[0], grid_pos[1]) time.sleep(delay_grid) # 像素间隔延时 time.sleep(delay_pixel) # 每隔一定步数打印一次进度 if (step 1) % 100 0: print(f已完成 {step 1} / {grid_h * grid_w} 格) print(绘制完成)这里有几个工程上的细节值得展开说明。延时设置延时并不是越大越好。延时过大会让绘制时间成倍增加延时过小则可能导致操作还没被游戏识别就执行下一步出现丢色块。建议根据自己电脑性能和网络状况调整一般 0.2 ~ 0.4 秒是比较安全的范围。紧急停止脚本执行过程中如果发现鼠标轨迹不对需要立即停下来。代码中加入了 ESC 热键检测但这个逻辑在部分系统/窗口下可能失效最保险的做法是绘制前把鼠标移到屏幕角落必要时直接移动鼠标或强制退出进程。进度打印每 100 格打印一次进度方便观察整体状态避免脚本“卡死”后还以为在正常运行。3.6 断点续绘与日志保存V1.2 一个比较实用的更新就是断点续绘。因为在长时间绘制过程中可能出现游戏弹窗、网络波动、电脑休眠等意外情况如果每画一格都报错退出整个流程就中断了。引入断点续绘后只需要从上次未完成的位置继续跑就可以了。断点信息保存到logs/progress.jsonimport json import os LOG_DIR logs LOG_FILE os.path.join(LOG_DIR, progress.json) def save_progress(row, col): data {row: row, col: col} os.makedirs(LOG_DIR, exist_okTrue) with open(LOG_FILE, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2) def load_progress(): if not os.path.exists(LOG_FILE): return None with open(LOG_FILE, r, encodingutf-8) as f: return json.load(f)在execute_draw中如果检测到progress.json存在就会从start_row progress[row] 1开始继续。这里有一点需要特别注意因为蛇形路径是每行换向的如果从某一行中间开始必须保留该行当前的行号和列号否则换向会错乱。上面代码简化了这个逻辑只实现了“从整行开始”的版本如果想精确到某一列需要在generate_snake_path中增加起始列参数。4. 完整实战流程下面把上面的模块串联起来写一个完整的实战流程。假设我们要绘制一张 32×32 的像素画。4.1 第一步准备输入图片先把目标图片放到images/input.png。如果没有合适的图片可以用下面的代码生成一张简单的测试图方便验证流程from PIL import Image import numpy as np # 创建一张 64x64 的彩色渐变图作为测试输入 w, h 64, 64 arr np.zeros((h, w, 3), dtypenp.uint8) for y in range(h): for x in range(w): arr[y, x] [x * 4 % 256, y * 4 % 256, 128] Image.fromarray(arr).save(images/input.png) print(测试图片已生成images/input.png)这张图包含丰富的色彩渐变在颜色匹配阶段能比较明显地看出量化效果。4.2 第二步读取配置配置文件统一放在config.json内容包括画板尺寸、色板、延时参数等{ grid_width: 32, grid_height: 32, delay_click_color: 0.3, delay_click_grid: 0.15, delay_between_pixels: 0.2, palette: [ [255, 255, 255], [0, 0, 0], [255, 0, 0], [0, 255, 0], [0, 0, 255], [255, 255, 0], [255, 0, 255], [0, 255, 255], [128, 128, 128], [255, 128, 0], [128, 0, 255], [0, 128, 128], [128, 128, 0], [128, 0, 0], [0, 128, 0], [0, 0, 128] ] }4.3 第三步主入口脚本main.py负责整个流程编排import json import os import time from PIL import Image from color_quantize import quantize_image from path_planner import generate_snake_path from executor import execute_draw, grid_to_screen, calc_grid_parameters def load_config(pathconfig.json): with open(path, r, encodingutf-8) as f: return json.load(f) def main(): config load_config() grid_w config[grid_width] grid_h config[grid_height] palette [tuple(c) for c in config[palette]] # 1. 读取图片并缩放 print(读取图片并缩放...) img Image.open(images/input.png) img img.resize((grid_w, grid_h), Image.LANCZOS) # 2. 颜色量化 print(颜色量化中...) idx_matrix, preview_img quantize_image(img, palette) preview_img.save(output/preview.png) print(预览图已保存到 output/preview.png请确认颜色效果。) input(按回车键继续...) # 3. 坐标校准 print(开始坐标校准...) tl None br None print(请将鼠标移动到画板左上角5 秒后获取坐标...) time.sleep(5) tl __import__(pyautogui).position() print(请将鼠标移动到画板右下角5 秒后获取坐标...) time.sleep(5) br __import__(pyautogui).position() grid_params calc_grid_parameters(tl, br, grid_w, grid_h) # 4. 色板按钮坐标映射 # 这里需要手动输入每个颜色在游戏色板中的屏幕坐标 # 为了简化这里用一个示例字典实际使用时请通过校准获取 palette_buttons { 0: (100, 500), # 白色 1: (130, 500), # 黑色 # 后续颜色坐标按实际位置补充 } # 5. 执行绘制 print(开始自动绘制...) execute_draw(idx_matrix, grid_params, palette_buttons, delay_colorconfig[delay_click_color], delay_gridconfig[delay_click_grid], delay_pixelconfig[delay_between_pixels]) if __name__ __main__: main()4.4 第四步色板按钮校准上文主脚本中palette_buttons是一个示例字典实际使用时每个颜色索引必须对应游戏内色板按钮的屏幕坐标。这一步是整个项目中“工程量”最大的部分因为不同电脑分辨率不同、游戏窗口位置不同、画板尺寸不同颜色按钮的位置都会变化。推荐做法是增加一个“色板校准模式”启动脚本后程序遍历每个颜色索引提示用户将鼠标移动到对应颜色按钮上回车确认记录坐标。这样第一次配置后后续同一分辨率下就可以复用不需要每次都校准。示例代码如下import pyautogui def calibrate_palette_buttons(palette_size): 手动校准每个色板按钮在屏幕中的坐标。 buttons {} print(f开始校准 {palette_size} 个色板按钮...) for i in range(palette_size): input(f请将鼠标移动到颜色索引 {i} 的按钮位置然后按回车...) x, y pyautogui.position() buttons[i] (x, y) print(f颜色索引 {i} - ({x}, {y})) return buttons4.5 第五步运行与验证运行完整流程python main.py预期过程程序读取图片并生成预览图打印“预览图已保存到 output/preview.png”等待用户按回车进入坐标校准按提示移动鼠标到画板左上角、右下角执行绘制终端每 100 格输出一次进度绘制完成游戏画板中显示最终像素画。在正式全屏执行之前建议先做一个“模拟测试”把execute_draw中的pyautogui.click替换成print输出当前目标和坐标确认路径和坐标没有明显错误后再真正操作。def dry_run(idx_matrix, grid_params): grid_h, grid_w idx_matrix.shape path generate_snake_path(grid_w, grid_h) for row, col in path: color_idx int(idx_matrix[row][col]) x, y grid_to_screen(row, col, grid_params) print(f({row}, {col}) - 颜色 {color_idx} - 屏幕 ({x}, {y}))这个 dry-run 模式调试坐标非常有用。5. 常见问题与排查思路5.1 画出来的颜色和原图差别很大现象整张图颜色灰蒙蒙的或者某个颜色大面积偏移。原因色板没有整理准确部分真实色板颜色没填进PALETTE图片本身颜色空间偏色比如源图是广色域而游戏色板范围更小色板按钮坐标校准错误导致实际点击了另一个颜色。排查方法先查看output/preview.png如果预览图效果很好说明颜色匹配逻辑没问题问题出在自动执行阶段检查palette_buttons坐标是否正确可以在脚本中增加“点击后读取屏幕像素颜色”的功能验证点击是否选中了目标颜色将源图先用图像处理软件做一次“色板转换”把颜色范围压缩到游戏色板近似区间再做量化效果会更好。5.2 鼠标点击位置整体偏移现象所有格子都画偏了比如整体向右上方偏移了半格。原因校准画板左上角时鼠标没有真正点到画板边缘Windows 高 DPI 缩放下pyautogui获取的坐标和实际显示比例不一致。排查方法重新执行校准确保左上角和右下角两个点尽量准确如果是高 DPI 屏幕在 Python 脚本最开头加上ctypes.windll.shcore.SetProcessDpiAwareness(1)让程序感知真实像素坐标。import ctypes try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: pass检查cell_width和cell_height是否合理如果明显偏大或偏小说明校准点有问题重新校准。5.3 绘制过程中鼠标乱跳现象脚本运行到一半鼠标突然跳到屏幕其他位置开始乱点。原因输入法状态切换导致快捷键冲突脚本运行时不小心碰到了鼠标或键盘某些系统通知弹出抢占了焦点。排查方法绘制过程中尽量不要动鼠标和键盘把紧急停止热键设置到最顺手的位置关闭系统中可能弹窗的软件比如聊天软件、系统更新提示在execute_draw中增加焦点检查每次点击前先确认游戏窗口是否处于前台。如果不是则延时等待或强制激活窗口。import pygetwindow as gw def ensure_window_active(window_title): windows gw.getWindowsWithTitle(window_title) if windows: win windows[0] if not win.isActive: win.activate() time.sleep(1)5.4 脚本执行到一半就卡住现象终端不再输出进度鼠标也没有反应。原因游戏内弹出了对话框挡住了绘制区域网络波动导致游戏重连鼠标点击被系统安全软件拦截。排查方法保存断点后手动恢复先退出脚本处理弹窗后再继续检查系统安全软件日志看是否拦截了模拟点击将延时适当调大给游戏更多响应时间。5.5 断点续绘时路径错乱现象从断点恢复绘制后绘制方向出现混乱部分格子被重复填充。原因断点只保存了行号或列号但蛇形路径的换向规则决定了恢复时机必须精确到“该行的起始列”。排查方法在save_progress中同时保存行号和列号generate_snake_path增加start_col参数恢复时从指定行列开始生成路径。def generate_snake_path(grid_width, grid_height, start_row0, start_col0): path [] for row in range(start_row, grid_height): if row % 2 0: cols range(grid_width) else: cols range(grid_width - 1, -1, -1) for col in cols: if row start_row and col start_col: continue path.append((row, col)) return path6. 最佳实践与工程建议6.1 颜色匹配前先做降噪和锐化原始图片如果来自网络截图往往带有压缩噪声直接缩放量化后会出现很多“噪点色块”即单个孤立格子颜色和周围差异很大。建议在缩放前用 OpenCV 做一次高斯模糊让颜色过渡更平滑再使用锐化核增强边缘轮廓。import cv2 import numpy as np def preprocess_image(image_path): img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.GaussianBlur(img, (3, 3), 0) sharp_kernel np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) img cv2.filter2D(img, -1, sharp_kernel) return Image.fromarray(img)6.2 色板坐标单独保存避免每次重新校准颜色按钮位置在同一个分辨率和窗口尺寸下是固定的建议第一次校准后保存到config.json中后续直接加载。{ palette_buttons: { 0: [100, 500], 1: [130, 500] } }这样脚本启动时如果检测到已有配置可以直接跳过色板校准。6.3 绘制前必须做 dry-run 模拟把execute_draw里真正点击的部分用日志替代先跑一遍完整流程确认坐标、路径、颜色选择都没有问题再加注执行。这个习惯能避免大量低级事故。6.4 启用日志记录在生产环境中所有关键步骤都要输出日志并且日志要包含时间戳和状态信息。这样如果脚本执行失败可以快速定位问题发生在哪个阶段。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s [%(levelname)s] %(message)s, handlers[ logging.FileHandler(logs/draw.log, encodingutf-8), logging.StreamHandler() ] )6.5 遵守游戏规则避免对正常玩家造成影响自动化脚本在游戏中使用时需要注意建议只用于学习编程和维护自己账号的娱乐内容不要影响他人控制绘制时长和频率避免长时间高频率操作导致账号异常如果游戏明确禁止脚本操作应先了解规则自行评估风险不要对规则置之不理。6.6 权限与安全边界脚本只应在自己本机、自己账号、可控制的范围内使用。不要将自动化能力扩展到攻击类、刷量类、破坏类场景中。涉及账号、密码、Token 等敏感信息时一律不要在脚本中硬编码应该通过环境变量或独立的配置文件管理并且配置文件不要提交到公开仓库中。7. 总结与学习路线到这里一套完整的《明日方舟》像素画自动绘制脚本就完成了。从整体流程来看核心环节包括图片缩放、颜色量化、坐标校准、路径规划和鼠标自动执行。V1.2 版本重点优化了颜色匹配精度、蛇形绘制路径和断点续绘机制让整个绘制过程的稳定性和效率都得到明显提升。如果你是从零开始接触这个项目建议先按顺序掌握这几个知识点图像基础RGB 颜色空间、Pillow 的基本操作、图片缩放算法NumPy 批量计算怎么用矩阵运算替代逐像素 for 循环理解颜色量化为什么用向量化计算pyautogui 坐标体系屏幕坐标系、窗口坐标系、元素定位的关系自动化测试思想dry-run 模式、断点续绘、日志追踪这些在真正的自动化测试项目里同样适用。下一步你可以尝试以下扩展方向加入“按区域批量绘制”把画板分成多个区块逐区域绘制减少大范围移动加入 OCR 识别自动检测当前游戏窗口位置和画板边界省去手动校准把脚本封装成带 GUI 的桌面应用支持拖拽图片、实时预览、一键启动把颜色量化改成流行的K-Means聚类算法让脚本自动从原图中提取最优色板而不是只用固定颜色。做这类工具最有意思的地方不只是最后画出了好看的像素画而是整个过程中用到的代码思路几乎可以平移到其他 GUI 自动化、图像识别、批量处理任务中。如果你也想在真实项目中写出这种“稳定、可追踪、容易恢复”的自动化程序这个项目是一个非常好的练手机会。如果这篇文章对你有帮助建议先收藏后续可以直接对照着把环境搭起来。所有代码模块都拆分清楚了从color_quantize.py到execute_draw每一步都可以独立测试排错难度也大大降低。遇到问题欢迎在评论区一起交流。
返回列表