検索

ガイド

LDPlayer マクロのループ、停止条件、スケジュール実行

LDPlayer や BlueStacks のマクロを停止条件付きでループさせ、実行間で進捗を保存し、終了コードを返し、MAS のスケジューラーで毎日実行する方法です。

  • Windows
  • Mac
  • エミュレータ
  • Studio
  • Python SDK
初級 更新日 約9分で読めます
このページの内容
  1. 始める前に
  2. LDPlayer と BlueStacks のマクロループのパターン
  3. マクロループの停止条件
  4. スリープと人間らしいタイミング
  5. ストレージで進捗を保持する
  6. Webhook が状態を報告できるよう終了コードで終える
  7. 完全な例
  8. BlueStacks や LDPlayer で MAS のスケジューラーにマクロを登録する
  9. グループで実行する
  10. うまくいかないとき
  11. ループが終わらない
  12. スケジュールが発火しなかった
  13. Time slot is occupied on this port
  14. マクロは終わったのに実行が Failed と表示される

無人で動くマクロには 3 つのものが必要です: いつ止まるかを知っているループ、どこまで進んだかを覚える方法、そしてあなたなしで起動するスケジュール。このガイドでは、Macro Automation Studio (MAS) の LDPlayer や BlueStacks のマクロループについて、この 3 つすべてを扱います。ループのパターン、停止条件、実行間のストレージ、Webhook が報告できる終了コード、そしてスケジューラーです。マクロを「実行をクリックする」から「毎朝勝手に動く」へ移したいすべての方向けです。

始める前に

  • MAS がインストール済みでサインインしていること。トライアル向けに 無料でダウンロード できます。
  • コードベースのプロジェクトが Code Editor からエミュレータで動くこと。はじめに を参照してください。
  • エミュレータが、カードで確認できるポートでデバイスとして追加されていること。デバイス を参照してください。
  • テンプレートと領域が取り込み済みであること。画像認識ガイド を参照してください。

LDPlayer と BlueStacks のマクロループのパターン

3 つの形でほぼすべてのマクロをカバーできます。

停止条件付きの while。 画面上の何か、またはカウンターが止まれと言うまでループが動きます。

python
import mas

images = mas.images({"collect": 401, "done": 402})

collected = 0
while collected < 50:
    if mas.find_object(images.done):
        break
    button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
    if button is None:
        continue
    mas.click(button.x, button.y, delay_ms=1000)
    collected += 1

固定回数の for。 何回やるかが分かっている場合です。

python
for round_no in range(1, 11):
    mas.log(f"Round {round_no} of 10")
    button = mas.find_object_retry(images.collect)
    if button is None:
        mas.log("Nothing to collect, stopping early", level="warning")
        break
    mas.click(button.x, button.y)

テンプレートが現れるまで。 読み込み画面や長いタイマーに使う、期限付きの待機です。

python
import time

