Wio Terminalでゲームを作ってみる勝手企画②
さて、前回の
で初期の画面はできたかんじですが、動くゲームにするためにはいくつかクリヤしないといけないポイントがあります。
避けては通れない道
①キー入力
②画面のキャラクター判定
③(これはできれば)音とかも出せると(・∀・)イイネ!!
①と②が最優先、これがないとゲームになりません。③はまあ後でもいいでしょう。③があるとクオリティがあがるってやつですね。
①キー入力
何か良い例はないかなーと探してみると、あったあったありました☆
↑ここの「ブザーで楽器」というところで、スイッチ入力でブザー音を出している例があります。(ついでに③も解決できそうですね。いただきですw)
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
pinMode(WIO_5S_UP, INPUT_PULLUP);
pinMode(WIO_5S_DOWM, INPUT_PULLUP);
pinMode(WIO_5S_LEFT, INPUT_PULLUP);
pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
pinMode(WIO_5S_PRESS, INPUT_PULLUP);
このようにpinModeを設定して、あとからdigitalRead(WIO_5S_LEFT) == LOW のように値を読むようです(LOWだとスイッチが入力されている。ということ。)
斜め入力は↑と←が入力されたら↖と見なす、というふうに工夫すれば行けそうです。
テストプログラム
上記のpinMode設定をsetup内に入れて、loopの中に
void loop() {
String s = " ";
tft.drawString(" ", 100, 0, 2);
if (digitalRead(WIO_5S_UP) == LOW) {
s = "U";
if (digitalRead(WIO_5S_LEFT) == LOW) {
s = "UL";
} else if (digitalRead(WIO_5S_RIGHT) == LOW) {
s = "UR";
}
}else if(digitalRead(WIO_5S_DOWN) == LOW) {
s = "D";
if (digitalRead(WIO_5S_LEFT) == LOW) {
s = "DL";
} else if (digitalRead(WIO_5S_RIGHT) == LOW) {
s = "DR";
}
}else if(digitalRead(WIO_5S_LEFT) == LOW) {
s = "L";
}else if (digitalRead(WIO_5S_RIGHT) == LOW) {
s = "R";
}
tft.drawString(s, 130, 0, 2); // Draw the Number character
delay(500);
}
このようなプログラムを入れて実行してみます。
上
下
右上
左下。
ばっちり動作しているようです。,,Ծ‸Ծ,,v
では次に進みましょう(∩´∀`)∩☆
つぎは、
②画面のキャラクター判定
ですね~。
普通のゲームだったら敵キャラとか爆弾とか、パワーエサとか(?)にプレイヤーが当たったかどうか判定する必要があるでしょ。そのために画面上に表示されているキャラクター(この場合は数字)が、どの場所に何が表示されているか分からないといけません。
昔のゲーム作りに強いパソコンのBASICとかだとそういう命令があったそうですが、今の子にはそういうのついていないので、画面に表示している情報を保存しておくバーチャルな画面(情報だけ)の変数を別に用意することにします。
X方向に画面(キャラ)分、Y方向に画面(キャラ)分の2次元配列を用意すればいいかんじですね。
二次元配列の作り方と使い方はこのあたり参照。
この配列の内容を調べれば、XとYの位置ごとのデータを知ることができます。画面に表示するところで、画面の表示内容と同じ情報を一緒に配列に保存すれば良いわけですね。
具体的には
int STAGE[PLAY_FIELD_WIDTH][PLAY_FIELD_HEIGHT];
for (int y = 1; y < PLAY_FIELD_HEIGHT; y += 1) {
for (int x = 1; x < PLAY_FIELD_WIDTH; x += 1) {
int n = random(0, 9);
tft.drawNumber(n, x*TEXT_WIDTH, y*TEXT_HEIGHT, 2); // Draw the Number
STAGE[x][y]=n; // 画面キャラ情報を配列に入れておく
}
}
画面の初期表示部分にこのように配列の設定も一緒に入れておきます。
さあて、ここまでできたら、実際にゲームの形にしていきましょう♪
ゲームの動きを作る
ホンモノのJoachimさんのNumber Tronは、何とレトロPC用のゲームだったそうで、フリーウェアらしいけれどそもそも動かすのが大変そうw
なので、正解がわからないまま @MickyAlbertさんの
の動画を観察して、まずは
・自キャラは「X」というキャラクターとします。(存在X?)
・方向キーを押すと、その方向の数字が点滅
・スイッチを離すと、点滅していた数字を「食べ」て、その数字の数だけ自キャラ「X」はすすむ。(道中の数字は食べちゃって空白になる)
・空白や壁等数字以外に当たったらゲームオーバー
このようなルールでやってみることにします。(得点とか♡とかの要素は後回し。(♡って食べるとどうなるんでしょうね?w))
プロトタイプ作った☆
まだガガガと作っただけなのでぜんぜん調整はしていませんが
#include <TFT_eSPI.h> // Hardware-specific library #include <SPI.h>
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
#define TEXT_HEIGHT 16 // Height of text to be printed and scrolled #define TEXT_WIDTH 16 #define PLAY_FIELD_WIDTH 19 #define PLAY_FIELD_HEIGHT 14
#define BUZZER_PIN WIO_BUZZER
int STAGE[PLAY_FIELD_WIDTH][PLAY_FIELD_HEIGHT];
int MyX = 10; // Start Position
int MyY = 8;
int NowX = 0;
int NowY = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200); // いらないよね
pinMode(WIO_KEY_A, INPUT_PULLUP);
pinMode(WIO_KEY_B, INPUT_PULLUP);
pinMode(WIO_KEY_C, INPUT_PULLUP);
pinMode(WIO_5S_UP, INPUT_PULLUP);
pinMode(WIO_5S_DOWN, INPUT_PULLUP);
pinMode(WIO_5S_LEFT, INPUT_PULLUP);
pinMode(WIO_5S_RIGHT, INPUT_PULLUP);
pinMode(WIO_5S_PRESS, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
randomSeed(analogRead(A0)); // ランダムシード
tft.init();
tft.setRotation(3); // 3 がスティックのある面を下にした状態
stageInit();
Serial.print("Start ");
while( !buttonChk() ){
// start loop
}
Serial.println(": ");
NowY=0;
NowX=0;
}
void stageInit(){
tft.fillScreen(ILI9341_WHITE);
for(int i=TEXT_HEIGHT;i<PLAY_FIELD_HEIGHT*TEXT_HEIGHT;i+=1){
tft.drawLine(TEXT_WIDTH-8,i,PLAY_FIELD_WIDTH*TEXT_WIDTH,i,ILI9341_BLACK); //drawRectangle も、fillRectangle も使えない!?
}
for (int y = 1; y < PLAY_FIELD_HEIGHT; y += 1) {
for (int x = 1; x < PLAY_FIELD_WIDTH; x += 1) {
int n = random(1, 9);
STAGE[x][y]=n; // 画面キャラ情報を配列に入れておく
tft.drawNumber(STAGE[x][y], x*TEXT_WIDTH, y*TEXT_HEIGHT, 2); // Draw the Number
}
}
//初期キャラ位置
tft.drawChar('X', MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2);
STAGE[MyX][MyY]=0; // 空白は 0 とする。
tft.drawString(" < NUMBER TRON > ", 100, 0, 2); // Draw the Number character
}
void playTone(int tone, int duration) {
for (long i = 0; i < duration * 1000L; i += tone * 2) {
digitalWrite(BUZZER_PIN, HIGH);
delayMicroseconds(tone);
digitalWrite(BUZZER_PIN, LOW);
delayMicroseconds(tone);
}
}
int buttonChk(){ // 入力方向はNowXとNowYに入り、入力があったら1が戻り、なければ0を返す
int r=1;
if (digitalRead(WIO_5S_UP) == LOW) {
NowY=-1;
NowX=0;
if (digitalRead(WIO_5S_LEFT) == LOW) {
NowX=-1;
} else if (digitalRead(WIO_5S_RIGHT) == LOW) {
NowX=+1;
}
}else if(digitalRead(WIO_5S_DOWN) == LOW) {
NowY=+1;
NowX=0;
if (digitalRead(WIO_5S_LEFT) == LOW) {
NowX=-1;
} else if (digitalRead(WIO_5S_RIGHT) == LOW) {
NowX=+1;
}
}else if(digitalRead(WIO_5S_LEFT) == LOW) {
NowY=0;
NowX=-1;
}else if (digitalRead(WIO_5S_RIGHT) == LOW) {
NowY=0;
NowX=+1;
}else{
r=0;
NowY=0;
NowX=0;
}
return(r);
}
void GameOver(){
tft.drawChar('#', MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2); // 自キャラ表示
while(1){
//
}
}
void loop() {
// String s;
char s[100];
int b;
Serial.printf("NowX:%d , NowY:%d \n",NowX,NowY);
if(NowX==0 && NowY==0){ // 移動していなかったらボタンチェック
b = buttonChk();
Serial.print("B if ");
if(b){
Serial.println("in... ");
//点滅開始
int c=1;
int ox,oy;
while(c){
ox=NowX;
oy=NowY;
tft.drawString(" ", (MyX+ox)*TEXT_WIDTH, (MyY+oy)*TEXT_HEIGHT, 2); //Char(一文字)だとゴミが残る
delay(500);
tft.drawNumber(STAGE[MyX+ox][MyY+oy], (MyX+ox)*TEXT_WIDTH, (MyY+oy)*TEXT_HEIGHT, 2); // Draw the Number
playTone(1014, 100);
c = buttonChk();
tft.drawString(" ", (MyX)*TEXT_WIDTH, (MyY)*TEXT_HEIGHT, 2); //Char(一文字)だとゴミが残る
}
Serial.print("C Loop Out ");
for(int i=STAGE[MyX+ox][MyY+oy];i>0;i=i-1){
MyX=MyX+ox;
MyY=MyY+oy;
Serial.printf(s," MyX:%d MyY:%d Val:%d \n",MyX,MyY,STAGE[MyX][MyY]);
// tft.drawString(s, 110, 0, 2); // Draw the Number
if(STAGE[MyX][MyY]==0 ||MyX<1||MyY<1 || MyX>PLAY_FIELD_WIDTH-1 || MyY>PLAY_FIELD_HEIGHT-1 ){
playTone(1614, 1000);
GameOver();
}
playTone(1000-STAGE[MyX][MyY]*100, 100);
playTone(1500, 30);
tft.drawChar('X', MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2); // 自キャラ表示
STAGE[MyX][MyY]=0; // キャラ位置データも消去
delay(500);
//tft.drawChar(' ', MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2);
tft.drawString(" ", MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2); //Char(一文字)だとゴミが残る
}
}else{
tft.drawString(" ", MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2); //Char(一文字)だとゴミが残る
playTone(1014, 100);
tft.drawChar('X', MyX*TEXT_WIDTH, MyY*TEXT_HEIGHT, 2); // 自キャラ表示
delay(500);
}
}
}
だらだら長くなっちゃいました。もうちょっと読みやすく整理したほうがよさそうだけれど、まずは動くこと優先とゆーことで☆
動かしてみましたヨー。
むふふ。うごくうごく☆
ちゃっかり音も入れてみました☆
いまはまだ「動くのがたのしい」ってレベルですが、ちゃんと遊んで楽しめるようにリファイン&チューニングしないとですね~☆
次はスコアとか表示するようにしよっと♪
―――
さらにつづきー
#WioTerminal #ゲーム制作 #レトロゲーム #作ってみた #らせん工房
よろしければサポートお願いします!いただいたサポートはクリエイターとしての活動費にさせていただきます!感謝!,,Ծ‸Ծ,,