# 游戏脚本制作教程：用 MAS 给任何安卓游戏做脚本

> 在 Macro Automation Studio 中给任何安卓游戏制作脚本的游戏脚本制作教程：从 Marketplace 安装、让 MAS Agent 编写，或在 BlueStacks 上用 Python 自己写。

Source: https://automationmacro.com/zh-CN/docs/make-a-bot-for-any-game (教程, updated 2026-09-05)

这篇游戏脚本制作教程介绍如何在 Macro Automation Studio（MAS）中给任何安卓游戏做脚本，从一个已安装的游戏，到一个按计划、在一个或多个账号上运行的脚本。MAS 脚本从屏幕出发工作：用图像找按钮，用 OCR 读计数器，用拟人化的节奏点击。适合在 Windows 或 Mac 上第一次做脚本的人。

<div class="doc-video" data-video="SDrdMIV49a8" data-title="How to make a bot for any game"></div>

## 开始之前

- 已安装 MAS 并用免费试用或套餐登录。参见[安装 MAS](/docs/install)。
- 游戏运行在 MAS 能驱动的设备上：这台电脑上的模拟器、一台云设备，或你自己的手机。参见[设备](/docs/devices)。
- 设备已在某个设备组中，卡片显示已停止而不是错误。[快速开始](/docs/getting-started)页演示了如何添加。
- 走 Python 路线不需要其他任何东西。MAS 自带 Python 环境和 `mas` 包。

## 安卓游戏脚本怎么做：三种方式

### 从 Marketplace 安装

已经有人为你的游戏做好了脚本时，这是最快的路线。

1. 打开 **Marketplace**，搜索游戏。
2. 打开条目，点击**下载**。脚本会出现在**宏**页面。
3. 在**设备组**中打开你的组，在设备的**宏**选择器里选中这个脚本，点击**启动**。

带截图的完整流程见[如何运行 Marketplace 中的宏](/docs/run-macro-from-marketplace)。现成脚本有 [Whiteout Survival](/whiteout-survival-bot)、[Kingshot](/kingshot-bot) 和 [Last Asylum: Plague](/last-asylum-plague-bot)。

### 让 MAS Agent 编写

没有现成条目的游戏，可以走这条无代码路线。

1. 打开 **Agent**，选择设备。
2. 用一句话描述流程，包括什么时候该停，然后点击**编写**。
3. Agent 提问时回答它。它会停下来问，而不是猜。
4. 宏通过 3 次验证运行后，点击**添加到我的宏**。

编写过程消耗 AI 积分；运行完成的宏不消耗任何积分。结果是一个可以打开和编辑的普通 Python 项目。参见 [MAS Agent](/docs/agent)。

### 用 Python 自己写

完全掌控脚本做的每一个决定。本页剩下的内容就是这条路线。

## 用 Python 自动化安卓游戏

### 1. 列出脚本必须看到的东西

手动玩一遍流程，记下它经过的每个屏幕。也就是你按的按钮、打断你的弹窗、显示体力的计数器，以及表示完成的提示。每一项都会变成一张模板图片或一个 OCR 区域。知道自己停止状态的脚本，永远不会盲跑。

### 2. 创建项目

1. 打开**宏**，点击**新建项目**。
2. 选择**代码型**，把**目标设备**设为移动端，给项目命名，点击**创建项目**。
3. 打开项目。Code Editor 会显示 `src/app.py`，也就是 MAS 运行的文件。

### 3. 在 Asset Lab 中截取模板

1. 在设备上启动游戏，进入有那个按钮的屏幕。
2. 在 Code Editor 中打开**资源**面板，点击**打开资源助手**。Asset Lab 会带着实时画面打开。
3. 紧贴按钮裁一个矩形并保存。它会带着一个数字 ID 进入你的图片库。
4. 对弹窗的关闭按钮、确认按钮和"体力不足"提示重复这一步。
5. 回到**资源**面板，对每张图片使用**复制 ID**，把 ID 粘贴到脚本顶部的 `mas.images` 中。

裁剪要小而有辨识度。整屏截图只能匹配那一个屏幕；一个按钮在任何出现的地方都能匹配。模板可以是 jpg、png、gif 或 webp，单张最大 10 MB。[Asset Lab](/docs/asset-lab) 页详细介绍这个工具。

### 4. 截取 OCR 区域

1. 在 Asset Lab 中，围绕你要读的计数器画一个框。
2. 运行实时 OCR 测试，收紧框，直到数字能干净地读出来。
3. 把坐标复制到脚本里的 `Region(x1, y1, x2, y2)`。

如果文字读得不好，[OCR 指南](/docs/guides/ocr-text-reading)解释了分割模式和颜色转换。

### 5. 写循环

下面的循环是每个游戏脚本共有的形状。每一行都对应一个 SDK 调用：

- `find_object_retry` 最多找 `total_tries=3` 次，每次间隔 `time_sleep=2.0` 秒，没有匹配时返回 `None`。
- `click(x, y, delay_ms=1000)` 点击匹配的中心，然后等一秒让游戏响应。
- `read_text(region, psm=7)` 把计数器当作单行读取。
- `time.sleep(random.uniform(0.8, 2.0))` 在轮次之间加入像人一样的停顿。
- `mas.save` 和 `mas.retrieve` 保存轮次计数，所以中断的运行会从上次的位置继续。
- 停止条件结束循环。示例用了五个：最大轮次、时间预算、"体力不足"模板、计数器过低，以及连续找不到太多次。

### 6. 运行并阅读日志

