ますたーです。こんにちは。
今回はSwitchを2台使った、「完全自動」レイドバトル自動周回の記事です。
本ブログのArduino自動化記事、記念すべき「第20回」に相応しい内容ですね。YouTubeにも動画をアップしました!
なお、Arduino Leonardo自動化の導入・機材構成については導入記事を参考にしてください。
導入記事:【Arduino自動化01】Arduino開発環境の導入
※本ブログに初めてお越しの方は「本ブログについて」もぜひ、ご覧ください。
概要
Nintendo Switch・Arduino Leonardoを2組を使い、片方でレイド募集、もう片方でレイド周回を行います。おそらく、完全自動で同一レイド周回ができる最適解です。
※1人(1台)で周回する場合は第19回の記事、ローカル配布だけを行いたい場合は第18回の記事をご覧ください
※本記事は、2組の機材が必要です。Arduinoの接続にはジャンパーケーブル(オス-オス)が3本必要です(後述)
もくじです。
ソースコードだけ欲しい、という方はどうぞ→読み飛ばす。
概要だけを知りたい方は⇒YouTube動画をご覧ください。
【注1】今回の自動化はArduino Leonardoを2組使います。また、今までの記事よりも機材構成・接続の難易度が高いです(といっても繋ぐだけですけど)。機材の接続手順が非常に大事ですので、今回はできる限り読み飛ばさず読んでいただいたほうが良いかもしれません。
【注2】本記事では、シリアル通信を使います。筆者自身、このあたり専門家では無いのでうまく行かなくてもご了承ください><
【注3】本記事では「Arduino Leonardo」は純正かつ「ソケットヘッダ付」のものを利用している想定で書いています。特に互換機など、ご自身がお持ちのボードに応じて、適宜読み替え・ソースコード修正が必要になる場合があります。なお、互換機の中には、シリアル通信ができないボードも一部あります。
本ブログの内容は、YouTubeにもアップロードしています。合わせてご視聴いただけると、より理解が深まると思います。
★2021/10/11 AM7:00 公開!
0.できたこと(まず見てほしい)
結論ファーストです。「Arduino Leonardo」2台を有線接続し、それぞれがNintendo Switchを操作することで、レイドバトルの自動周回を、「完全放置」でできるようになりました。結果、デリバードレイドを一晩中自動周回させ、マックスこうせき・けいけんアメがカンストしました!
本記事では、必要機材・やり方(配線方法)・ソースコードをすべて紹介します。
1.前提知識
ここは、開発経緯や、前提になる知識について余談的に書いています。
お急ぎの方は⇒2章「事前準備」まで読み飛ばす。
開発の背景:いままでのレイドバトル自動化の課題
本ブログでは、レイドバトルの周回作業の簡略化・省力化を目的とした記事をいくつか執筆してきました。例えば、前々回(第18回)の記事ではレイドバトルのローカル配信(=募集側)の自動化、前回(第19回)の記事ではソロ・レイド周回(=セルフ周回)の自動化を、それぞれ実装しました。
しかしこれらは、完全に独立したプログラムであり、「募集側と周回側を一緒に動かす」ことはできませんでした。言い換えれば、目的のレイドバトルを周回するためには、何らかの「手動作業」が必要だった*1のです。
個別には非常に便利な自動化でした(私もしばらく愛用していました)が、こと「放置」という観点からは、もう一歩踏み込みたい課題が残っていました。そこで、今回は「募集側」「参加側」を両方自動化しよう!と考えました。
Arduino2台を通信させて、レイドバトル「完全」自動周回を実現!
レイドバトルの「募集側」「参加側」の両方を自動化するにあたって、2台の操作が「ちゃんとタイミングよく動いてくれる」ことが前提となります。ところが、Arduinoはマクロコントローラーと同様、単体では「●●秒待って、●●を押して…」と、単純な操作の自動化しかできません。
要するに、きっちり時間を測って「募集側」「周回側」の操作を独立してプログラムを書いても、どうしてもだんだんと動作がズレてしまい、長時間の放置はできなくなってしまいます。
そこで、2台のArduinoの同期を取るために「シリアル通信*2」を利用しました。つまり、物理的に2台のArduino Leonardoを接続し、募集側の状況をもう1台のArduinoに報告するようにしたのです。
より具体的には、募集側Arduinoから「今からレイドバトルを始めるよ!」とか「今から戦闘だからA連打してね!」といった状況を、参加(周回)側のArduinoにシリアル通信で送信するようにプログラムを書きました。
これにより、募集側と周回側のArduinoそれぞれの操作タイミングがズレずに、完全自動でのレイドバトル周回が実現できました。
通信のためには、Arduino2台の配線作業が必要
さて、今回はArduino2台の操作タイミングをあわせるために「シリアル通信」を採用することにしました。
この方式では、前述のとおり、物理的に2台のArduinoを接続するので、接続用の銅線*3がいります。さらに、Arduino Leonardoにはシリアル通信できるPinとできないPinが混在します(要するに、挿すところを間違えたら動きません!)。
そういう意味では、「シリアル通信」を使うためには物理的な配線作業が必要になるため、今までよりもちょっとだけ難易度が上がります。とはいえ、冒頭にも書いたとおり、原則として「差し込むだけ」ですので、頑張りましょう。詳しくは、次章で紹介します。
2.事前準備(プログラム書き込みと機材の接続)
配線に必要なもの(ハードウェア)
今回の自動化において、準備するものは下記の通りです。ジャンパーワイヤー以外は【番外編3】筆者の使用機材に購入リンクを含め載せています。
- 【必須】Nintendo Switch(またはLite)2台
- 【必須】Arduino Leonardo 2台
- 【必須】SwitchとArduinoを繋ぐUSBケーブル(兼 PCからの書き込み用)2本
- 【新たに必要】ジャンパーワイヤー(オス-オス) 3本
- (あれば便利)Switchドック(またはハブスタンド)&純正充電アダプタ 2組
- (あれば便利)スイッチ付きUSBコネクタ 1個
要するに、【Arduino自動化01】導入記事に書いた基本構成が2組+ジャンパーワイヤー3本があれば最低限OKです。
お使いのArduino Leonardoが、【番外編3】筆者の使用機材で紹介している、純正のものであればソケットヘッダが付いているはずなので、ジャンパーワイヤーはオス-オスでOKです。
なお、ジャンパーワイヤーとは、つまるところただの銅線(=抵抗が無い)ですから、よほどのことがなければ、どこのメーカーから購入してもOKです。筆者はエントリーキットに入っていたものを使っています。
→ 持っていない人は「Arduino ジャンパーワイヤー オス オス」でAmazon検索。
ただし、pinの形状すなわち、オス-オス、オス-メス、メス-メスが間違っていないかは注意して購入しましょう*4。今回使うのはわずか3本ですし、大量に買っても余らせがちなので、無駄遣いにならないように選びましょう(たぶん、本ブログでジャンパーワイヤーを使うことも今後は無いでしょうし)。
プログラムを書き込んで配線(Switchを接続する前の作業)
手順1.2台のArduinoにそれぞれプログラムを書き込む
まずは、2台のArduino Leonardoそれぞれに後述の「ホスト側(配布側)」・「参加側(周回側)」のプログラムをIDEで書き込みます。
ここの手順は普段となんら変わりありませんが、このとき、ArduinoはPCとのみ接続し、Arduino同士の配線は後回しにしてください(重要)。Arduino同士を接続した状態でプログラムを書き込むと、場合によっては干渉を起こし、正しくプログラムが書き込まれない可能性があります。
また、1度にPCに接続するArduinoも1台にしておいた方が、何かと安定します。なお、どちらのArduinoにどちらのプログラムを書き込んだか、しっかりわかるようにしておきましょう。
手順2.Arduino2台を接続(配線)する
プログラムをそれぞれのArduinoに書き込んだら、パソコンからArduinoを取り外してください。その後、ArduinoをSwitchに接続する前(重要)に、Arduino同士の配線を済ませます。具体的には、レイド募集側の11番ピンともう片方の9番ピンを、レイド募集側の10番ピンともう片方の10番ピンを、そして、レイド募集側の5Vピンともう片方の5Vピンを、それぞれ接続します。
なお、接続する順番や、ジャンパーワイヤーの色は気にしなくてOKです。コツとしては、しっかり差し込むことでしょうか。あくまでもアナログ的に接続するので、うまく動かない場合は接続不良や、ケーブルの断線も疑うようにしましょう。
ちなみに、シリアル通信では、本来「GND」にもワイヤーの接続が必要らしいです。もし万が一、上記手順でうまく行かないor動作が安定しない場合は、更に1本追加して、募集側・参加側の両方のGND端子同士を繋ぐジャンパーワイヤーを接続してください。
手順3.必要に応じてハブスタンドほかに接続しておく
あとは、Nintendo Switch以外の配線類をすべて済ませておきます。充電アダプタも、つけるのであれば、このタイミングで配線に加えておいてください*5。
なお、配線が完了しても、Nintendo Switchはまだ差し込まないでください。
さて、以上の手順で、今回の鬼門は終わりました。お疲れさまでした。あとは、Switchを差し込むだけ、なのですが、レイド周回側のSwitchを先に差し込んで動作が止まるのを確認してから、レイド募集側を差し込む必要がある点には注意しておきましょう。詳しくは後述します。
3.Switch(ポケモン剣盾)の準備
レイド1ターン周回要員の育成
基本的には、【Arduino自動化18】に掲載の流れに沿って、募集側・周回側ともに1匹ずつ最適なポケモンを手持ち先頭に加えておきましょう。なお、自動化の特性上、使える「わざ」は1つだけにしておき、火力強化系のアイテムをもたせておくと安心です。
自動下記に引用(一部改変)しますが、デリバードレイドの周回を行う場合には、CS極振りレジエレキと、AS極振りルガルガン(まよなかのすがた)がオススメです。
初撃オススメ:Cぶっぱレジエレキ「エレキボール」
レジエレキはS種族値200というトンデモ性能で、デオキシススピードフォルムすら抜き去る圧倒的な素早さの持ち主。そのユニークな特性「トランジスタ」により、でんきタイプのわざであれば実数値1.5倍の火力を出せるため、初撃に使うにはもってこいです。
続いて、追撃要員の紹介です。こちらは、こうげき努力値に極振りし、こだわりハチマキを持たせたルガルガンのストーンエッジがオススメです。夢特性であれば「ノーガード」になるため命中不安なエッジを確定で当てることができます。また、いわタイプなので、デリバード(こおり・ひこう)の4倍弱点を突くことができることもポイントです。無論、天候による威力増減の影響も受けません。
これらのポケモンを準備したら、配信側・参加側の2プレイヤーがそれぞれ1匹ずつを持つように手持ちの先頭にあらかじめ加えておきましょう。
レイド募集側の準備:デリバード★4のレイドバトルの巣穴の目の前でセーブ
ホスト側(募集側)では、クライアント側(周回側)で周回させたいレイドバトルを出現させた状態にしておきます。詳細説明は【Arduino自動化18】に譲りますが、デリバードレイドは最高効率のレイド報酬と言われていますので、今回は、デリバードレイド(★4)バトルを想定しています。
ホスト側の事前の準備は下記2つです。
- 「ねがいのかたまり」を入れて厳選済の巣穴の目の前でセーブ
- 手持ち先頭を1ターン周回要員にしておく
デリバードレイドでは、カンムリ神殿の巣穴が好アクセスポイントなので、うまく活用すると良いでしょう。
レイド周回側の準備:目の前に話しかけるものが無いところでセーブ
続いて、クライアント側(レイド周回側)の準備です。準備、と言っても、守るべきことは3つだけ。
- 十分な量のモンスターボールを所持し、リュック先頭に並び替えておくこと
- 目の前に話しかけるものが一切無い場所でセーブすること
- 手持ち先頭を1ターン周回要員にしておく
特に、2はループそのものが途切れる要員となりますので、周りから野生のポケモンなどが突進してきたり、ワイルドエリアで手持ちポケモンに話しかける方向を向いていたりなどは避けてください。
ここまで、準備お疲れ様でした。
あとは、Switchを差し込んで周回していくだけです。ただし、2台のSwitchは同時ではなく、時間を空けて、順番に差し込む必要があります。具体的には、先に「レイド参加側」のSwitchを接続し、Homeボタンが2回押されたような挙動を確認した上で、もう一台(募集側)のSwitchを接続する、という流れです。
このあたりは、スイッチ付USBアダプタを機材に入れているかどうかで煩雑さが変わるので、順番に説明します(無くても大丈夫ですのでご安心を)。
USBスイッチ付アダプタがある場合
接続の手順は以下の通りです。
- 周回側・募集側のNintendo SwitchをそれぞれのArduinoに接続する
- 周回側のArduinoのLEDが消灯するまで待つ
- USBスイッチを「ON」にする
USBスイッチ付アダプタが無い場合
接続の手順は以下の通りです。
- 周回側のNintendo Switchを周回側のArduinoに接続する
- 周回側のArduinoのLEDが消灯するまで待つ
- 募集側のNintendo Switchを募集側のArduinoに接続する
今回は、レイド自動周回「募集側(ホスト側)」「周回側(クライアント側)」の2つのソースコードを公開します。配線・接続手順については前述の通りです。
一応、ソースコード冒頭にもコメント部分に記載していますが、よくわからない人は、適宜本記事を読み直していただければと思います。
5-1.レイドバトル募集側(ホスト側)
レイド募集側のプログラムです。
#include <SwitchControlLibrary.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11);
#define SOFTWARE_SERIAL (1)
#define HOLDTIME (95)
#define WAIT_TIME (30)
#define BATTLE_TIME (60)
enum {RECEIVE_BEGIN_PACKET=3, BEGIN_LOOP, START_YYCOMM, START_BATTLE, RESET_GAME } state;
#define BAND (300)
int LED = 13;
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
void DoubleHome(void);
void setup() {
unsigned long int current_time=0;
unsigned long int start_time=0;
for(int i=0;i<7;i++)PushRL(300);
delay(1000);
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH);
DoubleHome();
delay(5000);
#ifdef USE_SERIAL_1
Serial1.begin(BAND);
start_time=millis();
current_time=start_time;
while (!Serial1) {
if(current_time - start_time >= 2000UL){
Serial1.begin(BAND);
start_time=millis();
}
}
DoubleHome();
#endif
#ifdef USE_NORMAL_SERIAL
Serial.begin(BAND);
start_time=millis();
current_time=start_time;
while (!Serial) {
if(current_time - start_time >= 2000UL){
Serial.begin(BAND);
start_time=millis();
}
}
DoubleHome();
#endif
#ifdef SOFTWARE_SERIAL
mySerial.begin(BAND);
DoubleHome();
mySerial.write(RECEIVE_BEGIN_PACKET);
mySerial.flush();
#endif
delay(3000);
}
void loop() {
unsigned long int current_time=0;
unsigned long int start_time=0;
unsigned long int temp_time=0;
#ifdef USE_NORMAL_SERIAL
Serial.print(BEGIN_LOOP);
Serial.flush();
#endif
#ifdef USE_SERIAL_1
Serial1.print(BEGIN_LOOP);
Serial1.flush();
#endif
#ifdef SOFTWARE_SERIAL
mySerial.print(BEGIN_LOOP);
#endif
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
PushKey("A", HOLDTIME, 1500);
PushKey("A", HOLDTIME, 2500);
PushKey("UP", HOLDTIME, 300);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
#ifdef USE_NORMAL_SERIAL
Serial.print(START_YYCOMM);
#endif
#ifdef USE_SERIAL_1
Serial1.print(START_YYCOMM);
#endif
#ifdef SOFTWARE_SERIAL
mySerial.print(START_YYCOMM);
#endif
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
delay(1000UL*WAIT_TIME);
PushKey("A", HOLDTIME, 300);
#ifdef USE_NORMAL_SERIAL
Serial.print(START_BATTLE);
#endif
#ifdef USE_SERIAL_1
Serial1.print(START_BATTLE);
#endif
#ifdef SOFTWARE_SERIAL
mySerial.print(START_BATTLE);
#endif
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)BATTLE_TIME*1000UL ; current_time=millis() ){
if( current_time - start_time < (unsigned long)(BATTLE_TIME-3)*1000UL )PushKey("A", HOLDTIME, 300);
}
#ifdef USE_NORMAL_SERIAL
Serial.print(RESET_GAME);
#endif
#ifdef USE_SERIAL_1
Serial1.print(RESET_GAME);
#endif
#ifdef SOFTWARE_SERIAL
mySerial.print(RESET_GAME);
#endif
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
digitalWrite(LED, HIGH);
delay(100);
PushKey("Home", HOLDTIME, 1000);
PushKey("X", HOLDTIME, 500);
PushKey("A", HOLDTIME, 3000);
PushKey("A", HOLDTIME, 1000);
PushKey("A", HOLDTIME, 18500);
PushKey("A", HOLDTIME, 10000);
}
void DoubleHome(void){
SwitchControlLibrary().PressButtonHome();
delay(95);
SwitchControlLibrary().ReleaseButtonHome();
delay(300);
SwitchControlLibrary().PressButtonHome();
delay(95);
SwitchControlLibrary().ReleaseButtonHome();
delay(300);
delay(200);
return;
}
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);
}
5-2.レイドバトル周回側(クライアント側)
クライアント側のプログラムです。
#include <SwitchControlLibrary.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(9, 10);
enum {RECEIVE_BEGIN_PACKET='3', BEGIN_LOOP, START_YYCOMM, START_BATTLE, RESET_GAME } state;
#define BAND (300)
int LED = 13;
#define HOLDTIME (95)
#define BATTLE_TIME (100)
void PushRL(int delay_time_ms);
int PushKey(char* keyname, int holdtime, int delaytime);
inline void DoubleHome(void);
inline void participate(void);
inline int A_renda_inBattle(int battle_took_time);
void setup() {
unsigned long int current_time=0;
unsigned long int start_time=0;
for(int i=0;i<7;i++)PushRL(300);
delay(1000);
pinMode(LED, OUTPUT);
digitalWrite(LED, HIGH);
DoubleHome();
delay(5000);
mySerial.begin(BAND);
mySerial.listen();
start_time=millis();
current_time=start_time;
DoubleHome();
}
int volatile now = '-';
void loop() {
if ( mySerial.available() ) {
digitalWrite(LED, HIGH);
now = mySerial.read();
}
digitalWrite(LED, LOW);
switch( now ){
case RECEIVE_BEGIN_PACKET:
case 3:
break;
case BEGIN_LOOP:
case 4:
break;
case START_YYCOMM:
case 5:
now = -1;
participate();
break;
case START_BATTLE:
case 6:
now = -1;
A_renda_inBattle(BATTLE_TIME);
break;
case RESET_GAME:
case 7:
break;
default:
break;
}
}
void DoubleHome(void){
SwitchControlLibrary().PressButtonHome();
delay(95);
SwitchControlLibrary().ReleaseButtonHome();
delay(300);
SwitchControlLibrary().PressButtonHome();
delay(95);
SwitchControlLibrary().ReleaseButtonHome();
delay(300);
delay(200);
return;
}
void participate(void){
delay(4000);
PushKey("Y", HOLDTIME, 1300);
mySerial.peek();
for(int i = 0 ; i < 7 ; i++) PushKey("X", HOLDTIME, 200);
mySerial.peek();
PushKey("right", HOLDTIME, 300);
PushKey("A", HOLDTIME, 700);
PushKey("A", HOLDTIME, 1700);
PushKey("A", HOLDTIME, 2100);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
PushKey("A", HOLDTIME, 500);
mySerial.peek();
return;
}
int A_renda_inBattle(int RENDAJIKAN){
unsigned long int current_time=0;
unsigned long int start_time=0;
unsigned long int temp_time=0;
for( start_time=millis(), current_time=start_time ; current_time - start_time < (unsigned long)RENDAJIKAN*1000UL ; current_time=millis() ){
if( current_time - start_time < (unsigned long)(BATTLE_TIME-3)*1000UL ){
PushKey("A", HOLDTIME, 300);
mySerial.peek();
}
}
return;
}
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);
}
6.あとがき
まずは、本ブログ開設から11ヶ月目にしてようやく、Arduino自動化の記事「第20回」の更新を迎えることができました。まずは、本ブログを読んでいただいている皆様に、厚く御礼申し上げます。また、引き続き本ブログをよろしくお願いいたします。
さて、今回の記事では、前々回(第18回:レイド配布)・前回(第19回:ソロレイド周回)の延長線として、諸々苦戦しながらも、ついにレイドバトルの「完全自動周回」を実現することができました。おまたせしました!!!
本プログラムを使えば、デリバードレイドの周回作業はもちろん、例えば乱数調整で出した色違いの巣穴で使えば、複数回、周回側で捕獲チャレンジを自動で行うことができます。さらに、ロトミIDくじなどで予め十分な量のマスターボールを調達しておけば、色違い量産も可能になります。本当にパワフルなプログラムとして仕上がったと思いますので、是非ともご参考いただければと思います。
*
本自動化については、以前より「根強い需要」が垣間見えていて、私自身も「いつか欲しいなぁ」と常日頃から感じていました。しかしながら、なかなか着手できないのには理由がありました。
実は、私自身が平日・土日も仕事に追われていることもあり、なかなか着手するモチベーションが上がらなかった上、実装のハードルがそれなりに高かったのです。今回は「シリアル通信」を使うことで2台のArduinoをタイミング同期させて動作させることができましたが、電子回路の知識が一切ない筆者にとっては「すべて手探り状態での検証・実装」でした。
そのような経緯でなかなか着手できずにいたのですが、普段より本ブログをご覧いただいている皆様からの温かいコメントや、複数人からのリクエストコメント、YouTubeチャンネルでの高評価などが増えてきており、「本当にありがたいなぁ」「リクエストにもなんとか応えてみたいなぁ」と感じていました。
そんな折、本ブログ記事も気づけば第19回まで更新されていて、次は「第20回に相応しい記事にしたいなぁ」とおぼろげながらに考え、せっかくならチャレンジしてみよう!と、1週間ほど前にようやく奮起しました。
予想以上の難しさの中、仕様検討とプログラミング、そしてトライ・アンド・エラーを経て、ついに、本記事の公開まで気合で漕ぎ着けることができました!
そんなこんなで、「おまたせしました!」「できたてほやほやのプログラムです!」と、皆様に向けて胸を張って紹介できますし、皆様に支えていただけたので最後まで仕上げることができたな、と感じています。皆様にも是非ともご参考いただければ幸いです。
もともと、ブログタイトルの通り「忘備録」として作った本ブログの記事が色々な人のお役に立てていて、私自身にとっても支えになっていて、非常に嬉しく思います。
これからも、マイペースな更新になると思いますが、「中身のある忘備録」として続けていけたらと考えますので、引き続きどうぞ、本ブログをよろしくお願いいたします。
そして、「参考になったよ」という方は、是非ともコメントをいただければ幸いです。
重ねて第20回の記事公開を迎えられたことを記念し、これを支えていただいた皆様への感謝の辞にて結ばせていただきます。
2021年10月10日 ますたー c⌒っ.ω.)っ
前回記事:【Arduino自動化19】ねがいのかたまりで自動レイドバトル周回【完全放置で「けいけんアメ」「ヨロイこうせき」】
前々回記事:【Arduino自動化18】ローカルレイド自動配布【デリバードレイドでマックスこうせき】
導入記事:【Arduino自動化01】Arduino開発環境の導入
関連記事:【Arduino自動化 番外編3】筆者の使用機材一覧【紹介記事】
他のArduino自動化:ポケモン剣盾Arduino自動化 カテゴリーの記事一覧
YouTubeチャンネル:ますたーの忘備録 - YouTube 【NEW!!】
---