# BlueStacks マクロの画像認識と LDPlayer のループ

> 画像認識を使った BlueStacks マクロの作り方: Asset Lab でテンプレートを切り出し、BlueStacks や LDPlayer で照合し、目的の状態が出るまでループします。

Source: https://automationmacro.com/ja/docs/guides/image-recognition-macros (ガイド, updated 2026-09-05)

BlueStacks や LDPlayer の画像認識マクロは、動く前に画面を見ます。ボタンがそこにあるからタップし、ポップアップが出たから閉じ、「エネルギー切れ」のメッセージが表示されたら止まります。このガイドでは、Macro Automation Studio (MAS) で BlueStacks か LDPlayer 上に、最初のテンプレートの切り出しから、状態を待つループまでを作ります。エミュレータ内蔵のレコーダーでは物足りなくなった方向けです。

## レコーダーが分岐できない理由

BlueStacks や LDPlayer のマクロレコーダーは、タップとタイミングを保存して再生します。画面はまったく見ません。ポップアップがボタンを覆ったこと、今日は読み込み画面が長かったこと、カウンターがゼロになったことを判断できません。記録されたマクロはすべて一本道です。

MAS のマクロは Python スクリプトです。各 `mas.find_object` 呼び出しはスクリーンショットを撮り、OpenCV のテンプレートマッチングでその中からテンプレート画像を探します。最良の一致がしきい値 (既定は `threshold=0.8`) に達すると、呼び出しはその中心を返します。そうでなければ `None` を返します。この 1 つの `if` が、マクロに分岐、待機、リトライ、停止をさせるものです。

## 始める前に

- MAS がインストール済みで、トライアルかプランでサインインしていること。トライアルは [無料ダウンロード](/download) から始まります。[MAS のインストール](/docs/install) を参照してください。
- BlueStacks か LDPlayer が ADB を有効にして動いており、デバイスとして追加されていること。[BlueStacks](/docs/bluestacks-setup-guide) か [LDPlayer](/docs/ldplayer-setup-guide) のガイドを参照してください。
- エミュレータが、今後も変えない固定の解像度を使っていること。ガイドは縦向きの 540x960、240 DPI を基準にしています。
- コードベースのプロジェクトが Code Editor で開いていること。[はじめに](/docs/getting-started) を参照してください。

## Asset Lab でテンプレートを取り込む

1. アプリを、見つけたいボタンのある画面にします。
2. Code Editor で **アセット** パネルを開き、**Asset Helper を開く** をクリックします。Asset Lab がデバイスのライブ画面で開きます。
3. ボタンの周りをぴったり囲む長方形を切り出して保存します。切り抜きは画像ライブラリに入り、数値の ID を受け取ります。
4. マクロが認識すべきすべての状態について繰り返します: ポップアップの閉じるボタン、確認ボタン、「完了」のメッセージ。
5. **アセット** パネルで各画像の **ID をコピー** をクリックし、スクリプトの先頭で宣言します。

```python
import mas

images = mas.images({
    "play_button": 201,
    "close_popup": 202,
    "out_of_energy": 203,
})
```

`mas.images` は各 ID に読みやすい名前を与え、公開時にどの画像を同梱するかをパッカーに伝えます。テンプレートは jpg、png、gif、webp で、1 枚 10 MB までです。[Asset Lab](/docs/asset-lab) を参照してください。

> [!TIP]
> 変わらない部分を切り出してください。数字の載ったボタンはその数字にしか一致しません。代わりに数字の横のアイコンを切り出します。

## 適切な呼び出しを選ぶ

| 呼び出し | 使う場面 | 戻り値 |
|---|---|---|
| `find_object(image)` | 今の画面を 1 度だけ見たい | `ObjectMatch` または `None` |
| `find_object_retry(image, total_tries=3, time_sleep=2.0)` | 対象が現れるまで少し時間がかかるかもしれない | `ObjectMatch`、または最後の試行後に `None` |
| `find_any_object([a, b, c])` | 複数のテンプレートのどれでもよい: ポップアップの変種、2 つのテーマ | 最初の一致、`matched_template_id` 付き |
| `find_objects(image, max_matches=10)` | 同じアイコンが複数回現れ、すべて欲しい | 信頼度順に並んだリスト |

`find_object_retry` は標準の待機プリミティブです: `find_object` を最大 `total_tries` 回呼び、失敗した試行の間に一定の `time_sleep` を挟み、最後の試行の後には待ちません。他のすべてのキーワード (`threshold`、`search_region`、`screenshot`) は `find_object` に渡されます。`find_any_object_retry` はリストに対して同じことをします。古いマクロのために残されている `wait_for_object` より、これらを使ってください。

```python
match = mas.find_object_retry(images.play_button, total_tries=5, time_sleep=1.5)
if match:
    mas.click(match.x, match.y, delay_ms=1000)
else:
    mas.log("Play button did not appear", level="warning")
```

`ObjectMatch` には `x`、`y`、`center`、`matched_template_id` があります。スコアは持たないので、後から信頼度を読むのではなく、呼び出しでしきい値を調整してください。

## search_region で検索を絞る

テンプレートマッチングは、スクリーンショット全体の上でテンプレートをスライドさせます。`Region` はスライドを長方形に限定するので、速くなり、画面の他の場所にある似たものを避けられます。座標は左上からのピクセルです。

```python
from mas import Region

TOP_BAR = Region(x1=0, y1=0, x2=540, y2=120)

energy_icon = mas.find_object(images.energy_icon, search_region=TOP_BAR)
```

要素の場所が分かっているときは常に領域を使ってください: カウンターなら上部バー、アクションボタンなら下部、ダイアログなら中央。領域は Asset Lab から読み取ります。座標ピッカーがライブ画面上のピクセル座標を示します。

