ますたーです。こんにちは。
今回は、「トーナメント」を自動周回するArduino Leonardo自動化の記事です。前回の記事に引き続き、マクロ対応コントローラーで自動化していたコマンドをArduino Leonardoで実装します。今回はJoy-Conのスティック入力操作にもチャレンジします。ゆえに、これを読むとArduino Leonardoでマクロ対応コントローラーの「完全代替」ができるかもしれません。
なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
本記事では、シュートシティの「トーナメント」自動周回を実装します。
今回の目次です。
作ったきっかけ
実は、私もマクロ対応コントローラーは持っています。剣盾発売当初から今年の2月まではずっと愛用していたのですが、ずっと下記4点をネックに感じていました。
- 方向キーを輪ゴムなどでアナログに固定しなければならない(安定しない)
- 無線コントローラーだと充電しないと使えない(使い続けられない)
- やることを変えるたびにマクロ登録をし直さないといけない(面倒くさい)
- Homeボタンがマクロに登録できない(設定画面に入れない)
Arduino Leonardoは、これらを全てを解決できるからという判断で購入し、今に至るわけです。結果、良かったと思っています(2台買いました)。
この記事のスタンス
前回の記事では、「決められたコマンド入力」が分かっているものをArduino Leonardoで動く自動化プログラムを作りましたが、今回はその改良版で、Joy-Conのスティック入力にも対応させます。
したがって、もし読者の皆様にて「入力したいキーの順番があらかじめ決まっている」ものがあれば、スティック入力を含めてプログラムを自分で書ける雛形としてご利用いただけると思います(他のサイトで紹介されているマクロコントローラーの自動化コマンドなど)。ソースコードだけ欲しいという方はどうぞ⇒ 読み飛ばす
トーナメント自動周回のやり方
様々なブログでも紹介されている、かなり有名でオールドスタイルなやり方ですが、努力値AS振りザシアンLv.100@くちたけん(わざ:アイアンヘッドのみ;PP最大値まで増やしておく)1匹のみを手持ちに準備し、コマンドを入力し続けるだけです。ザシアンがいない人は、Lv.100サザンドラ@こだわりメガネ(わざ:あくのはどうのみ)でも多少効率が落ちますがOKです。
肝心の入力コマンドですが、基本的に「A連打、ちょっとだけB」というすごくシンプルな入力です。ただし、スティック上方向を起点(0度)として、時計回りにおおよそ15度~22度の向きに傾ける必要があり、この角度が手動だとかなりシビアです。ズレるとキチンと再戦ができず、ループが途切れてしまいます。
輪ゴムやテープなどでアナログに固定しなければならないマクロ対応コントローラーでは多少ツライですが、この点、Arduino Leonardoであればスティック入力のズレが起こることなく確実に再戦できます。
上述の通り、今回のコマンドはかなりシンプルで、スティックを倒しながらA連打+Bを1回というものです。プログラムに起こすと、下記の様になります。
こちらは説明用に抜粋したプログラムの一部です。⇒ 完成版プログラムまで読み飛ばす
#define HOLDTIME (95)
void loop() {
TiltLeftStick(17, 1.0, 0, 0);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("B", HOLDTIME, 150);
}
今回は「スティックを右上に傾ける」「Aボタンを9回押す」「Bボタンを1回押す」コマンドなので、書き換えはわずか11行で完成します。簡単ですね。
構成としては、前回の記事と同様、「loop(){」とそれに対応する「}」の間に自動化するコマンドを1行ずつ書き込むだけの簡単設計です。新たに「TiltLeftStick()」という関数を実装したので、スティック入力の自動化にも対応しています。
TiltLeftStick(17, 1.0, 0, 0);
関数の仕様の話をしてもしょうがないですが、要するにJoy-Conの左スティック入力は「どの向き」に「どのくらい倒すか」を指定することで実現できると理解いただければOKです。また、「倒し続ける時間」「スティックを戻した後の待機時間」はそれぞれ「ミリ秒」で指定できます。
なお、倒し続ける時間・待機時間(3つ目・4つ目)をそれぞれ「0」「0」と指定すると倒し続ける(スティック位置を戻さない)こともできます。
上記例だと17°の方向に傾きMAXでスティックを倒し続けるようになっています。
プログラムの使い方(準備)
前述の通り、手持ちをLv100ザシアン@くちたけん 1匹のみにして、シュートシティの右上の方にあるポケモンリーグ本部へ行き、受付の前でArduino Leonardoを差し込むだけです。
今回のプログラムは前回の記事に機能を追加したものです。読者の皆様が流用して1から使いやすいように、さらにキレイに作っています。
今回はJoy-Con左スティック入力にも対応したので、このソースコードを全文コピペして、loop()の中身だけ好みのものに変更すれば、オリジナルのマクロコントローラーとしても使うことができると思います。興味があればチャレンジしてみてください。
#include <SwitchControlLibrary.h>
#define HOLDTIME (95)
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime);
void setup() {
for(int i=0;i<7;i++)PushRL(300);
delay(1000);
}
void loop() {
TiltLeftStick(17, 1.0, 0, 0);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("A", HOLDTIME, 150);
PushKey("B", HOLDTIME, 150);
}
void PushRL(int delay_time_ms){
SwitchControlLibrary().PressButtonR();
SwitchControlLibrary().PressButtonL();
delay(HOLDTIME);
SwitchControlLibrary().ReleaseButtonR();
SwitchControlLibrary().ReleaseButtonL();
delay(delay_time_ms);
return;
}
int PushKey(char* keyname, int holdtime, int delaytime){
if(strlen(keyname)==1){
switch(keyname[0]){
case 'A': case 'a':
SwitchControlLibrary().PressButtonA(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonA(); delay(delaytime);
break;
case 'B': case 'b':
SwitchControlLibrary().PressButtonB(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonB(); delay(delaytime);
break;
case 'X': case 'x':
SwitchControlLibrary().PressButtonX(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonX(); delay(delaytime);
break;
case 'Y': case 'y':
SwitchControlLibrary().PressButtonY(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonY(); delay(delaytime);
break;
case 'L': case 'l':
SwitchControlLibrary().PressButtonL(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonL(); delay(delaytime);
break;
case 'R': case 'r':
SwitchControlLibrary().PressButtonR(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonR(); delay(delaytime);
break;
case 'H': case 'h':
SwitchControlLibrary().PressButtonHome(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
break;
case '+': case 'p': case 'P':
SwitchControlLibrary().PressButtonPlus(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonPlus(); delay(delaytime);
break;
case '-': case 'm': case 'M':
SwitchControlLibrary().PressButtonMinus(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonMinus(); delay(delaytime);
break;
default:
break;
}
}else if(strlen(keyname)>=2){
switch(keyname[0]){
case 'z': case 'Z':
if(keyname[1]=='R'||keyname[1]=='r'){
SwitchControlLibrary().PressButtonZR(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonZR(); delay(delaytime);
}
if(keyname[1]=='L'||keyname[1]=='l'){
SwitchControlLibrary().PressButtonZL(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonZL(); delay(delaytime);
}
break;
case 'r': case 'R':
SwitchControlLibrary().MoveHat(2); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'l': case 'L':
SwitchControlLibrary().MoveHat(6); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'u': case 'U':
SwitchControlLibrary().MoveHat(0); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'd': case 'D':
SwitchControlLibrary().MoveHat(4); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().MoveHat(8); delay(delaytime);
break;
case 'H': case 'h':
SwitchControlLibrary().PressButtonHome(); delay(holdtime);
if(holdtime>0)SwitchControlLibrary().ReleaseButtonHome(); delay(delaytime);
default:
break;
}
}else{
return -1;
}
return strlen(keyname);
}
void TiltLeftStick(int direction_deg, double power, int holdtime, int delaytime){
double rad = (double)direction_deg*PI/180.0;
int x, y;
x = (double)128*sin(rad)*power;
y = (double)-128*cos(rad)*power;
x += 128; y += 128;
if(x >= 255) x=255; if(x <= 0) x=0;
if(y >= 255) y=255; if(y <= 0) y=0;
SwitchControlLibrary().MoveLeftStick(x,y);
if(holdtime> 0){
delay(holdtime);
SwitchControlLibrary().MoveLeftStick(128,128);
}
if(delaytime>0) delay(delaytime);
return;
}
あとがき
今回もマクロ対応コントローラーの代替としてプログラムを実装・紹介しました。新たにスティック入力に対応できるようになったことで、Arduino Leonardoがまさにマクロ対応コントローラーの「完全代替」に一歩近づいたかな、と感じています。
思えば、私が今年2020年の2月にArduinoを買ったきっかけになったのが、まさにトーナメントの自動周回でコントローラーのスティック入力がうまく固定できなかったから、だったのですよね。せっかくデジタルに入力制御ができるマクロ対応コントローラーなのに、肝心の入力がアナログだという矛盾をはらんでいて、個人的に矛盾だらけだなぁと思ったものです。
ゲーム中でも、スティック入力操作を使う動作は沢山ありますし、剣盾なら「カレーづくり」や「マホミルの進化(リザードンポーズ)」、なによりも「町中の移動」や「タマゴ孵化」などでしょうか。今回の雛形を使えば、これらの自動化もおいおい実装できるようになるかもしれませんね。
なにはともあれ、皆様のポケモン自動化ライフが豊かになることを願っています。
ではではc⌒っ.ω.)っ
前回記事:
【Arduino自動化06】完全放置「マックスこうせき」集め【ダイマックスアドベンチャー】
次の記事:
【Arduino自動化08】バトルタワー自動周回【BP稼ぎ】
導入記事:
【Arduino自動化01】Arduino開発環境の導入