def wait_for(image, timeout_s=120, every_s=3.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

上のすべてのループには出口があります。find_object は見失うと None を返し、例外は送出しないので、テンプレートがないだけでは while True は終わりません。すべてのループに上限を与えてください。

マクロループの停止条件

これらのうち少なくとも 2 つを組み合わせます。1 つはゲームに対する備え、もう 1 つは自分のバグに対する備えです。

  • カウンター。 while collected < 50。安価で予測できます。
  • 時間の予算。 time.monotonic() は秒を数え、時計が変わっても跳びません。外側のループに使い、スケジュール実行が次の実行が始まる前に必ず終わるようにします。
  • 画面の状態。 「エネルギー切れ」、「インベントリがいっぱい」、日次上限のメッセージなどのテンプレート。他のテンプレートと同じように Asset Lab で取り込み、各周回の先頭で確認します。
  • 連続した見失い。 何回連続で何も見つからなかったかを数え、5 回で諦めます。想定していなかった画面は、まさにこのように見えます。
python
started = time.monotonic()
misses = 0
while time.monotonic() - started < 20 * 60:
    if mas.find_object(images.out_of_energy):
        break
    button = mas.find_object_retry(images.collect)
    if button is None:
        misses += 1
        if misses >= 5:
            break
        continue
    misses = 0
    mas.click(button.x, button.y)

スリープと人間らしいタイミング

click はタップ後に delay_ms=1000 すでに待ち、swipe は既定で duration_ms=1000 かけます。周回の間には、ばらつく間を加えます。

python
import random

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

一定の間隔は機械的に見え、ゲームとの競合も起こします。タップの 900 ms 後に出るポップアップは、遅い日には固定の 1000 ms の遅延をすり抜けます。ランダムな間は両方をなだらかにします。アプリ自身の反応時間には clickdelay_ms を残し、人間のリズムには pause() を使ってください。

ストレージで進捗を保持する

途中で止まったスケジュール実行は、次回に続きから始めるべきです。ストレージ関数は、このコンピューター、デバイスのポート、選んだタスク名をキーとして、タスクごとに小さな JSON ドキュメントを保持します。

python
TASK = "daily_collect"

state = mas.retrieve(TASK)          # {} on the first run
collected = state.get("collected", 0)
last_day = state.get("day")

mas.save(TASK, {"collected": collected, "day": today})   # after each pass of the loop

if collected >= 50:
    mas.clear(TASK)                 # start fresh next time

save(task_name, data) は項目を書き込むか置き換え、retrieve(task_name) は辞書か空の辞書を返し、clear(task_name) は削除します。data は JSON にシリアライズできる必要があります。ポートがキーの一部なので、同じマクロを動かす 2 つのエミュレータインスタンスがカウンターを共有することはありません。合計が欲しいときは retrieve_all(task_name) がすべてのインスタンスの項目を返します。ストレージ を参照してください。

Webhook が状態を報告できるよう終了コードで終える

MAS はスクリプトの終了時にプロセスの終了コードを読みます。ゼロは実行の完了を意味し、それ以外は失敗としてマークします。Webhook の購読者はそれに応じて macro.completedmacro.failed を、ペイロード内の exit_code とともに受け取ります。

python
import sys

if energy is None:
    mas.log("Could not read energy", level="error")
    sys.exit(2)      # macro.failed, exit_code 2

mas.log("All done")
sys.exit(0)          # macro.completed

処理されない例外もゼロ以外で終了するので、クラッシュは追加のコードなしで失敗として報告されます。スクリプトだけが知っているイベントには、mas.webhook("level.reached", {"level": 40}) が、カスタムイベントを購読するエンドポイントに custom.level.reached イベントを送ります。エンドポイントは Webhook ページで設定します。Webhook を参照してください。

完全な例

このマクロは 1 日に最大 50 個の報酬を集め、数を覚え、「エネルギー切れ」か 20 分後に止まり、終了コードで報告します。ID を自分のものに置き換えてください。

python
import datetime
import random
import sys
import time

import mas

images = mas.images({
    "collect": 401,
    "close_popup": 402,
    "out_of_energy": 403,
})

TASK = "daily_collect"
DAILY_LIMIT = 50
TIME_BUDGET_S = 20 * 60


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


def main():
    today = datetime.date.today().isoformat()
    state = mas.retrieve(TASK)
    collected = state.get("collected", 0) if state.get("day") == today else 0
    mas.log(f"{today}: starting at {collected} of {DAILY_LIMIT}")

    started = time.monotonic()
    misses = 0
    while collected < DAILY_LIMIT:
        if time.monotonic() - started > TIME_BUDGET_S:
            mas.log("Time budget reached", level="warning")
            break
        popup = mas.find_object(images.close_popup)
        if popup:
            mas.click(popup.x, popup.y, delay_ms=800)
            continue
        if mas.find_object(images.out_of_energy):
            mas.log("Out of energy")
            break
        button = mas.find_object_retry(images.collect, total_tries=3, time_sleep=2.0)
        if button is None:
            misses += 1
            if misses >= 5:
                mas.log("Five misses in a row, giving up", level="error")
                mas.save(TASK, {"collected": collected, "day": today})
                sys.exit(2)
            continue
        misses = 0
        mas.click(button.x, button.y, delay_ms=1000)
        collected += 1
        mas.save(TASK, {"collected": collected, "day": today})
        pause()

    mas.log(f"Finished at {collected} of {DAILY_LIMIT}")
    sys.exit(0)


if __name__ == "__main__":
    main()

スケジュールする前に、Code Editor の 実行 (F5) で 1 度実行し、自分で終わることを確かめてください。

BlueStacks や LDPlayer で MAS のスケジューラーにマクロを登録する

スケジューラーはデスクトップアプリの中にあり、設定した時刻にエミュレータでマクロを起動します。

  1. スケジューラー を開き、新しいスケジュールを作成 をクリックします。
  2. 名前 (3 から 100 文字) を入力し、マクロ を選びます。
  3. エミュレータポートスキャン をクリックしてインスタンスのポートを選ぶか、入力します。
  4. 日付時刻 を設定します。時刻はコンピューターのローカル時刻です。
  5. 繰り返し を設定します: 1 回だけなら なし毎日曜日 にチェックを入れた 毎週、または 毎月
  6. 発火のたびにマクロを続けて複数回動かしたいのでなければ、繰り返し回数 は 1 のままにします。
  7. 有効 をオンのままにして、スケジュールを作成 をクリックします。

頼りにする前に知っておくこと:

  • アプリは開いたまま、サインインしたままにする必要があります。スケジューラーはシステムサービスではなく、cron 構文もありません。
  • ジョブは 30 秒ごとに確認されるので、実行は時刻から最大 30 秒遅れて始まることがあります。
  • スケジュールは 1 つのポートを対象にします。時刻になったときにポートが別の実行で使用中なら、スケジューラーはそのサイクルを飛ばし、次のサイクルでもう一度試します。
  • 同じポートで 2 つのスケジュールが同じ時間枠を共有することはできません。アプリは「Time slot is occupied on this port」で拒否します。
  • 同時に実行できるスケジュールジョブは最大 20 個です。
  • 繰り返し回数 が 1 より大きいとき、失敗した実行 (ゼロ以外の終了) は残りの繰り返しを止め、ジョブを Failed とマークします。
  • スケジュール実行はデバイスカードに保存された引数、またはデバイスが従う設定プロファイルを使います。設定プロファイル を参照してください。
  • ジョブの 履歴を表示 に、すべての実行が状態とログ付きで表示されます。

スケジューラー ページでは、ジョブの編集、スキップ、キャンセルを説明しています。

グループで実行する

同じマクロを複数のインスタンスで手動で実行するには、それらを 1 つのデバイスグループに入れ、各デバイスにマクロを割り当てて すべて開始 をクリックします。デバイスは 0.5 秒間隔で順番に開始し、それぞれが固有の実行、ログ、終了コードを持ちます。グループをスケジュールするには、同じマクロと時刻でポートごとに 1 つのスケジュールジョブを作ります。多重起動ガイド では、デバイスごとの引数、プロキシ、ストレージを扱います。

うまくいかないとき

ループが終わらない

上限のない while True か、決して現れないテンプレートに依存する停止条件です。time.monotonic() による時間の予算と連続見失いカウンターを加えてください。この 2 つがすべてのループを終わらせます。

スケジュールが発火しなかった

その時刻にアプリが閉じていたかスリープ中だった、ポートが使用中だった、またはジョブが 有効 ではありません。履歴を表示 でスキップや失敗の項目を確認し、スケジュールの時間帯はコンピューターを起こしたままにしてください。

Time slot is occupied on this port

同じポートの別のジョブがすでにその時刻を持っています。どちらかのジョブを数分ずらすか、繰り返し回数 で 2 つ目のマクロを 1 つ目に含めてください。

マクロは終わったのに実行が Failed と表示される

スクリプトが最後の操作の後にゼロ以外の終了コードか処理されない例外で終わりました。ログの最後の行を読んでください。クリーンアップ経路の sys.exit(1) や、保存された状態に対する KeyError がよくある原因です。

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

次のステップ

関連ページ

このページは役に立ちましたか?

質問がありますか? Discordで質問する