## しきい値を調整する

`threshold` は 0.0 から 1.0 で、既定は 0.8 です。一致とみなすには、これに達する必要があります。

- アンチエイリアス、わずかな拡大縮小、ボタンの発光効果のせいで正しいテンプレートを見失い続けるときは、0.7 に下げます。
- テンプレートが間違った場所に一致するとき、たとえば似たアイコンが 2 つ並んでいたり、グレーアウトしたボタンも現れたりするときは、0.9 に上げます。
- 0.7 より下げる前に切り出し直してください。そこまで低いしきい値は、同じ色のほぼ何でも受け入れます。

```python
enabled = mas.find_object(images.claim_enabled, threshold=0.9)   # strict: disabled and enabled look alike
glowing = mas.find_object(images.reward_icon, threshold=0.7)     # lenient: the icon has a pulsing glow
```

## find_any_object でポップアップを処理する

ポップアップは、記録されたマクロが壊れる原因です。すべてのポップアップを知っている関数を 1 つマクロに持たせ、各ループの先頭で呼びます。

```python
POPUPS = [images.close_popup, images.confirm_button, images.later_button]


def clear_popups():
    popup = mas.find_any_object(POPUPS)
    if popup is None:
        return False
    mas.log(f"Closing popup {popup.matched_template_id}")
    mas.click(popup.x, popup.y, delay_ms=800)
    return True
```

`find_any_object` はリストを受け取り、最初の一致を返します。`search_strategy="best_match"` は代わりにリスト全体で最高スコアを選び、`"priority_order"` は並べた順にテンプレートを試します。`True` を返すとループが `continue` するので、次の周回はきれいな画面を見ます。

## 状態を待つ LDPlayer マクロループ

待機とは、期限付きのループです。`find_object_retry` は短い待機をカバーします。1 分かかることのある読み込み画面には、進捗を記録してきれいに諦められるよう、ループを自分で書きます。

```python
import time

def wait_for(image, timeout_s=60, every_s=2.0):
    deadline = time.monotonic() + timeout_s
    while time.monotonic() < deadline:
        match = mas.find_object(image)
        if match:
            return match
        time.sleep(every_s)
    return None
```

`time.monotonic()` は秒を数え、時計が変わっても跳ばないため、期限に適したタイマーです。すべての待機には終わりが必要です: `None` を返し、実行を止めるかどうかは呼び出し側に決めさせます。

## 画像認識を使った完全な BlueStacks マクロ

このスクリプトは画面を開き、ポップアップを片付け、有効な間ボタンをタップし、「エネルギー切れ」のテンプレートが現れるか時間の予算が尽きたら止まります。画像 ID を画像ライブラリのものに置き換えてください。

```python
import random
import sys
import time

import mas
from mas import Region

images = mas.images({
    "play_button": 201,
    "close_popup": 202,
    "confirm_button": 203,
    "out_of_energy": 204,
    "home_screen": 205,
})

BOTTOM = Region(x1=0, y1=700, x2=540, y2=960)
TIME_BUDGET_S = 15 * 60


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


def main():
    home = mas.find_object_retry(images.home_screen, total_tries=10, time_sleep=3.0)
    if home is None:
        mas.log("Home screen never appeared", level="error")
        sys.exit(2)

    started = time.monotonic()
    taps = 0
    while time.monotonic() - started < TIME_BUDGET_S:
        if clear_popups():
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy, done")
            break
        button = mas.find_object_retry(images.play_button, total_tries=3, time_sleep=2.0, search_region=BOTTOM)
        if button is None:
            mas.log("Play button not found, looking again", level="warning")
            continue
        mas.click(button.x, button.y, delay_ms=1000)
        taps += 1
        time.sleep(random.uniform(0.8, 2.0))

    mas.log(f"Finished after {taps} taps")


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

Code Editor で **実行** (<kbd>F5</kbd>) で実行し、コンソールを見ます。テンプレートを見失ったら、まず切り抜きを、次にしきい値を直します。

## トラブルシューティング

### テンプレートがまったく一致しない

切り抜きが、デバイスが今動いている解像度や DPI と違うもので撮られた、切り抜きに変化した背景が含まれている、または要素がアニメーションしています。エミュレータのディスプレイ設定を取り込み時のものと比べ、より小さく切り出し直し、`threshold=0.7` を試してください。要素に複数の見た目があるなら、それぞれ取り込んで `find_any_object` を使います。`mas.ImageNotFoundError` は、その ID が画像ライブラリにまったくないことを意味します。**アセット** パネルからもう一度コピーしてください。

### 間違ったものに一致する

テンプレートが汎用的です: 単純な矢印、一色の四角、2 回出てくる単語。隣にある固有のものを切り出すか、しきい値を 0.9 に上げるか、要素のある場所に検索を留める `search_region` を渡します。`find_objects` はテンプレートがしきい値を超えたすべての場所を示すので、似たものを見つけやすくなります。

### 1 つのインスタンスでは動くが、別のインスタンスで失敗する

2 つ目のエミュレータインスタンスが別の解像度か DPI で動いています。テンプレートマッチングはピクセルベースなので、540x960 で取り込んだテンプレートは 720x1280 や別の DPI では一致しません。すべてのインスタンスを同じディスプレイ設定にして再起動してください。クラウドデバイスでは、取り込んだのと同じディスプレイプリセットでデバイスを作ります。インスタンスを変えざるを得ないなら、解像度ごとにテンプレートのセットを取り込み、`mas.get_screen_size()` で選びます。

100%リスクのない自動化ツールは存在しないため、自動化は責任を持って、ご自身の判断で行ってください。
