RGKT-NDS-HMBR-005(004)
先日ソースをアップしたシーケンス遷移サンプルを使って
簡単なゲームを作ってみました。
最初に動いてから結構時間掛かっちゃいましたけど頃合な感じなんで
ソース公開します。
コンソールベースで凄く原始的な、
ブロック崩しや PONG のベースになるようなプログラムです。
最低限の基本的な処理は大体含んでます。
ただし、キャラクタ関連の表示手順が
コンソール表示機能を利用した場合の表示方法を取ってる為、
少しだけ無駄を含んだコードが見受けられるかも知れません。
スプライトで組んだ場合はもう少しだけ簡素に記述可能かと思います。
凝ったアルゴリズムではなくて恐縮ですが
参考になりそうな箇所は適当に使ってみたりしてみてください。
…結局一番苦労したのは関数や変数や定数の命名とソースのコメントという
コード本体とは全然関係ないところにあったのでした。(ー人ー)チーン
遊び方等は後ほど…。
ピンポンゲーム(簡易版) ソースコード
main.h
// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。 #ifndef _MAIN_H_ #define _MAIN_H_ // TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。 #include <nds.h> #ifdef __cplusplus extern "C" { #endif // TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。 //シーン定数 typedef enum{ SCENE_TITLE, SCENE_GAME }SCENE_SEQ; //シーン遷移フラグ用構造体 typedef struct{ u16 act; }ST_SCENE; #ifdef __cplusplus } #endif #endif
main.c
// root //--------------------------------------------------------------------------------- #include <stdio.h> #include "main.h" #include "title.h" #include "game.h" //--------------------------------------------------------------------------------- // シーン遷移フラグ static ST_SCENE scene = {SCENE_TITLE}; //--------------------------------------------------------------------------------- void initMain(void); void checkScene(void); //--------------------------------------------------------------------------------- int main(void) { initMain(); while(1) { scanKeys(); // シーケンス遷移動作 switch(scene.act){ case SCENE_TITLE: updateTitle(); break; case SCENE_GAME: updateGame(); break; default: break; } // 遷移トリガチェック checkScene(); swiWaitForVBlank(); } return 0; } //--------------------------------------------------------------------------------- void initMain(void) { // TODO: ここに 初期化処理を追加する。 // コンソールの初期化。 consoleDemoInit(); } //--------------------------------------------------------------------------------- // シーン遷移トリガ関数 void checkScene(void) { switch(scene.act){ case SCENE_TITLE: if(isExitTitle()) scene.act = SCENE_GAME; break; case SCENE_GAME: if(isExitGame()) scene.act = SCENE_TITLE; break; default: break; } } //---------------------------------------------------------------------------------
title.h
// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。 #ifndef _TITLE_H_ #define _TITLE_H_ // TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。 #include <nds.h> #ifdef __cplusplus extern "C" { #endif // TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。 //シーン定数 typedef enum{ TITLE_INIT, TITLE_EXIT }TITLE_SEQ; //シーン遷移フラグ用構造体 typedef struct{ u16 act; }ST_TITLE; //グローバル関数(シーン更新・シーン終了確認用)、プロトタイプ宣言 void updateTitle(void); bool isExitTitle(void); #ifdef __cplusplus } #endif #endif
title.c
// SCENE_TITLE //--------------------------------------------------------------------------------- #include <stdio.h> #include "title.h" #include "gamePlay.h" //--------------------------------------------------------------------------------- // シーン遷移フラグ static ST_TITLE title = {TITLE_INIT}; //--------------------------------------------------------------------------------- static void initTitle(void); static void exitTitle(void); //--------------------------------------------------------------------------------- // シーン更新関数 void updateTitle(void) { switch(title.act){ case TITLE_INIT: initTitle(); break; case TITLE_EXIT: exitTitle(); break; default: break; } } //--------------------------------------------------------------------------------- static void initTitle(void) { // TODO: ここに 初期化処理を追加する。 // コンソールクリア consoleClear(); // タイトルの表示 iprintf("\x1b[8;7HP i n g P o n g"); // ハイスコアの表示 iprintf("\x1b[14;9HHigh Score:%d", high_score); // コピーライトの表示 iprintf("\x1b[20;7H(C)2009 REGEKATSU"); iprintf("\x1b[21;10HNDS-HMBR-005"); // タイトルシーンを終了し、ゲームコンフィグシーンへ遷移する。 title.act = TITLE_EXIT; } //--------------------------------------------------------------------------------- static void exitTitle(void) { title.act = TITLE_INIT; } //--------------------------------------------------------------------------------- // シーン終了確認関数 bool isExitTitle(void) { return (title.act == TITLE_INIT) ? true : false; } //---------------------------------------------------------------------------------
game.h
// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。 #ifndef _GAME_H_ #define _GAME_H_ // TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。 #include <nds.h> #ifdef __cplusplus extern "C" { #endif // TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。 //シーン定数 typedef enum{ GAME_INIT, GAME_CONFIG, GAME_PLAY, GAME_EXIT }GAME_SEQ; //シーン遷移フラグ用構造体 typedef struct{ u16 act; }ST_GAME; //グローバル関数(シーン更新・シーン終了確認用)、プロトタイプ宣言 void updateGame(void); bool isExitGame(void); #ifdef __cplusplus } #endif #endif
game.c
// SCENE_GAME //--------------------------------------------------------------------------------- #include <stdio.h> #include "game.h" #include "gamePlay.h" #include "gameConfig.h" //--------------------------------------------------------------------------------- // シーン遷移フラグ static ST_GAME game = {GAME_INIT}; //--------------------------------------------------------------------------------- static void initGame(void); static void exitGame(void); static void checkSceneGame(void); //--------------------------------------------------------------------------------- // シーン更新関数 // タイトルシーンより遷移してくる。 // 最初にゲームコンフィグシーンに遷移。 // ゲームコンフィグシーンが終了したら // ゲームプレイシーンに遷移する。 // ゲームプレイシーンが終了したら、 // ゲームシーンを抜けて、タイトルシーンへ遷移する。 void updateGame(void) { switch(game.act){ case GAME_INIT: initGame(); break; case GAME_CONFIG: updateGameConfig(); checkSceneGame(); break; case GAME_PLAY: updateGamePlay(); checkSceneGame(); break; case GAME_EXIT: exitGame(); break; default: break; } } //--------------------------------------------------------------------------------- static void initGame(void) { // TODO: ここに 初期化処理を追加する。 game.act = GAME_CONFIG; } //--------------------------------------------------------------------------------- static void exitGame(void) { game.act = GAME_INIT; } //--------------------------------------------------------------------------------- // シーン遷移トリガ関数 static void checkSceneGame(void) { switch(game.act){ case GAME_INIT: break; case GAME_CONFIG: if(isExitGameConfig()) game.act = GAME_PLAY; break; case GAME_PLAY: if(isExitGamePlay()) game.act = GAME_EXIT; break; case GAME_EXIT: break; default: break; } } //--------------------------------------------------------------------------------- // シーン終了確認関数 bool isExitGame(void) { return (game.act == GAME_INIT) ? true : false; } //---------------------------------------------------------------------------------
gamePlay.h
// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。 #ifndef _GAME_PLAY_H_ #define _GAME_PLAY_H_ // TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。 #include <nds.h> #ifdef __cplusplus extern "C" { #endif // TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。 //シーン定数 typedef enum{ GAME_PLAY_INIT, GAME_PLAY_MAIN_INIT, GAME_PLAY_MAIN_EXEC, GAME_PLAY_MAIN_MISS, GAME_PLAY_MAIN_END, GAME_PLAY_EXIT }GAME_PLAY_SEQ; //ゲームレベル定義 typedef enum{ LEVEL_EASY, LEVEL_NORMAL, LEVEL_HARD }LEVEL_STATUS; //シーン遷移用、構造体変数 typedef struct{ u16 act; }ST_GAME_PLAY; //外部公開用変数 extern int high_score; extern LEVEL_STATUS game_level; //外部公開用関数、プロトタイプ宣言 void updateGamePlay(void);// bool isExitGamePlay(void); void setGameLevel(void); #ifdef __cplusplus } #endif #endif
gamePlay.c
// SCENE_GAME_PLAY //--------------------------------------------------------------------------------- #include <stdio.h> #include "gamePlay.h" //--------------------------------------------------------------------------------- // ゲーム画面内の移動・描画の為の定義 typedef enum{ CONSOLE_WIDTH_MIN = 0, CONSOLE_WIDTH_MAX = 31, CONSOLE_HEIGHT_MIN = 0, CONSOLE_HEIGHT_MAX = 23 }CONSOLE_SCREEN_SIZE; // パドル動作・描画の為の定義 typedef enum{ PADDLE_WIDTH_SIZE = 3, PADDLE_HEIGHT_SIZE = 1, PADDLE_WIDTH_POS = 15, PADDLE_HEIGHT_POS = 21 }PADDLE_STATUS; // ボール動作・描画の為の定義 typedef enum{ BALL_WIDTH_SIZE = 1, BALL_HEIGHT_SIZE = 1, BALL_WIDTH_POS = 5, BALL_HEIGHT_POS = 5, BALL_WIDTH_DIR = 1, BALL_HEIGHT_DIR = 1 }BALL_STATUS; // ゲーム用、デフォルト値 #define DEFAULT_BALL_NUM 3 #define DEFAULT_SCORE_NUM 0 // ゲーム用、キャラクタ描画パラメータ typedef enum{ DRAW_HIDE, DRAW_SHOW }DRAW_PARAM; // ゲーム速度設定用マクロ // ゲームレベルの低いほどゲーム速度は遅く、高いほどゲーム速度は速くなる。 #define SET_WAIT_LEVEL(x) (3-(x)) //--------------------------------------------------------------------------------- // シーン遷移フラグ static ST_GAME_PLAY game_play = {GAME_PLAY_INIT}; //--------------------------------------------------------------------------------- // ゲーム構造体、メンバー変数定義 typedef struct{ int px; // パドルX座標 int py; // パドルY座標 int bx; // ボールX座標 int by; // ボールY座標 int dx; // ボールX移動方向 int dy; // ボールY移動方向 }GAME_STATUS; static GAME_STATUS game_stat = {0}; // ゲームプレイステータス int ball_num, // 持ちボール数 score, // スコア high_score = DEFAULT_SCORE_NUM; // ハイスコア LEVEL_STATUS game_level = LEVEL_EASY; // ゲームレベル //--------------------------------------------------------------------------------- // ゲームプレイ用、シーン遷移関数 static void initGamePlay(void); static void initGamePlayMain(void); static void execGamePlayMain(void); static void missGamePlayMain(void); static void endGamePlayMain(void); static void exitGamePlay(void); // ゲームプレイ用、動作別更新関数 static void updatePaddle(void); static void updateBall(void); static void updateStatus(void); // ゲームプレイ用、キャラクタ描画関数 static void drawPaddle(DRAW_PARAM); static void drawBall(DRAW_PARAM); // ゲームプレイ用、トリガチェック関数 static bool isUpdateBall(void); static bool isMissGamePlayMain(void); static bool isEndGamePlayMain(void); // ゲームプレイ用、状態ウェイト関数 static void setWaitForVBlank(u32); //--------------------------------------------------------------------------------- // シーン更新関数 void updateGamePlay(void) { swiWaitForVBlank(); scanKeys(); switch(game_play.act){ case GAME_PLAY_INIT: initGamePlay(); break; case GAME_PLAY_MAIN_INIT: initGamePlayMain(); break; case GAME_PLAY_MAIN_EXEC: execGamePlayMain(); break; case GAME_PLAY_MAIN_MISS: missGamePlayMain(); break; case GAME_PLAY_MAIN_END: endGamePlayMain(); break; case GAME_PLAY_EXIT: exitGamePlay(); break; default: break; } } //--------------------------------------------------------------------------------- static void initGamePlay(void) { ball_num = DEFAULT_BALL_NUM; score = DEFAULT_SCORE_NUM; // コンソールをクリアする。 consoleClear(); game_play.act = GAME_PLAY_MAIN_INIT; } //--------------------------------------------------------------------------------- static void initGamePlayMain(void) { game_stat.px = PADDLE_WIDTH_POS; game_stat.py = PADDLE_HEIGHT_POS; game_stat.bx = BALL_WIDTH_POS; game_stat.by = BALL_HEIGHT_POS; game_stat.dx = BALL_WIDTH_DIR; game_stat.dy = BALL_HEIGHT_DIR; // ゲーム画面のクリアを行う。ゲームミス後の再ゲームスタート等に必要。 consoleClear(); game_play.act = GAME_PLAY_MAIN_EXEC; } //--------------------------------------------------------------------------------- static void execGamePlayMain(void) { // プレイヤ移動動作の更新を行う。 updatePaddle(); // ボール移動動作の更新を行う。 // キー入力受付を2回するうちボール更新を1回までとする。 if(isUpdateBall()) updateBall(); // ゲーム情報(スコア、残機数)の更新・表示を行う。 updateStatus(); // ゲームミスの確認。ミスしていれば状態遷移を行う。 if(isMissGamePlayMain()) game_play.act = GAME_PLAY_MAIN_MISS; // ゲームレベルに応じてウエイトを設ける // ここの数値が低い程、ゲーム速度が速くなる(ゲーム難易度が上がる)。 setWaitForVBlank(SET_WAIT_LEVEL(game_level)); } //--------------------------------------------------------------------------------- static void missGamePlayMain(void) { // 画面に「ミス」のメッセージを割り込ませるかたちで表示する。 iprintf("\x1b[11;14Hmiss"); // ボール残数を減らす。 ball_num--; // 1秒間のウェイトを設ける。 setWaitForVBlank(60); // ボール残数が無くなったらゲームオーバーへ遷移する。 if(isEndGamePlayMain()) game_play.act = GAME_PLAY_MAIN_END; // ボール残数が有るならゲームを再スタートへ遷移する。 else game_play.act = GAME_PLAY_MAIN_INIT; } //--------------------------------------------------------------------------------- static void endGamePlayMain(void) { // 画面をクリアして、「ゲームオーバー」のメッセージを表示する。 consoleClear(); iprintf("\x1b[11;10HGame Over!!"); // 3秒間のウェイトを設ける。 setWaitForVBlank(180); // 状態遷移。ゲームプレイシーン終了へ遷移。 game_play.act = GAME_PLAY_EXIT; } //--------------------------------------------------------------------------------- static void exitGamePlay(void) { game_play.act = GAME_PLAY_INIT; } //--------------------------------------------------------------------------------- static void updatePaddle(void) { // パドル動作の更新 // 前回のパドル画像を消去する。 drawPaddle(DRAW_HIDE); // 現在のパドル位置を取得・更新する。 if(keysHeld() & KEY_LEFT){ if(game_stat.px > CONSOLE_WIDTH_MIN) game_stat.px--; } if(keysHeld() &KEY_RIGHT){ if(game_stat.px <= CONSOLE_WIDTH_MAX - PADDLE_WIDTH_SIZE) game_stat.px++; } // 現在のパドル画像を描画する。 drawPaddle(DRAW_SHOW); } //--------------------------------------------------------------------------------- static void updateBall(void) { // ボール移動方向 // ボールが画面左端の場合、移動方向を右へ変更する。 if(game_stat.bx <= CONSOLE_WIDTH_MIN){ game_stat.dx = -game_stat.dx; } // ボールが画面右端の場合、移動方向を左へ変更する。 if(game_stat.bx >= CONSOLE_WIDTH_MAX){ game_stat.dx = -game_stat.dx; } // ボールが画面上端の場合、移動方向を下へ変更する。 if(game_stat.by <= CONSOLE_HEIGHT_MIN){ game_stat.dy = -game_stat.dy; } // ボールが画面下端の場合、移動方向を上へ変更する。(デバッグ用) /* if(game_stat.by >= CONSOLE_HEIGHT_MAX){ game_stat.dy = -game_stat.dy; } */ // ボールがパドルに当たってるかを確認する。 if((game_stat.by == (game_stat.py - 1))){ if((game_stat.bx >= game_stat.px -1) && (game_stat.bx <= game_stat.px + PADDLE_WIDTH_SIZE)){ game_stat.dy = -game_stat.dy; // ボールを跳ね返したらスコアを更新する。 score++; // 現在のスコアがハイスコアを越えてたらハイスコアを更新する。 if(score > high_score) high_score = score; } } // 前回のボール画像を消去する。 drawBall(DRAW_HIDE); // ボール座標を移動方向へ進める。 game_stat.bx += game_stat.dx; game_stat.by += game_stat.dy; // 現在のボール画像を描画する。 drawBall(DRAW_SHOW); } //--------------------------------------------------------------------------------- static void updateStatus(void) { iprintf("\x1b[23;0Hball:%d score:%d", ball_num , score); } //--------------------------------------------------------------------------------- static void drawPaddle(DRAW_PARAM param) { char str[] = " "; switch(param){ case DRAW_HIDE: break; case DRAW_SHOW: sprintf(str, "---"); break; default: break; } iprintf("\x1b[%d;%dH%s", game_stat.py, game_stat.px, str); } //--------------------------------------------------------------------------------- static void drawBall(DRAW_PARAM param) { char c; c = (param == DRAW_SHOW) ? 'O' : ' '; iprintf("\x1b[%d;%dH%c", game_stat.by, game_stat.bx, c); } //--------------------------------------------------------------------------------- static bool isUpdateBall(void) { static u32 update_interval; update_interval++; return (update_interval % 2) ? true : false; } //--------------------------------------------------------------------------------- static bool isMissGamePlayMain(void) { return (game_stat.by > PADDLE_HEIGHT_POS) ? true : false; } //--------------------------------------------------------------------------------- static bool isEndGamePlayMain(void) { return (ball_num <= 0) ? true : false; } //--------------------------------------------------------------------------------- // シーン終了確認関数 bool isExitGamePlay(void) { return (game_play.act == GAME_PLAY_INIT) ? true : false; } //--------------------------------------------------------------------------------- // ゲームレベル変更関数 void setGameLevel(void) { if(game_level < LEVEL_HARD){ game_level++; }else if(game_level >= LEVEL_HARD){ game_level = LEVEL_EASY; } } //--------------------------------------------------------------------------------- // 状態ウェイト関数 static void setWaitForVBlank(u32 frame) { while(frame){ swiWaitForVBlank(); frame--; } } //---------------------------------------------------------------------------------
gameConfig.h
// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。 #ifndef _GAME_CONFIG_H_ #define _GAME_CONFIG_H_ // TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。 #include <nds.h> #ifdef __cplusplus extern "C" { #endif // TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。 //シーン定数 typedef enum{ GAME_CONFIG_INIT, GAME_CONFIG_EXEC, GAME_CONFIG_EXIT }GAME_CONFIG_SEQ; //シーン遷移フラグ用構造体 typedef struct{ u16 act; }ST_GAME_CONFIG; //グローバル関数(シーン更新・シーン終了確認用)、プロトタイプ宣言 void updateGameConfig(void); bool isExitGameConfig(void); #ifdef __cplusplus } #endif #endif
gameConfig.c
// SCENE_GAME_CONFIG //--------------------------------------------------------------------------------- #include <stdio.h> #include "gameConfig.h" #include "gamePlay.h" //--------------------------------------------------------------------------------- // レベルセレクト表示座標 #define SELECT_LEVEL_WIDTH_POS 13 #define SELECT_LEVEL_HEIGHT_POS 10 //--------------------------------------------------------------------------------- // シーン遷移フラグ static ST_GAME_CONFIG game_config = {GAME_CONFIG_INIT}; //--------------------------------------------------------------------------------- // ゲームコンフィグ用、シーン遷移関数 static void initGameConfig(void); static void execGameConfig(void); static void exitGameConfig(void); // ゲームコンフィグ用、ゲームレベル選択関数 static void selectGameLevel(void); //--------------------------------------------------------------------------------- // シーン更新関数 void updateGameConfig(void) { switch(game_config.act){ case GAME_CONFIG_INIT: initGameConfig(); break; case GAME_CONFIG_EXEC: execGameConfig(); break; case GAME_CONFIG_EXIT: exitGameConfig(); break; default: break; } } //--------------------------------------------------------------------------------- static void initGameConfig(void) { // TODO: ここに 初期化処理を追加する。 // 初期レベル(イージーレベル)になるよう初期化する。 game_level = LEVEL_HARD; selectGameLevel(); // レベルセレクトの表示 iprintf("\x1b[%d;%dHeasy", SELECT_LEVEL_HEIGHT_POS + LEVEL_EASY, SELECT_LEVEL_WIDTH_POS); iprintf("\x1b[%d;%dHnormal", SELECT_LEVEL_HEIGHT_POS + LEVEL_NORMAL, SELECT_LEVEL_WIDTH_POS); iprintf("\x1b[%d;%dHhard", SELECT_LEVEL_HEIGHT_POS + LEVEL_HARD, SELECT_LEVEL_WIDTH_POS); game_config.act = GAME_CONFIG_EXEC; } //--------------------------------------------------------------------------------- static void execGameConfig(void) { // TODO: ここに 実行動作を追加する。 // セレクトボタンが押されたらゲームレベルを選択する。 if(keysDown() & KEY_SELECT) selectGameLevel(); // スタートボタンが押されたらゲームコンフィグシーンを終了する。 if(keysDown() & KEY_START) game_config.act = GAME_CONFIG_EXIT; } //--------------------------------------------------------------------------------- static void exitGameConfig(void) { game_config.act = GAME_CONFIG_INIT; } //--------------------------------------------------------------------------------- // シーン終了確認関数 bool isExitGameConfig(void) { return (game_config.act == GAME_CONFIG_INIT) ? true : false; } //--------------------------------------------------------------------------------- // ゲームレベル選択関数 static void selectGameLevel(void) { // 前回のカーソルを消す。 iprintf("\x1b[%d;%dH ", SELECT_LEVEL_HEIGHT_POS + game_level, SELECT_LEVEL_WIDTH_POS - 1); // ゲームレベルを上げる。 setGameLevel(); // 現在のカーソルを表示する。 iprintf("\x1b[%d;%dH*", SELECT_LEVEL_HEIGHT_POS + game_level, SELECT_LEVEL_WIDTH_POS - 1); } //---------------------------------------------------------------------------------