ポケモンSVのピクニックでタマゴを自動で受け取っていくコードを作成しました。
孵化は行わないため孵化をしたい場合は以下の記事のコードをご利用ください。
ポケモンSVの孵化自動化コードは以前にも作ったのですが、改良版ができたため公開します。 [sitecard subtitle=関連記事 url=https://pokemonit.com/sv-hatch-eggs-in-the-bo[…]
この記事をご覧になる前に
マイコンをまだ導入していない方は、以下の記事を参考に導入してみてください。このブログでは、『Arduino Leonardo』というマイコンを使っているため、異なる種類のものではうまく動かない可能性があります。
マイコンと呼ばれるものをご存知でしょうか? Switchに接続するだけで様々な作業を "自動" で行ってくれるというものです。 導入すれば作業が楽になるだけでなく、寝てる間に色々稼ぐこともできちゃう便利なアイテムなわけですね! […]
今回のコードは「NintendoSwitchControlLibrary」を使用しています。まだダウンロードされていない方は、解凍したフォルダを『Arduino』フォルダの中にある『libraries』フォルダの中にコピーするか、ライブラリマネージャよりインストールしてください。
A library for microcontrollers that uses Arduino to automate…
コード利用における注意点!
今回のコードは完全な動作を約束するものではありません。
ポケモンSV特有の処理落ちや入力漏れにはなるべく対応できるよう尽力していますが、それでも止まってしまう可能性は否定できません。
また、今回のコードに限らずなのですが、エラー落ちは想定できておりません。ポケモンSVは特にエラーが発生しやすいという話を聞いているので、そういった観点でもある程度見張れる状態での使用をおすすめいたします。
ソースコード
実際に書いたコードはこちらになります。
2022年12月29日追記:コードの安定感を少し向上させました。具体的には「タマゴパワー」が稀にLv1になってしまう問題の解消、及び、バスケット方向への振り向きに稀に失敗することがあったため2回に分けて入力するように変更しています。
/**
* タマゴをひたすら受け取るスケッチ
*
* ※このスケッチはタマゴの受け取りのみでタマゴ孵化は行いません。
* ボックスがタマゴで埋め尽くされても泣かないように!
*
* 初期条件は以下の通り
* 1.「プラトタウン」に空を飛ぶを行い、その着地点にいること
* 2.マップを開いた際に北が上にくるようになっていること
* 3.ピクニック、及び、サンドウィッチのチュートリアルを終えていること
* 4.「17 スーパーピーナッツバターサンド」のレシピを開放済であること
* 5.「17 スーパーピーナッツバターサンド」の素材(バナナスライス・ピーナッツバター・バター・シルバーピック(金額気にしないなら他のピックでもOK))をループ回数分用意してあること
* 6.手持ちは親となるポケモン2匹のみになるようにしてあること
* 7.ボックスに十分な空きを確保してあること(相性にもよりますが、1ループあたり50個前後のタマゴを受け取ります)
* 8.オフライン状態であること
* 9.無線のコントローラーが接続されていないこと
* 10.「設定」から「話の速さ」を「はやい」に、「わざ覚えスキップ」を「する」にしておくこと
*/
////////////////// libraries ////////////////////////////////
// ライブラリを読み込むためのコード
#include <NintendoSwitchControlLibrary.h>
////////////////// options ////////////////////////////////
// 何回ループさせるか?
const int LOOP_COUNT = 2;
// 「17 スーパーピーナッツバターサンド」にカーソルを合わせるのに、下キーと右キーの入力回数を指定する
const int SANDWICH_DOWN_COUNT = 8;
const int SANDWICH_RIGHT_COUNT = 0;
////////////////// constant parameters ////////////////////////////////
// 具材の個数
const int INGREDIENT_COUNT = 3;
// タマゴパワーの有効時間(分)
const int EGG_POWER_MINUTES = 30;
// ボタン押下時の標準的な待機時間
const int BASE_PUSH_BUTTON_TIME_MS = 1000;
// カーソル操作時の標準的な待機時間
const int BASE_CURSOR_OPERATION_TIME_MS = 200;
// スティックによる移動の待機時間
const int STICK_OPERATION_TILT_TIME_MS = 100;
const int STICK_OPERATION_DELAY_TIME_MS = 500;
// メニューの開閉にかかる待機時間
const int MENU_OPEN_OR_CLOSE_TIME_MS = 1500;
////////////////// variable parameters ////////////////////////////////
// ループをカウントするための変数
int count = 0;
////////////////// methods ////////////////////////////////
/**
* Switchをスリープさせる
*
* @return void
*/
void sleepGame() {
holdButton(Button::HOME, 1500);
pushButton(Button::A, BASE_PUSH_BUTTON_TIME_MS);
}
/**
* 「プラトタウン」から「コサジの小道灯台」に空を飛ぶで移動する(同じ場所に空を飛ぼうとするとAボタンが反応しないことがあるため、「プラトタウン」からのスタートとした)
*
* @return void
*/
void goFromLosPlatosToLighthouse() {
pushButton(Button::Y, 3000);
tiltLeftStick(204, Stick::MAX, 380);
delay(1000);
pushButton(Button::A, BASE_PUSH_BUTTON_TIME_MS);
pushButton(Button::A, 2000);
pushButton(Button::A, 10000);
}
/**
* 「コサジの小道灯台」から「プラトタウン」に空を飛ぶで移動する
*
* @return void
*/
void goFromLighthouseToLosPlatos() {
pushButton(Button::Y, 3000);
tiltLeftStick(52, Stick::MIN, 380);
delay(1000);
pushButton(Button::A, BASE_PUSH_BUTTON_TIME_MS);
pushButton(Button::A, 2000);
pushButton(Button::A, 10000);
}
/**
* ピクニックを開始する
*
* @param bool is_init
* @return void
*/
void startPicnic(bool is_init = false) {
pushButton(Button::L, 2000);
pushButton(Button::X, MENU_OPEN_OR_CLOSE_TIME_MS);
// 初回のみカーソルを合わせる
if (is_init) {
holdHat(Hat::RIGHT, BASE_CURSOR_OPERATION_TIME_MS);
holdHat(Hat::UP, 2000);
pushHat(Hat::DOWN, BASE_CURSOR_OPERATION_TIME_MS);
pushHat(Hat::DOWN, BASE_CURSOR_OPERATION_TIME_MS);
}
pushButton(Button::A, 10000);
}
/**
* ピクニックを終了する
*
* @return void
*/
void stopPicnic() {
pushButton(Button::Y, 1500);
pushButton(Button::A, 4000);
pushButton(Button::B, 500, 6);
}
/**
* スティックで1マスずつ移動する
*
* @param uint8_t x
* @param uint8_t y
* @return void
*/
void moveWithStick(uint8_t x, uint8_t y) {
tiltLeftStick(x, y, 200);
delay(500);
}
/**
* 初期位置(テーブル)に向かう
*
* @return void
*/
void moveToTablePosition() {
// コライドン(ミライドン)をボールに戻す
pushButton(Button::PLUS, 2000);
// しゃがんで移動することで位置を安定させる
pushButton(Button::B, BASE_PUSH_BUTTON_TIME_MS);
// 右3
moveWithStick(Stick::MAX, Stick::NEUTRAL);
moveWithStick(Stick::MAX, Stick::NEUTRAL);
moveWithStick(Stick::MAX, Stick::NEUTRAL);
// 上5
moveWithStick(Stick::NEUTRAL, Stick::MIN);
moveWithStick(Stick::NEUTRAL, Stick::MIN);
moveWithStick(Stick::NEUTRAL, Stick::MIN);
moveWithStick(Stick::NEUTRAL, Stick::MIN);
moveWithStick(Stick::NEUTRAL, Stick::MIN);
// 左1
moveWithStick(Stick::MIN, Stick::NEUTRAL);
// 左下2
moveWithStick(Stick::MIN, Stick::MAX);
moveWithStick(Stick::MIN, Stick::MAX);
}
/**
* サンドウィッチを作る
*
* @return void
*/
void makeSandwich() {
// テーブルの方向を向く
tiltLeftStick(Stick::NEUTRAL, Stick::MAX, STICK_OPERATION_TILT_TIME_MS);
delay(STICK_OPERATION_DELAY_TIME_MS);
// 料理をする
pushButton(Button::A, 1500);
pushButton(Button::A, 5000);
// 「17 スーパーピーナッツバターサンド」を選択
pushHat(Hat::DOWN, BASE_CURSOR_OPERATION_TIME_MS, SANDWICH_DOWN_COUNT);
if (SANDWICH_RIGHT_COUNT == 1) {
pushHat(Hat::RIGHT, BASE_CURSOR_OPERATION_TIME_MS);
}
pushButton(Button::A, BASE_PUSH_BUTTON_TIME_MS);
pushButton(Button::A, 9000);
// 具材を乗せる
for (int i = 0; i < INGREDIENT_COUNT; i++) {
tiltLeftStick(Stick::NEUTRAL, Stick::MIN, i == 0 ? 640 : 540);
SwitchControlLibrary().moveLeftStick(Stick::NEUTRAL, Stick::MAX);
SwitchControlLibrary().sendReport();
SwitchControlLibrary().pressButton(Button::A);
SwitchControlLibrary().sendReport();
delay(540);
SwitchControlLibrary().releaseButton(Button::A);
SwitchControlLibrary().sendReport();
SwitchControlLibrary().moveLeftStick(Stick::NEUTRAL, Stick::NEUTRAL);
SwitchControlLibrary().sendReport();
delay(500);
}
delay(500);
// 上のパンはいらない
tiltLeftStick(Stick::MAX, Stick::MIN, 2000);
// あとはA連打で食事を終える
pushButton(Button::A, 300, 40);
delay(20000);
pushButton(Button::A, 2500);
}
/**
* バスケットの確認(処理時間がちょうど1分になるよう調整)
*
* @return void
*/
void checkBasket() {
pushButton(Button::A, 1000, 2); // 2300ms
pushButton(Button::B, 500, 96); // 57700ms
}
////////////////// execute ////////////////////////////////
void setup() {
// Switchがマイコンを認識するまでは信号を受け付けないため、適当な処理をさせておく
pushButton(Button::LCLICK, 500, 5);
}
void loop() {
// 指定のループ回数を終えたあと、スリープする
if (count == LOOP_COUNT) {
sleepGame();
exit(0);
}
// 「コサジの小道灯台」に空を飛ぶ
goFromLosPlatosToLighthouse();
// ピクニック開始
startPicnic(count == 0);
moveToTablePosition();
// サンドウィッチ作成
makeSandwich();
// 最初の確認まではバスケットの方向を向いて待機(1分)
tiltLeftStick(Stick::MIN, Stick::NEUTRAL, 100);
delay(29600);
tiltLeftStick(Stick::MIN, Stick::NEUTRAL, 100);
delay(60000 - 2600 - 30000);
// タマゴパワーの有効時間が続く限り、1分毎に受け取りを繰り返す(iを1から始めているのは初回分は待機済のため)
for (int i = 1; i < EGG_POWER_MINUTES; i++) {
checkBasket();
}
// ピクニック終了
stopPicnic();
// 「プラトタウン」に戻る
goFromLighthouseToLosPlatos();
count++;
}
設定の確認
マイコンを繋げる前に、以下の状態になっていることを確認してください。
- 「プラトタウン」に空を飛ぶを行い、その着地点にいること
- マップを開いた際に北が上にくるようになっていること
- ピクニック、及び、サンドウィッチのチュートリアルを終えていること
- 「17 スーパーピーナッツバターサンド」のレシピを開放済であること
- 「17 スーパーピーナッツバターサンド」の素材(バナナスライス・ピーナッツバター・バター・シルバーピック(金額気にしないなら他のピックでもOK))をループ回数分用意してあること
- 手持ちは親となるポケモン2匹のみになるようにしてあること
- ボックスに十分な空きを確保してあること(相性にもよりますが、1ループあたり50個前後のタマゴを受け取ります)
- オフライン状態であること
- 無線のコントローラーが接続されていないこと
- 「設定」から「話の速さ」を「はやい」に、「わざ覚えスキップ」を「する」にしておくこと
重要なものをいくつか解説します!
開始位置・手持ちのポケモンについて
開始位置は以下になります。厳密にこの位置でなくてもポケセンの裏とかでも空を飛ぶには成功するはずなので、割と大雑把で大丈夫です!
また、手持ちは親となるポケモン2匹のみの状態にしておいてください。
オプションについて
コードの27行目、31〜32行目がオプションになります。
// 何回ループさせるか?
const int LOOP_COUNT = 2;
27行目の「2」の部分がループ回数になります。デフォルトの2ループだと、相性にもよりますがだいたい100個くらいタマゴできると思います!
ほしいタマゴの数に合わせて調整してもらえればと思います。
// 「17 スーパーピーナッツバターサンド」にカーソルを合わせるのに、下キーと右キーの入力回数を指定する
const int SANDWICH_DOWN_COUNT = 8;
const int SANDWICH_RIGHT_COUNT = 0;
31〜32行目は「17 スーパーピーナッツバターサンド」のカーソルを合わせるために必要なキーの入力回数を指定します。レシピの開放状態により入力回数が異なると思いますので、オプションとしております。
SANDWICH_DOWN_COUNT
には下キーの入力回数を入れます。
SANDWICH_RIGHT_COUNT
には右キーの入力回数を入れます。(ここは1か0で入力してください)
動作について
以下のように動作していれば正常です。
- 「プラトタウン」から「コサジの小道灯台」に空を飛ぶ
- ピクニックを開始
- 移動
- サンドウィッチ作成
- 移動
- 待機
- バスケット確認
- スクワットしながら待機
- 30分間、⑦と⑧を繰り返し
- ピクニック終了
- 「コサジの小道灯台」から「プラトタウン」に空を飛ぶ
コード作成のノウハウについて
今回のコード作成のノウハウを共有します。
タマゴの受け取り
SVではタマゴ受け取り時にBボタン連打でもタマゴ受け取りが可能です。そのため、バスケット確認の初回のAボタン以外はBボタンを連打することでテキスト飛ばしつつタマゴも受け取れるのが便利でした。
移動法について
しゃがんだ状態での移動はカメラの向きが勝手に変わったりしなかったり、また、マス目移動のような正確な移動が可能になるのが利点です!
この移動法を使うことで安定した移動ができるようになりました。
うまく使えば、ストーリーを自動化することも夢じゃないかもしれませんね!
タマゴの受け取りと孵化を同時に行うコードについて
作成は未定ですが構想はあります。
- 孵化はオリーブころがし場で行う
- オリーブころがし場に入るまでは、基本的にしゃがんだ状態で移動する
- セルクルタウンとオリーブころがし場の中間で30分ピクニックをする(野生ポケモンが出現しない場所があります)
- 1回のループで2BOXずつ孵化を行う
雨の時の処理落ちが怖かったり、柵を飛び越える際にライド状態になる必要がある点、タマゴが極端に少なかったり極端に多かったりした際の挙動、空を飛ぶで安定して戻れるかなど、いくつか懸念点はありますが、プログラムとしては上記で実現できるかなーと思っております。
ちなみに、野生ポケモンが出現しないピクニックできる場所というのは、ここから前に進んだところ(セルクルタウンとオリーブころがし場の間のスペース)になります。
最後に
今回のコードはなるべく安定するよう仕上げたつもりですが、もしループを抜けてしまう事象など見つけましたら@lefmarnaまで共有いただければと思います!