DXジーニアスフルボトルをよりジーニアスな玩具に改造する
大変ご無沙汰しておりました。およそ二ヶ月半ぶりの更新になります。ちょっと本業が忙しくなってしまっていて(今も)、なかなか時間がとれずじまいでした。ちょくちょく見にきてくださっていた方々、申し訳ありません、ありがとうございます。
さて、今回のお題は仮面ライダービルド『DXジーニアスフルボトル』です。
この玩具、個人的な感想ですが、非常に完成度が高いです。ビルド系の玩具は『作り込みがあと一歩足りないなあ』と感じることが多かった気がしますが(特にハザードトリガー)、これについては文句なしです。Amazon のレビューも好意的なものが多いようです。
特にすごいなあと思うのが、この玩具の一番の売りと思われる『視覚表現』です。ジーニアスフルボトルが60本のフルボトルの力を持つということで、ジーニアスフルボトルの中に多数のボトルが存在しているに見え、かつ、それらがドライバーと連動して振られているように見えるという。最初CMでこれを見たときは「どうなってんだこれは??」と素直にびっくりしました。
購入後早速分解してわかったのですが、これはマジックミラーの性質をものすごくうまく使ったギミックだったのですね。マジックミラーは『エグゼイド』の『マイティブラザーズガシャット』でも使われていたものですが(←詳しくはこちら)、『透過』と『反射』の両方の性質を持ちます。つまり、こういうことです。
合わせ鏡によってボトルのオブジェが無限増殖し、それをマジックミラーの透過方向から見ることで、多数のボトルが存在するように見えているのですね。さらにマジックミラーの対向側のミラーがドライバーに連動して傾くことで、中のボトルが動いているように見えるという。これはもう、よくもまあこんなギミック思いつくものだなあと感心しきりです。作った人こそが天っ才だと思います。
さて、こんな感じで大変完成度の高いDXジーニアスフルボトル。ハザードトリガーに比べると改造事例はほとんどありません。自分が見た限りでは、電源スイッチの追加改造をされている方が何名かいるのみです。おそらく、ほとんど手を加える必要がないほど完成度・満足度の高いアイテム、ということなのだと思います。素晴らしいです。
ただ、自分としては「こんなことができたらもっと面白い玩具になるのでは?」という思いがぼんやりとあったので、せっかくなので手を加えてみることにしました。下手に手を加えて完成度を下げないように気をつけながら、「全てのボトルの力を持つ」という設定を最大限に活かして、「より玩具としておもしろいもの」を目指して作ってみます。
で、こちらが今回改造してみたジーニアスフルボトルです。前作のハザードトリガーと同様、見た目にはほとんど変化はありません。
裏面にスライド式の電源スイッチがついています。これがOFFになっているときは通常のDXジーニアスフルボトルとして動作するようになっていて、これをONにすると今回新規に追加したモードが使用可能になるようになっています。この辺りの詳しい仕組みについては後述します。
さて、今回新規に追加した機能ですが、大別すると3つで、細かく分けると5つあります。
- 『Be The One』再生機能
- 『60本フルボトル』モード
- 全音声読み上げ
- ベストマッチ選択
- 『レジェンドライダー』モード
- 全音声読み上げ
- 必殺技発動
以下、一つずつ説明していきます。
1. 『Be The One』再生機能
これは当初は入れるつもりはなかったのですが、桐生戦兎(葛城巧)が桐生戦兎としての記憶を取り戻してジーニアスフォームへ初変身したシーンがとてもカッコ良かったので、それをやりたいがためだけにこの機能を入れ込むことにしました。最近のCSMでも定番の機能ですし。やっぱり主題歌が流れると燃えますもんね。
入れ方としては、ジーニアスフルボトルでは単に回るだけの飾りになっていたボトルのフタにこの機能を割り当てることにしました。これで、ボトルのフタを回すことに意義が生まれます。
2. 60本フルボトルモード
A. 全音声読み上げ
自分が初めてジーニアスフルボトルの存在を知ったときに、まず真っ先に思いついたのがこの機能です。これができたら、なんだか劇場版の変身シーンみたいな感じになってカッコ良いんじゃないかと。実戦(?)を考えると、こんな長い読み上げなんかやっている間にエボルトさんにぶっ飛ばされてしまいそうですが、まあ玩具としてはなんかカッコ良くて良いかなと。
B. ベストマッチ選択
「全部のフルボトルの力を持っているのなら、どのベストマッチにだってなれるのでは?」と思い、それができたら玩具として面白いだろうということで、入れ込むことにしました。まあ、おそらく全てのベストマッチの上位互換であろうジーニアスフォームが存在する以上、実戦的(?)にはほとんど意味のない機能ではあるのですが。
「ベストマッチの選択方法をどうするか?」を考えたとき、一番最初に考えたのは、本体に小さいDIPスイッチをつけておいて、それで二進数でベストマッチの番号を指定する、というものでした。でもいちいちベストマッチの番号を覚えるのも、それを二進数で表現し直すのも、どう考えても面倒でスマートじゃないのでさてどうしよう、とボンヤリ考えていたときに、「あ、せっかく全ボトル名読み上げてんだからそれ使えばいーじゃん!」ということに気がつきました。
ということで、A.のボトル名読み上げ中にボタンを押すと、押したときに音声再生していたボトル名に対応したベストマッチを選択するようにしました。これももちろん実戦(?)を考えると使いにくいことこの上ない(←タイミングよくボタンを押さないとなりたいベストマッチが行き過ぎてしまう)機能なのですが、玩具としては「狙ったところでボタンを押す」という遊びが加わって良いかなと。
3. レジェンドライダーモード
A. 全音声読み上げ
「 すべてのフルボトルの力を持つのであれば、レジェンドライダーフルボトルの力を持つという解釈もありでは?」というところから想像して、60本フルボトルモードのときと同じような読み上げ機能を入れてみることにしました。結果、なんだかディケイドライバーみたいなアイテムになった気がします。
60本フルボトルモードもこのレジェンドライダーモードも、ジーニアスフルボトルのメインのボタンを押すことで発動するのですが、ボタンを一瞬だけ押すと60本フルボトルモード、ちょっとだけ長めに押すとレジェンドライダーモードが発動するようにして使い分けられるようにしています。
B. 必殺技発動
最初は元々のジーニアスフルボトルの「ワンサイド!」「逆サイド!」「オールサイド!」に倣って、「ワンサイド!」に代わってクウガ〜ディケイドまでの平成一期のライダー名読み上げ、「逆サイド!」に代わってダブル〜エグゼイドまでの平成二期のライダー名読み上げの機能にしようと思っていました。
ところがある日の仕事帰りに、
- レバーの回転ごとに各ライダーの色で順に発光して、最後にジーニアスフィニッシュに辿り着くとカッコ良いのでは?
- 最後まで辿り着かなくても、いやむしろ途中でレバーの回転を敢えて止めることで、任意のレジェンドライダーの必殺技を発動できると楽しいのでは?
というアイデアを思いついてしまったので、「うわコレ音源用意するの超面倒くさい」と思いながらも、やった方が玩具として絶対面白いものになると思ったので、入れ込むことにしました。レバーを何度もぐるぐる回すことの動機づけにもなりますし。
という感じで、一番最初の思いつき(全音声読み上げ)からすると、最終的に構想が随分膨らんでしまいましたが、なんとか全部無事に入れ込むことができました。次に、実際の中身についてご説明します。
まずはいつものごとく、使用した部品のご紹介です。
毎度おなじみ Arduino Pro miniです。今回は3.3V 8MHz版を使用しています。
毎度おなじみフルカラーLEDです。線一本で何個でもLEDを制御できるし、追加の抵抗不要でArduinoに直結するだけで使えるので、ほんとにもうこればっかり使っています。
毎度おなじみのMP3プレイヤーです。唯一応答速度にだけは不満があるのですが、ArduinoでMP3を扱うときには長らくこれ以上の最適解には出会っていません。今回、『Be The One』といろんな効果音を同時に鳴らすために、2個のDF Playerを使うという暴挙にでました。さながらダブルオーガンダムのツインドライヴのよう。
最初は一個でなんとかならないか(一個のDFPlayerで二つの音源を同時に再生できなきいか)と考えて、DFPlayerに搭載されているadvertise機能なども試してみたのですが、結局どちらか一方の再生中はどちらか一方の再生が停止される、という仕様でしたので、やむなく二個同時使用に踏み切ることにしました。
基本的にはリチウムイオンポリマー電池は取り扱いが煩雑ですし、決して安全なものではないので使いたくない(←それゆえハザードトリガーもトランスチームガン・カスタムも未使用)のですが、今回はスペース的に普通の電池を使うのがどう考えても無理でしたので、頼らせて頂くことにしました。やっぱりこのサイズでこの出力というのは利便性が高いので。
以上が主な部品ですが、今回初めて、リレーを使ってみました。秋月で売っている、3V駆動の小型リレーY14H-1C-3DSです。あと、秋月で手に入るディテクタスイッチESE22MH4XDKも今回多用しています。
で、これらを全部ジーニアスフルボトルに詰め込むと、
こんな感じになりました。なかなかの密度感です。 以前作ったスマブラガシャットに負けず劣らずです。
これだと全然良くわからないので、回路図をベースにしながら説明していきます。
順に見ていきましょう。
まずリレー部分。ここは、追加モジュールの電源スイッチがOFFになっているときには、元々ジーニアスフルボトルに組み込まれているボタン電池x3と玩具基板が繋がる状態になっています。つまり、このときは、通常のDX版と同様に動作することになります。
追加モジュールに電源が入ると、Arduinoがこのリレーを制御できるようになるため、玩具基板を任意にON/OFFできるようになります。今回追加した機能を使うときは、音声や発光が干渉しないよう、玩具基板は基本的にOFFにしておくのですが、ジーニアスフルボトルをドライバーに装填して変身するときの発光処理をプログラムで一から書き直すのは大変なので、そのときだけは玩具基板をONにするようにしています。
ちなみに、リレーを使うときはダイオードを併用するのが基本というか、機器を壊してしまわないために必須なんですね。初めて使ったものだから、初めはそのあたりの常識を全然知らないままに進めていってしまっていました。危なかった。。。
リチウムイオンポリマー電池については、
ここ、ボトルの底の隙間に入っています。ここしかスペース的な余裕がありませんでした。。。今回はビルドドライバーと切り離されているときでも単体でArduinoを動かさなくてはいけないので、ハザードトリガーのようにビルドドライバーから電力を供給するという手段は使えませんでした。
各種スイッチは全て新規で追加しています。音声再生/装填判定スイッチとドライバー連動スイッチについては、最初は元々玩具基板で使われているスイッチをそのまま使えば良いのでないかと考えていたのですが、ハザードトリガーのときとは違って、今回は玩具基板をOFFにしていることが多いので、玩具基板がOFFだとこのスイッチで電位の変化が発生しないため、玩具のスイッチと同じ場所にスイッチを追加しました。
こんな感じで、ニッパーでスペースをつくったあと、グルーガンでガチガチに固めています。
Be The One再生用スイッチは今回完全新規なので、フタの回転を検出して再生/停止をするためにここに取り付けています。ただ、固定の仕方が甘かったのか、フタを回転させてもうまく反応しないことがよくあり。。。ここは要改善ポイントです。
先でも触れた通り、今回はDFPlayerを二つ同時に使用しています。
実際こんな感じで、まさにツインドライヴという感じです。
DFPlayerが二つになってもスピーカーを一つにまとめることは多分できたと思うのですが、一回試してみたらあまり綺麗な音にならなかった気がしたので、Be The One用には一個別にスピーカーを追加することにしました。
ホントにもうスペースがないので、先ほどのボトル増殖ギミックの立役者(?)であるミラーの後ろに貼り付けました。あまり半田付け部品を可動部分に取り付けるのは宜しくないのですが、やむなしです。
最後にフルカラーLEDです。
これはこんな感じでボトルオブジェクトのサイド部分から光が入るように穴を開けました。玩具基板のLEDの上下真逆の方から光を入れる方法もあったのかもしれませんが、ボトルの真ん中らへんで発光していた方がそれっぽいかな、と思って横から光らせることにしました。ただ、実際横から光らせてみると、お互い向かい会う形で発光することになり、光が干渉してしまってイメージと違うような色に見えることがあったため、ひょっとしたら上下逆方向から光らせた方が良かったのかもしれません。
ハードウェアの改造ポイントは以上です。最後に、作成したプログラムの全文を載せておきます。
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Adafruit_NeoPixel.h>
SoftwareSerial ss_music_player(2, 3); // RX, TX
SoftwareSerial ss_effect_player(4, 5); // RX, TX
#define LED_PIN 6
#define RELAY_PIN 7
#define BTN_PIN 8
#define DRIVER_PIN 9
#define PLAYER_PIN 10
#define NUM_OF_LED 2
#define LED_OFF 0
#define MODE_INIT 0
#define MODE_SIXTY 1
#define MODE_BESTMATCH 2
#define MODE_RIDER 3
#define DRIVER_STAGE_WAIT_CHANGE 0
#define DRIVER_STAGE_WAIT_ONE_SIDE 1
#define DRIVER_STAGE_WAIT_REVERSE_SIDE 2
#define DRIVER_STAGE_WAIT_ALL_SIDE 3
#define DRIVER_STAGE_END 4
#define EFFECT_OFFSET_BESTMATCH_1 100 // ドライバー装填時サウンド用
#define EFFECT_OFFSET_BESTMATCH_2 130 // 変身完了時サウンド用
#define EFFECT_OFFSET_RIDER_FINISH 160 // レジェンドライダー必殺技サウンド用
#define BTN_TIME_LOWER 300 // 0.3秒(ボタン中押し判定の下限)
#define BTN_TIME_UPPER 800 // 0.8秒(ボタン中押し判定の上限)
#define BTN_COUNT_LONG 80 // 10ms x 100 でおよそ 0.8秒
#define CRITICAL_INTERVAL 10000
#define COLOR_INTERVAL 500
#define LEVEL_INTERVAL 50
//--------------------- フルボトル定義 ---------------------//
#define NUM_OF_FULLBOTTLES 61
const uint8_t fullbottles[NUM_OF_FULLBOTTLES][3] PROGMEM = {
{ 0, 0, 0}, // 0. エンプティ
{255, 0, 0}, // 1. ラビット
{ 0, 0,255}, // 2. タンク
{102, 51, 0}, // 3. ゴリラ
{ 0,153,204}, // 4. タイヤモンド
{255,102, 0}, // 5. タカ
{153,153,153}, // 6. ガトリング
{102, 0,102}, // 7. 忍者
{255,255, 0}, // 8. コミック
{255,255,255}, // 9. パンダ
{ 51,153,255}, // 10. ロケット
{128,128,128}, // 11. ハリネズミ
{255, 0, 0}, // 12. 消防車
{255,204, 0}, // 13. ライオン
{ 51,153,153}, // 14. 掃除機
{ 0, 0,255}, // 15. ドラゴン
{204,153, 0}, // 16. ロック
{ 0, 51,204}, // 17. 海賊
{ 0,153, 0}, // 18. 電車
{204, 0,153}, // 19. オクトパス
{255,255, 51}, // 20. ライト
{255, 0, 0}, // 21. フェニックス
{153,153,153}, // 22. ロボット
{128,128,128}, // 23. ウルフ
{ 0, 51,204}, // 24. スマホ
{ 0,153,255}, // 25. ユニコーン
{255,255,255}, // 26. 消しゴム
{255, 0, 51}, // 27. ローズ
{ 51,153,255}, // 28. ヘリコプター
{ 0,255, 0}, // 29. タートル
{153,153,153}, // 30. ウォッチ
{153, 51, 0}, // 31. カブト虫
{ 0,153,255}, // 32. カメラ
{255,255, 0}, // 33. クマ
{255,255,255}, // 34. テレビ
{153,102, 51}, // 35. ドッグ
{128,128,128}, // 36. マイク
{255, 0, 0}, // 37. サンタクロース
{255,204,204}, // 38. ケーキ
{102, 0,153}, // 39. スパイダー
{255,255,255}, // 40. 冷蔵庫
{255,255, 0}, // 41. トラ
{255, 51,102}, // 42. UFO
{ 0, 51,255}, // 43. クジラ
{ 0,102,255}, // 44. ジェット
{ 0, 51,153}, // 45. シカ
{255,153, 0}, // 46. ピラミッド
{255,204, 0}, // 47. キリン
{ 0, 51,255}, // 48. 扇風機
{128,128,128}, // 49. ペンギン
{ 0,204, 0}, // 50. スケボー
{ 0,153,102}, // 51. オバケ
{ 0, 0,255}, // 52. マグネット
{ 0, 51,153}, // 53. サメ
{255, 51, 0}, // 54. バイク
{102, 0,102}, // 55. バット
{255, 0, 0}, // 56. エンジン
{255,255, 0}, // 57. ハチ
{ 0, 51,204}, // 58. 潜水艦
{255,255,255}, // 59. サイ
{255, 0, 0} // 60. ドライヤー
};
#define NUM_OF_RIDERBOTTLES 19
const uint8_t riderbottles[NUM_OF_RIDERBOTTLES][3] PROGMEM = {
{ 0, 0, 0}, // 0. エンプティ
{255, 0, 0}, // 1. クウガ
{255,255, 0}, // 2. アギト
{255, 0, 0}, // 3. 龍騎
{255, 0, 0}, // 4. ファイズ
{ 0, 0,255}, // 5. ブレイド
{102, 0,153}, // 6. 響鬼
{255, 0, 0}, // 7. カブト
{255, 0, 0}, // 8. 電王
{255, 0, 0}, // 9 キバ
{255, 0,255}, // 10. ディケイド
{ 0,255, 0}, // 11. W
{255, 0, 0}, // 12. オーズ
{255,255,255}, // 13. フォーゼ
{255, 0, 0}, // 14. ウィザード
{255,102, 0}, // 15. 鎧武
{255, 0, 0}, // 16. ドライブ
{255,102, 0}, // 17. ゴースト
{255, 0,204} // 18. エグゼイド
};
DFRobotDFPlayerMini music_player;
DFRobotDFPlayerMini effect_player;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_OF_LED, LED_PIN, NEO_GRB);
uint8_t last_btn_state = HIGH;
uint8_t last_driver_state = HIGH;
uint8_t last_player_state = HIGH;
uint8_t btn_state = HIGH;
uint8_t driver_state = HIGH;
uint8_t player_state = HIGH;
uint8_t mode = MODE_INIT;
unsigned long btn_pressed_time = 0;
unsigned long btn_released_time = 0;
unsigned long btn_time_diff = 0;
unsigned long color_change_time = 0;
unsigned long before_color_change_time = 0;
unsigned long level_change_time = 0;
unsigned long before_level_change_time = 0;
unsigned long critical_start_time = 0;
uint8_t btn_counter = 0;
boolean isPlayingMusic = false;
boolean isDriverIn = false;
boolean isCriticalReady = false;
uint8_t bottle_counter = 1;
uint8_t selected_bestmatch = 0;
uint8_t rider_counter = 0;
uint8_t light_level = 10;
uint8_t driver_stage = DRIVER_STAGE_WAIT_CHANGE;
void playMusic(int index){
if(!ss_music_player.isListening()){
ss_music_player.listen();
}
music_player.playMp3Folder(index);
}
void pauseMusic(){
if(!ss_music_player.isListening()){
ss_music_player.listen();
}
music_player.pause();
}
void playEffect(int index){
if(!ss_effect_player.isListening()){
ss_effect_player.listen();
}
effect_player.playMp3Folder(index);
}
void pauseEffect(){
if(!ss_effect_player.isListening()){
ss_effect_player.listen();
}
effect_player.pause();
}
void fullbottle_led_ctrl(uint8_t index_1, uint8_t lv_1, uint8_t index_2, uint8_t lv_2){
uint8_t r_1 = 0;
uint8_t g_1 = 0;
uint8_t b_1 = 0;
uint8_t r_2 = 0;
uint8_t g_2 = 0;
uint8_t b_2 = 0;
if(index_1 != LED_OFF){
r_1 = pgm_read_byte(&(fullbottles[index_1][0]));
g_1 = pgm_read_byte(&(fullbottles[index_1][1]));
b_1 = pgm_read_byte(&(fullbottles[index_1][2]));
}
if(index_2 != LED_OFF){
r_2 = pgm_read_byte(&(fullbottles[index_2][0]));
g_2 = pgm_read_byte(&(fullbottles[index_2][1]));
b_2 = pgm_read_byte(&(fullbottles[index_2][2]));
}
pixels.setPixelColor(0, pixels.Color(uint8_t(r_1*0.1*lv_1), uint8_t(g_1*0.1*lv_1), uint8_t(b_1*0.1*lv_1)));
pixels.setPixelColor(1, pixels.Color(uint8_t(r_2*0.1*lv_2), uint8_t(g_2*0.1*lv_2), uint8_t(b_2*0.1*lv_2)));
pixels.show();
}
void riderbottle_led_ctrl(uint8_t index_1, uint8_t lv_1, uint8_t index_2, uint8_t lv_2){
uint8_t r_1 = 0;
uint8_t g_1 = 0;
uint8_t b_1 = 0;
uint8_t r_2 = 0;
uint8_t g_2 = 0;
uint8_t b_2 = 0;
if(index_1 != LED_OFF){
r_1 = pgm_read_byte(&(riderbottles[index_1][0]));
g_1 = pgm_read_byte(&(riderbottles[index_1][1]));
b_1 = pgm_read_byte(&(riderbottles[index_1][2]));
}
if(index_2 != LED_OFF){
r_2 = pgm_read_byte(&(riderbottles[index_2][0]));
g_2 = pgm_read_byte(&(riderbottles[index_2][1]));
b_2 = pgm_read_byte(&(riderbottles[index_2][2]));
}
pixels.setPixelColor(0, pixels.Color(uint8_t(r_1*0.1*lv_1), uint8_t(g_1*0.1*lv_1), uint8_t(b_1*0.1*lv_1)));
pixels.setPixelColor(1, pixels.Color(uint8_t(r_2*0.1*lv_2), uint8_t(g_2*0.1*lv_2), uint8_t(b_2*0.1*lv_2)));
pixels.show();
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT_PULLUP);
pinMode(DRIVER_PIN, INPUT_PULLUP);
pinMode(PLAYER_PIN, INPUT_PULLUP);
// ---------- MP3プレイヤーセットアップ ----------
ss_music_player.begin(9600);
ss_effect_player.begin(9600);
ss_music_player.listen();
if (!music_player.begin(ss_music_player)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin music_player:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while(true);
}
Serial.println(F("music_player online."));
music_player.setTimeOut(500); //Set serial communictaion time out 500ms
music_player.volume(20); //Set volume value (0~30).
ss_effect_player.listen();
if (!effect_player.begin(ss_effect_player)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin effect_player:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while(true);
}
Serial.println(F("effect_player online."));
effect_player.setTimeOut(500); //Set serial communictaion time out 500ms
effect_player.volume(15); //Set volume value (0~30).
// ---------- フルカラーLEDセットアップ ----------
pixels.begin();
for(int8_t i=0;i<NUM_OF_LED;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
pixels.show();
// ---------- リレーセットアップ ----------
// リレーをLOWにすると玩具基板がON、リレーをHIGHにすると玩具基板がOFF。
// 基本的に玩具基板はOFFにしておく。
digitalWrite(RELAY_PIN, HIGH);
// 起動処理
fullbottle_led_ctrl(1, 10, 2, 10); // ラビットタンク
playEffect(66); // 「最高だ!」
delay(2000);
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(1, i, 2, i); // ラビットタンク
delay(100);
}
}
void loop() {
// ---------- フタの一回転を検知して、『Be The One』の再生/停止 ----------
player_state = digitalRead(PLAYER_PIN);
if(last_player_state == HIGH && player_state == LOW){
if(isPlayingMusic){ // 再生中なら、停止
pauseMusic();
}else{ // 停止中なら、始めから再生
playMusic(1);
}
isPlayingMusic = !isPlayingMusic;
}
last_player_state = player_state;
// ---------- 押ボタン処理 ----------
btn_state = digitalRead(BTN_PIN);
if(last_btn_state == HIGH && btn_state == LOW){
// ボタンが押された
btn_pressed_time = btn_released_time = millis(); // リリース計測用変数をここでリセット
// 60本モードで、ボトル名読み上げ中の場合は、ベストマッチモードへ移行
if(mode == MODE_SIXTY && bottle_counter < NUM_OF_FULLBOTTLES){
mode = MODE_BESTMATCH;
Serial.println(F("Mode Bestmatch."));
selected_bestmatch = bottle_counter/2 + bottle_counter % 2;
fullbottle_led_ctrl(selected_bestmatch*2-1, 10, selected_bestmatch*2, 10);
playEffect(63); // 「勝利の法則は決まった!」
delay(2000);
// ベストマッチモード中は、玩具基板は無効にする
digitalWrite(RELAY_PIN, HIGH);
// LED消灯
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(selected_bestmatch*2-1, i, selected_bestmatch*2, i);
delay(100);
}
}
}else if(last_btn_state == LOW && btn_state == HIGH){
// ボタンが離された
btn_released_time = millis();
}
last_btn_state = btn_state;
btn_time_diff = btn_released_time - btn_pressed_time;
if(0 < btn_time_diff && btn_time_diff <= BTN_TIME_LOWER){
// ボタン短押しで60本モード移行
if(mode != MODE_BESTMATCH){
mode = MODE_SIXTY;
Serial.println(F("Mode Sixty."));
playEffect(62); // 「さあ、実験を始めようか」
delay(3000);
bottle_counter = 1;
before_color_change_time = millis();
// 連続音声読み上げ開始
playEffect(80);
delay(50); // 音声と発光の同期の微修正
// 60本モード中は、読み上げが終わるまでは玩具基板は無効にする
digitalWrite(RELAY_PIN, HIGH);
}
btn_pressed_time = 0;
btn_released_time = 0;
}else if(BTN_TIME_LOWER < btn_time_diff && btn_time_diff < BTN_TIME_UPPER){
// ボタン中押しでレジェンドライダーモード移行
if(mode != MODE_BESTMATCH){
mode = MODE_RIDER;
Serial.println(F("Mode Rider."));
playEffect(64); // 「変身!」
delay(2000); // 玩具音声待ち
bottle_counter = 1;
before_color_change_time = millis();
// 連続音声読み上げ開始
playEffect(81);
// ライダーモード中は、玩具基板は無効にする
digitalWrite(RELAY_PIN, HIGH);
}
btn_pressed_time = 0;
btn_released_time = 0;
}
// ボタン長押しによるドライバー装填認識
if(btn_state == LOW){
if(btn_counter < BTN_COUNT_LONG){
btn_counter++;
}else{
if(!isDriverIn){
if(mode == MODE_SIXTY){
// ジーニアスフルボトル装填音再生
playEffect(68);
}else if(mode == MODE_BESTMATCH){
// ベストマッチ音声読み上げ&発光(「A, B, ベストマッチ!」)
playEffect(selected_bestmatch + EFFECT_OFFSET_BESTMATCH_1);
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(selected_bestmatch*2-1, i, LED_OFF, 0);
delay(75);
}
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(LED_OFF, 0, selected_bestmatch*2, i);
delay(75);
}
fullbottle_led_ctrl(selected_bestmatch*2-1, 10, selected_bestmatch*2, 10);
}else if(mode == MODE_RIDER){
// ジーニアスフルボトル装填音再生
playEffect(68);
}
}
isDriverIn = true;
}
}else{
btn_counter = 0;
rider_counter = 0;
if(isDriverIn){
// 再生中の効果音は停止
pauseEffect();
// ドライバーから外したときの発光処理
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(9, i, 9, i); // 白で発光させるためにパンダを指定
delay(100);
}
// モードを初期状態に戻す
mode = MODE_INIT;
// 玩具基板を無効にしておく
digitalWrite(RELAY_PIN, HIGH);
}
isDriverIn = false;
isCriticalReady = false;
driver_stage = DRIVER_STAGE_WAIT_CHANGE;
}
// ---------- ドライバー連動 ----------
driver_state = digitalRead(DRIVER_PIN);
if(last_driver_state == HIGH && driver_state == LOW && btn_state == LOW){
if(mode == MODE_SIXTY){
if(driver_stage == DRIVER_STAGE_WAIT_CHANGE){
playEffect(75); // 「イェーイ!x6」
delay(10000); // 変身音待ち
playEffect(70);
delay(5000); // 変身名乗り待ち
driver_stage = DRIVER_STAGE_WAIT_ONE_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_ONE_SIDE){
critical_start_time = millis(); // 必殺技カウント開始
isCriticalReady = true;
playEffect(76);
delay(1500);
driver_stage = DRIVER_STAGE_WAIT_REVERSE_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_REVERSE_SIDE){
playEffect(77);
delay(1500);
driver_stage = DRIVER_STAGE_WAIT_ALL_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_ALL_SIDE){
playEffect(78);
delay(2000);
driver_stage = DRIVER_STAGE_END;
}
}else if(mode == MODE_BESTMATCH){
if(driver_stage == DRIVER_STAGE_WAIT_CHANGE){
delay(10000); // 変身音待ち
playEffect(65); // 「ビルドアップ」
delay(1500);
playEffect(selected_bestmatch + EFFECT_OFFSET_BESTMATCH_2);
delay(5000); // 変身名乗り待ち
driver_stage = DRIVER_STAGE_WAIT_ONE_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_ONE_SIDE){
critical_start_time = millis(); // 必殺技カウント開始
isCriticalReady = true;
fullbottle_led_ctrl(selected_bestmatch*2-1, 10, LED_OFF, 0);
playEffect(selected_bestmatch*2-1);
delay(1500);
driver_stage = DRIVER_STAGE_WAIT_REVERSE_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_REVERSE_SIDE){
fullbottle_led_ctrl(LED_OFF, 0, selected_bestmatch*2, 10);
playEffect(selected_bestmatch*2);
delay(1500);
driver_stage = DRIVER_STAGE_WAIT_ALL_SIDE;
}else if(driver_stage == DRIVER_STAGE_WAIT_ALL_SIDE){
fullbottle_led_ctrl(selected_bestmatch*2-1, 10, selected_bestmatch*2, 10);
playEffect(69);
delay(2000);
driver_stage = DRIVER_STAGE_END;
}
}else if(mode == MODE_RIDER){
if(driver_stage == DRIVER_STAGE_WAIT_CHANGE){
playEffect(74); // 「イェーイ!x18」
delay(10000); // 変身音待ち
playEffect(70);
delay(5000); // 変身名乗り待ち
driver_stage = DRIVER_STAGE_END;
}else{
if(rider_counter == 0){
critical_start_time = millis(); // 必殺技カウント開始
isCriticalReady = true;
}
rider_counter++;
if(rider_counter < NUM_OF_RIDERBOTTLES){
for(int i=5;i>=0;i--){
riderbottle_led_ctrl(rider_counter, i*2, rider_counter, i*2);
delay(40);
}
}else if(rider_counter == NUM_OF_RIDERBOTTLES){
fullbottle_led_ctrl(1, 10, 2, 10); // 到達したら、ビルドカラー
playEffect(69); // 「イェーイ!」
}
}
}
}
last_driver_state = driver_state;
// 必殺技発動判定
if(isCriticalReady && millis() - critical_start_time >= CRITICAL_INTERVAL){
if(mode == MODE_SIXTY){
if(driver_stage == DRIVER_STAGE_WAIT_REVERSE_SIDE){
playEffect(71); // ジーニアスアタック
}else if(driver_stage == DRIVER_STAGE_WAIT_ALL_SIDE){
playEffect(72); // ジーニアスブレイク
}else if(driver_stage == DRIVER_STAGE_END){
playEffect(73); // ジーニアスフィニッシュ
}
driver_stage = DRIVER_STAGE_WAIT_ONE_SIDE;
}else if(mode == MODE_BESTMATCH){
if(driver_stage == DRIVER_STAGE_WAIT_REVERSE_SIDE){
playEffect(82); // ボルテックアタック
}else if(driver_stage == DRIVER_STAGE_WAIT_ALL_SIDE){
playEffect(82); // ボルテックアタック
}else if(driver_stage == DRIVER_STAGE_END){
playEffect(83); // ボルテックフィニッシュ
}
driver_stage = DRIVER_STAGE_WAIT_ONE_SIDE;
}else if(mode == MODE_RIDER){
if(rider_counter >= NUM_OF_RIDERBOTTLES){
playEffect(73); // ジーニアスフィニッシュ
delay(10000);
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(1, i, 2, i); // ラビットタンク
delay(100);
}
}else{
riderbottle_led_ctrl(rider_counter, 10, rider_counter, 10);
playEffect(rider_counter + EFFECT_OFFSET_RIDER_FINISH);
delay(10000);
for(int i=10;i>=0;i--){
riderbottle_led_ctrl(rider_counter, i, rider_counter, i);
delay(100);
}
}
rider_counter = 0;
}
isCriticalReady = false;
}
// ---------- 逐次処理 ----------
if(mode == MODE_SIXTY){
if(bottle_counter < NUM_OF_FULLBOTTLES){
if(bottle_counter % 2 == 1){
fullbottle_led_ctrl(bottle_counter, 10, bottle_counter-1, light_level);
}else{
fullbottle_led_ctrl(bottle_counter-1, light_level, bottle_counter, 10);
}
}
color_change_time = level_change_time = millis();
if(color_change_time - before_color_change_time > COLOR_INTERVAL && bottle_counter < NUM_OF_FULLBOTTLES){
bottle_counter++;
light_level = 10;
before_color_change_time = color_change_time;
}
if(level_change_time - before_level_change_time > LEVEL_INTERVAL && light_level > 0){
light_level--;
before_level_change_time = level_change_time;
}
if(bottle_counter == NUM_OF_FULLBOTTLES){ // 最後まで到達したら、ラビットタンクで締め
delay(COLOR_INTERVAL);
fullbottle_led_ctrl(1, 10, 2, 10); // ラビットタンク
// 玩具基板を有効にしておく
digitalWrite(RELAY_PIN, LOW);
delay(9000);
for(int i=10;i>=0;i--){
fullbottle_led_ctrl(1, i, 2, i); // ラビットタンク
delay(100);
}
bottle_counter++; // 再実行させないため
}
}else if(mode == MODE_BESTMATCH){
; // 特に何もしない
}else if(mode == MODE_RIDER){
if(bottle_counter < NUM_OF_RIDERBOTTLES){
if(bottle_counter % 2 == 1){
riderbottle_led_ctrl(bottle_counter, 10, bottle_counter-1, light_level);
}else{
riderbottle_led_ctrl(bottle_counter-1, light_level, bottle_counter, 10);
}
}
color_change_time = level_change_time = millis();
if(color_change_time - before_color_change_time > COLOR_INTERVAL && bottle_counter < NUM_OF_RIDERBOTTLES){
bottle_counter++;
light_level = 10;
before_color_change_time = color_change_time;
}
if(level_change_time - before_level_change_time > LEVEL_INTERVAL && light_level > 0){
light_level--;
before_level_change_time = level_change_time;
}
if(bottle_counter == NUM_OF_RIDERBOTTLES){ // 最後まで到達したら、ラビットタンクで締め
delay(1000);
fullbottle_led_ctrl(1, 10, 2, 10); // ラビットタンク
delay(10000);
bottle_counter++; // 再実行させないため
}
}
delay(10);
}
気をつけなければいけないのは、今回はDFPlayerを二つ併用することになるため、ソフトウェアシリアル のTX/RXの組みを二セット用意する必要があるということです。Arduinoで二組以上のソフトウェアシリアル を使うときには、通信する前に都度listen()でソフトウェアシリアル通信の待受を切り替える必要があることに注意が必要です。
以上、今回の改造内容でした。結果につきましては、本記事冒頭の動画をご確認くださいませ。最後はほぼ力技でエイヤっとパーツを無理やり全部押し込んでしまった感がありますが、なんとか期待通りには動作してくれたかな、と思います。
というわけで、ジーニアスフルボトルをより面白い玩具に改造するために色々やってみました。リチウムイオンポリマー充電池を使っていたり一部ボタンの接触不良があったりで、前回のハザードトリガーに比べると、日々使って遊ぶ玩具としての完成度としてはやや劣るかなという気がしますが、玩具としての機能の充実さではハザードトリガーを上回るものにはなったかな、と思います。
最後に、改造ハザードトリガーと一緒にパチリ。一年に渡って続けてきたビルドの玩具改造もこれにておしまい、めでたしめでたし。。。
じゃなくて、もうちっとだけ続くんじゃ。
# 本当にもうちっとレベルですので、大掛かりなものはこれがラストです
ディスカッション
コメント一覧
( ゚д゚) ・・・
(つд⊂)ゴシゴシ
(;゚д゚) ・・・
(つд⊂)ゴシゴシゴシ
_, ._
(;゚ Д゚) …!?
もう、コンセレですやん! ( ゚∀゚)・∵.
自分は電源スイッチ付ける位しかできんかったですw
丈 様
ありがとうございます◎
今回初めてディテクタスイッチを使ったんですが、あれは確か丈様がフルフルに電源スイッチをつけたときに使われていたのを見て、「あ、こんなスイッチあるんや!いつか使お!」と思っていたのです。
おかげで完成までこぎつけることができました、ありがとうございます(^^)
お久しぶりです。
動画拝見しました。
Twitterの改造中画像を見て、ボトル60本音声とそれに対応したフルカラー点灯の予想はついたのですが、その予想を大幅に上回るものが出来上がるとは…
今回も回路図とスケッチを公開して頂けたので、自分でも同じものを作ることが出来るのでしょうが、音源集めで死ぬことになりそうなので、流石にこの改造は、息子に頼まれても断るしかないですね。
とても素晴らしい改造です。
ちたけうどん 様
長らく更新が滞っており申し訳ありません。コメント頂きありがとうございます◎ 期待を超えるものを出せたようで良かったです。
最近、ありがたいことに「何かおもしろいもの作るだろう」みたいな期待を頂くことがあり、プレッシャーではあるのですが、おかげで自分でも面白いものが作れているかなあと思います。
仰る通り、この記事の中でもさらっと流していますが、音源集めがとにかく大変でした。。。特にアギト、ブレイド、キバ!。優勝はアギト!(笑)
音声単体でなっている素材ってほとんどなくて、必殺技ってだいたい一番盛り上がるところで出すので、大概後ろでエンディングテーマが流れていて使いにくいという。。。
結局アギトは何かのゲームの音声から引っ張ってきました。
やっぱすげぇ…
スマブラガシャットで初めて拝見しまして、いつも感心してます!
じぇい 様
ありがとうございます◎ 自分の中ではスマブラガシャットを超えるものは未だ作れていないのですが、今回のジーニアスは結構良いセンいったかなあと思います。
なんやかんやで更新頻度がとても少なく申し訳ありませんが、今後も緩く見守って頂けますと幸いです。
今回のジーニアスボトルの動画を見たときに本当に興奮しました!
最初の曲が流れ出した時に既にあの初変身シーンが頭に浮かび上がって
きました!そしてその直後の戦兎の[さあ…実験を始めようか?]の
セリフで思わず[来たー!]と叫んでしまいました!
そしてあのボトルを一斉に読み上げてからの起動音は最高でした!
やっぱりジーニアスボトルだけあってこういうギミックがほしかったので
テンション上がりました
それにあのベストマッチの音声が流れるところで各ベストマッチに合った
必殺技を発動しているところを想像しました
レジェンドライダーの音声が流れたところにはここまでつけるのか!と
ビックリしました
とにかくハザードトリガー等もすごかったですが今回のジーニアスが
僕の中ではダントツトップでした!
あの基盤や仕組みを考え付いたのは天才です!
これからもいろんな発明品を期待しています!
長文失礼しました
如月 様
こんなに熱の込もったコメントを頂いたのは初めてです。恐縮です、ありがとうございます。大変嬉しいです。
記事本文でも書いていますが、やっぱりあのジーニアスの初変身シーンがなかったら、この作品は今の形にはなっていなかったと思います。ビルドは本当に良い作品でした。
これだけ褒められてしまうと、次の作品でガッカリさせてしまうのではないかと毎回心配になるのですが。。。一応、次の作品が11月上旬ぐらいに出来上がる見込みなので、またそのときぐらいに覗きに来ていただけると嬉しいです。ジーニアスほどド派手なものにはなりませんが、「これはこれでアリだよね」とは言ってもらえるものができるのではないかな、と思っています。
返信していただきありがとうございます!
いえいえがっかりなんてしません!
いつも楽しく見ています
それといつも助かっているところは動画内でどういう仕組みなのかを
解説していただけるところです
動画の中にはどういう仕組みなのかが解説されていないものもあるので
すごく役に立ちました!
一応僕もドライバーの塗装やオリジナルロストボトルの作成
基盤の移植で特殊なトリガーの作成などを趣味でやっているのですが
動画を見るたびにやっぱりこういう人はすごいなーと驚かされています
次の作品も気長に待ってます
体調に気を付けて頑張ってください
仕組み解説を入れ始めたのは比較的最近の動画からですね。以前スマブラガシャットを作成したときに、「ガシャット途中ですり替えてんじゃね?」みたいなコメントをたまーに頂いていたので、それがなんだか悔しくて、イカサマじゃないことをできるだけ証明するために入れることにしました。解説を入れるのはそれなりに手間がかかるのですが、少しでもニーズがあるなら、これからもできるだけ続けようと思います。
暖かいお言葉を頂きありがとうございました。如月様も色々オリジナルのものを作成されているとのことですので、宜しければご紹介くださいませ◎
確かにイカサマって言われるのは悔しいですよね
めげずに頑張ってください!
私はオリジナル設定でロストボトルで変身できる
ロストドライバー(金と黒のビルドドライバー)をベースにビルドのアイテムを模した
強化アイテムやボトルを作っています
如月様
返事が遅くなり申し訳ありません。ありがとうございます◎ 如月様は塗装関係のスキルをお持ちなのですね。羨ましい。。。以前ハザードトリガーを作ったときに特に感じたのですが、自分にもっと塗装技術があればもっと完成度を上げられるのに、と思うことがよくあります。自分でスキルを身に付けられればそれが一番ベストなのですが、諸々の制約でそれが実現できそうにない場合は、如月様のようなスキルをお持ちの方に、何かしら協力をお願いすることもあるかもしれません。そのときはどうぞ宜しくお願い致します。
返信ありがとうございます
いえいえ僕なんかまだまだ初心者です
つい数か月前に塗装にはまりだして趣味でやっていたので出来は悪いです
それなのにこのような言葉をいただきとても感激しました
僕で良ければ相談に乗りますです
はじめまして!いつも素晴らしい作品を作るヒダカさんを尊敬しています!自分もあるものにドライバー装着音を入れようとしているのですが、中々上手くいきません…どうすれば出来るのかヒダカさんのお知恵を拝借したい限りです…
K 様
はじめまして。先ほど、個別にメールを送らせて頂きましたので、お手数ですがご確認を宜しくお願い致します。
CSMなどでこういうものができてほしいすごいかっこいいです!
ありがとうございます! 今回色々手を加えてこんな形になりましたが、ジーニアスフルボトルは元々のアイデア(マジックミラーでボトルを無限増殖)が本当に秀逸なので、それに刺激されて出てきた改造、という感じです。
自分もクローズドラゴンを作りたく、コードを書いているのですが、うまく動きません、どうすれば良いでしょうか
うーん、具体的なところがわからないので抽象的な回答しかできませんが。。。とりあえず、課題を一個一個クリアしていくようにしてください。まずは発光させる。それができたら次は音声を鳴らす。それができたら次はスイッチによって状態を切り替えてそのタイミングで音を鳴らす。。。みたいな感じで、やりたいことを要素ごとに分解して、一つ一つ解決して積み重ねていけば、最終的には完成します。
ピンバック & トラックバック一覧
[…] 前回作成した改造版ジーニアスフルボトル、YouTube上での再生回数はそれほどでもありませんが、Twitter上では随分良い評価を頂きました(←単純な「いいね」数ならスマブラガシャット […]