ガイド
LDPlayer マクロのループ、停止条件、スケジュール実行
LDPlayer や BlueStacks のマクロを停止条件付きでループさせ、実行間で進捗を保存し、終了コードを返し、MAS のスケジューラーで毎日実行する方法です。
- Windows
- Mac
- エミュレータ
- Studio
- Python SDK
このページの内容
無人で動くマクロには 3 つのものが必要です: いつ止まるかを知っているループ、どこまで進んだかを覚える方法、そしてあなたなしで起動するスケジュール。このガイドでは、Macro Automation Studio (MAS) の LDPlayer や BlueStacks のマクロループについて、この 3 つすべてを扱います。ループのパターン、停止条件、実行間のストレージ、Webhook が報告できる終了コード、そしてスケジューラーです。マクロを「実行をクリックする」から「毎朝勝手に動く」へ移したいすべての方向けです。
始める前に
- MAS がインストール済みでサインインしていること。トライアル向けに 無料でダウンロード できます。
- コードベースのプロジェクトが Code Editor からエミュレータで動くこと。はじめに を参照してください。
- エミュレータが、カードで確認できるポートでデバイスとして追加されていること。デバイス を参照してください。
- テンプレートと領域が取り込み済みであること。画像認識ガイド を参照してください。
LDPlayer と BlueStacks のマクロループのパターン
3 つの形でほぼすべてのマクロをカバーできます。
停止条件付きの while。 画面上の何か、またはカウンターが止まれと言うまでループが動きます。
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。 何回やるかが分かっている場合です。
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)テンプレートが現れるまで。 読み込み画面や長いタイマーに使う、期限付きの待機です。
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 回で諦めます。想定していなかった画面は、まさにこのように見えます。
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 かけます。周回の間には、ばらつく間を加えます。
import random
def pause(low=0.8, high=2.5):
time.sleep(random.uniform(low, high))一定の間隔は機械的に見え、ゲームとの競合も起こします。タップの 900 ms 後に出るポップアップは、遅い日には固定の 1000 ms の遅延をすり抜けます。ランダムな間は両方をなだらかにします。アプリ自身の反応時間には click の delay_ms を残し、人間のリズムには pause() を使ってください。
ストレージで進捗を保持する
途中で止まったスケジュール実行は、次回に続きから始めるべきです。ストレージ関数は、このコンピューター、デバイスのポート、選んだタスク名をキーとして、タスクごとに小さな JSON ドキュメントを保持します。
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 timesave(task_name, data) は項目を書き込むか置き換え、retrieve(task_name) は辞書か空の辞書を返し、clear(task_name) は削除します。data は JSON にシリアライズできる必要があります。ポートがキーの一部なので、同じマクロを動かす 2 つのエミュレータインスタンスがカウンターを共有することはありません。合計が欲しいときは retrieve_all(task_name) がすべてのインスタンスの項目を返します。ストレージ を参照してください。
Webhook が状態を報告できるよう終了コードで終える
MAS はスクリプトの終了時にプロセスの終了コードを読みます。ゼロは実行の完了を意味し、それ以外は失敗としてマークします。Webhook の購読者はそれに応じて macro.completed か macro.failed を、ペイロード内の exit_code とともに受け取ります。
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 を自分のものに置き換えてください。
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 のスケジューラーにマクロを登録する
スケジューラーはデスクトップアプリの中にあり、設定した時刻にエミュレータでマクロを起動します。
- スケジューラー を開き、新しいスケジュールを作成 をクリックします。
- 名前 (3 から 100 文字) を入力し、マクロ を選びます。
- エミュレータポート で スキャン をクリックしてインスタンスのポートを選ぶか、入力します。
- 日付 と 時刻 を設定します。時刻はコンピューターのローカル時刻です。
- 繰り返し を設定します: 1 回だけなら なし、毎日、曜日 にチェックを入れた 毎週、または 毎月。
- 発火のたびにマクロを続けて複数回動かしたいのでなければ、繰り返し回数 は 1 のままにします。
- 有効 をオンのままにして、スケジュールを作成 をクリックします。
頼りにする前に知っておくこと:
- アプリは開いたまま、サインインしたままにする必要があります。スケジューラーはシステムサービスではなく、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でお知らせください。
質問がありますか? Discordで質問する