ゲームボーイ SDK である GBDK を使ってオリジナルゲームを作ろうとしている様子をブログで公開しています。前回までの内容はこちらです:
GBDK でゲームボーイのオリジナルゲームを作る(0)
GBDK でゲームボーイのオリジナルゲームを作る(1)

前回はゲームボーイの各ボタンが押された操作を認識できるようにしました。今回は推された矢印ボタンに合わせてキャラクターが移動するように改良してみます。


【座標の取り扱い】
今回作成しているゲームは複雑なグラフィック操作を行わず、テキストベースの画面で作ることを想定しています。このテキストを表示する座標(座標)を gotoxy( 横座標, 縦座標 ) という関数で座標を指定してから printf() を実行する、という形で実現します。ちなみに画面左上の座標は ( 0, 0 ) です、つまり横座標も縦座標も 0 から始まります。

キャラクターが移動可能な座標範囲を 10x10 として、キャラクターを "@" という文字であらわすことにして、キャラクターの座標を pos_x, pos_y という変数で管理しながら「矢印ボタンが押されたら画面をクリアしてから該当座標にキャラクターを表示する」というロジックでソースコード(move.c)を作るとこんな感じになります(前回からの変更点を青字にしています):
#include <gb/gb.h>
#include <gbdk/console.h>
#include <stdint.h>
#include <stdio.h>

#define SIZE 10

#define min_x 1
#define min_y 3
#define max_x ( min_x + SIZE - 1 )
#define max_y ( min_y + SIZE - 1 )

void clearScreen(){
    uint8_t i;
    char line[SIZE+1];

    //. line 生成
    for( i = 0; i < SIZE; i ++ ){
        line[i] = ' ';
    }
    line[SIZE] = NULL;

    for( i = min_y; i <= max_y; i ++ ){
        gotoxy( min_x, i );
        printf( line );
    }
}

void moveTo( uint8_t x, uint8_t y ){
    clearScreen();
    gotoxy( x, y );
    printf( "@" );
}

void main( void ){
    uint8_t key;
    uint8_t pos_x = 1, pos_y = 3;

    gotoxy( 1, 1 );
    printf( "move" );

    CRITICAL {
        add_SIO(nowait_int_handler);
    }
    set_interrupts(SIO_IFLAG);

    while( 1 ) {
        key = waitpad( J_A | J_B | J_START | J_SELECT | J_UP | J_DOWN | J_LEFT | J_RIGHT );
        waitpadup();

        clearScreen();

        if( key == J_A ){
            // A Button
            //printf( "A" );
        }else if( key == J_B ){
            // B Button
            //printf( "B" );
        }else if( key == J_START ){
            // START Button
            //printf( "START" );
        }else if( key == J_SELECT ) {
            // SELECT Button
            //printf( "SELECT" );
        }else if( key == J_UP ) {
            // UP Button
            if( pos_y > min_y ){
                pos_y --;
            }
        }else if( key == J_DOWN ) {
            // DOWN Button
            if( pos_y < max_y ){
                pos_y ++;
            }
        }else if( key == J_LEFT ) {
            // LEFT Button
            if( pos_x > min_x ){
                pos_x --;
            }
        }else if( key == J_RIGHT ) {
            // RIGHT Button
            if( pos_x < max_x ){
                pos_x ++;
            }
        }
        moveTo( pos_x, pos_y );

        /* In case of user cancellation */
        waitpadup();
    }
}


最初に "#define SIZE 10" 行で画面のサイズ(縦と横の大きさ)を決め、実際に移動できる座標範囲を ( 1, 3 ) ~ (10, 12 ) と定義します(この範囲を超えての移動ができないようにします)。

また画面をクリアするための関数 clearScreen() と、指定座標に "@" キャラクターを表示する moveTo() 関数を定義しておきます。

そして前回同様に main() 関数のループ内でボタン入力を検知しますが、矢印ボタンだった場合はキャラクターの新しい表示座標を計算した上で moveTo() 関数を実行する、という内容です。


この move.c をビルドします。前回までと同様、Windows の場合は compile.bat を、macOS や Linux の場合は Makefile を用意し、それぞれ "compile.bat" と "make" を実行して move.gb をビルドします:

(compile.bat)
c:\MyApps\gbdk\bin\lcc -Wa-l -Wl-m -Wl-j -o move.gb move.c

(Makefile)
CC	= ~/gbdk/bin/lcc -Wa-l -Wl-m -Wl-j

BINS	= move.gb

all:	$(BINS)

# Compile and link single file in one pass
%.gb:	%.c
	$(CC) -o $@ $<

clean:
	rm -f *.o *.lst *.map *.gb *~ *.rel *.cdb *.ihx *.lnk *.sym *.asm *.noi


成功すると move.gb という ROM ファイルができあがります。ゲームボーイエミュレータを起動して同ファイルを読み込むと、入力した方向キーの内容に従ってキャラクター("@")が上下左右に動いている様子を確認できます(動作している様子のスクリーン動画を撮ってみました):



キー入力に連動してキャラクターが動かせるようになりました。ここまでできると今回目標としている「モールモール」や「倉庫番」のようなゲームを作る目途が立ちましたね。

次回はいよいよ「モールモールっぽい画面を作る」ことに挑戦する予定です。