在 Code Editor 中选择设备，点击**运行**（<kbd>F5</kbd>）。每条 `mas.log` 都会出现在运行控制台。**停止**是 <kbd>Shift</kbd>+<kbd>F5</kbd>。一次只改一处：模板找不到时，先重新裁剪，再碰逻辑。

### 7. 设置定时

1. 打开**定时任务**，点击**新建计划**。
2. 填写**名称**，选择**宏**和**模拟器端口**，设置**日期**和**时间**。
3. 把**重复**设为**每天**，点击**创建计划**。

应用必须保持打开；定时任务在应用内部运行。参见[定时任务](/docs/scheduler)和[循环与定时](/docs/guides/loops-and-scheduling)。

### 8. 在设备组上运行

把每个账号的模拟器实例作为设备添加到同一个组，给每台设备分配脚本，点击**全部启动**。账号名这类按设备不同的值来自设置方案。参见[多开挂机](/docs/guides/multi-instance)。

## 一个完整的示例脚本

把图片 ID 和区域替换成你截取的。这个脚本刷资源，直到体力过低、时间预算用完或完成三十轮为止，并且能在重启后继续。

```python
import random
import sys
import time

import mas
from mas import Region

images = mas.images({
    "attack_button": 101,
    "confirm_button": 102,
    "close_popup": 103,
    "out_of_energy": 104,
})

ENERGY_REGION = Region(x1=380, y1=20, x2=520, y2=60)
TASK = "farm_bot"
MAX_ROUNDS = 30
TIME_BUDGET_S = 20 * 60
MIN_ENERGY = 10


def pause(low=0.8, high=2.0):
    time.sleep(random.uniform(low, high))


def read_energy():
    result = mas.read_text(region=ENERGY_REGION, psm=7)
    digits = "".join(ch for ch in result.text if ch.isdigit())
    return int(digits) if digits else None


def clear_popups():
    popup = mas.find_any_object([images.close_popup, images.confirm_button])
    if popup:
        mas.click(popup.x, popup.y, delay_ms=800)
        return True
    return False


def main():
    rounds = mas.retrieve(TASK).get("rounds", 0)
    misses = 0
    started = time.monotonic()
    mas.log(f"Starting at round {rounds}")

    while rounds < MAX_ROUNDS:
        if time.monotonic() - started > TIME_BUDGET_S:
            mas.log("Time budget reached", level="warning")
            break
        if clear_popups():
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy, stopping")
            break
        energy = read_energy()
        if energy is not None and energy < MIN_ENERGY:
            mas.log(f"Energy {energy} is below {MIN_ENERGY}, stopping")
            break
        button = mas.find_object_retry(images.attack_button, total_tries=3, time_sleep=2.0)
        if button is None:
            misses += 1
            mas.log(f"Attack button not found ({misses})", level="warning")
            if misses >= 5:
                mas.log("Giving up after 5 misses", level="error")
                sys.exit(2)
            continue
        misses = 0
        mas.click(button.x, button.y, delay_ms=1000)
        pause()
        rounds += 1
        mas.save(TASK, {"rounds": rounds})
        mas.log(f"Round {rounds} of {MAX_ROUNDS}")

    if rounds >= MAX_ROUNDS:
        mas.clear(TASK)
    mas.log(f"Finished with {rounds} rounds")


if __name__ == "__main__":
    main()
```

`sys.exit(2)` 把这次运行标记为失败，所以订阅了 `macro.failed` 的 Webhook 会收到通知。正常退出报告 `macro.completed`。参见 [Webhook](/docs/webhooks)。

## 让脚本保持稳定的技巧

- **分辨率。** 模板和区域属于你截取时的分辨率和 DPI。让运行脚本的每台设备都保持同样的设置；模拟器指南以竖屏 540x960、240 DPI 为基准。
- **模板卫生。** 只裁按钮，绝不带周围的背景。游戏更新改了美术后重新裁剪。一个按钮有两种样子时，两种都截下来，用 `find_any_object` 搜索。
- **停止条件。** 每个循环至少需要两个：一个计数器或时间预算，加一个表示"完成"的屏幕状态。没有它们的脚本会一直跑到你注意到为止。
- **慢一点。** 轮次之间停一到两秒代价很小，看起来也没那么机械。
- **记录决定，而不是点击。** `mas.log("Energy 8, stopping")` 告诉你运行为什么结束；一百次点击的日志做不到。

## 可能出什么问题

### 按钮永远找不到

裁剪太大、阈值对这种美术太严格，或者设备运行的分辨率和你截取时不同。裁得更紧，然后在调用上试试 `threshold=0.7`。[图像识别指南](/docs/guides/image-recognition-macros)有完整的检查清单。

### 计数器读出错误的数字

区域包含了旁边的图标，或者文字是深色背景上的浅色。在 Asset Lab 中收紧框，并传入 `color_conversion=ColorConversion.BLACK_WHITE`。[OCR 指南](/docs/guides/ocr-text-reading)展示了如何检查置信度。

### 第二天脚本一启动就停

存储的计数器已经到了上限。示例在达到 `MAX_ROUNDS` 时清除存储；如果你改了这一点，从一个临时脚本调用一次 `mas.clear("farm_bot")`。

### 点击落在按钮旁边

截取之后模拟器的分辨率或 DPI 变了，或者窗口不是竖屏。把模拟器显示重置为截取时的设置，重新截取仍然点不准的部分。

没有任何自动化工具是 100% 无风险的，请负责任地使用自动化，并自行斟酌决定。
