Created
April 29, 2021 02:13
-
-
Save KentaroAOKI/fcf4b732f2cd2d04283cae26f69ef230 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 初学者のためのゲームプログラミング | |
// #01 とりあえず動かしてみよう | |
// ゲームのルール | |
// - 主人公(MAN("a"))を動かしてブロック(BLOCK(","))をゴール(GOAL("O"))に移動 | |
// - ステージ全てのブロック(BLOCK)をゴール(GOAL)に移動させたら終了 | |
#include <iostream> | |
// ステージのMAPデータ | |
const char gStageData[] = "\ | |
########\n\ | |
#a #\n\ | |
# , #\n\ | |
# , #\n\ | |
# O O#\n\ | |
########"; | |
// ステージのMAPサイズ | |
const int gStageWidth = 8; | |
const int gStageHeight = 6; | |
// ステージ上のオブジェクト識別番号 | |
enum Object { | |
OBJ_SPACE, | |
OBJ_WALL, | |
OBJ_GOAL, | |
OBJ_BLOCK, | |
OBJ_BLOCK_ON_GOAL, | |
OBJ_MAN, | |
OBJ_MAN_ON_GOAL, | |
OBJ_UNKNOWN, | |
}; | |
// プロトタイプ宣言 | |
// ステージのマップデータからゲームの状態(state)を作成 | |
void initialize(Object* state, int w, int h, const char* stageData); | |
// ゲームの状態(state)からゲーム画面を出力 | |
void draw(const Object* state, int w, int h); | |
// 入力(input)状態から、主人公(MAN)とブロック(BLOCK)を動かしてゲームの状態(state)を更新 | |
void update(Object* state, char input, int w, int h); | |
// 全てのブロック(BLOCK)がゴール(GOAL)に置かれているか確認 全てゴールに置かれていたらtrueを返す | |
bool checkClear(const Object* state, int w, int h); | |
void initialize(Object* state, int width, int height, const char* stageData) { | |
const char* d = stageData; | |
int x = 0; | |
int y = 0; | |
while (*d != NULL) | |
{ | |
Object t; | |
switch (*d) | |
{ | |
case ' ': t = OBJ_SPACE; break; | |
case '#': t = OBJ_WALL; break; | |
case 'O': t = OBJ_GOAL; break; | |
case ',': t = OBJ_BLOCK; break; | |
case 'Q': t = OBJ_BLOCK_ON_GOAL; break; | |
case 'a': t = OBJ_MAN; break; | |
case '@': t = OBJ_MAN_ON_GOAL; break; | |
case '\n': | |
x = 0; | |
++y; | |
t = OBJ_UNKNOWN; | |
break; | |
default: t = OBJ_UNKNOWN; break; | |
} | |
++d; | |
if (t != OBJ_UNKNOWN) { | |
state[y * width + x] = t; | |
x++; | |
} | |
} | |
} | |
void draw(const Object* state, int width, int height) { | |
const char font[] = { ' ', '#', 'O', ',', 'Q', 'a', '@' }; | |
// 画面消去 | |
std::cout << "\x1B[2J\x1B[H"; | |
// ゲームの状態(state)からゲーム画面を出力(左上から右下まで順に描く) | |
for (int y = 0; y < height; ++y) { | |
for (int x = 0; x < width; ++x) { | |
Object o = state[y * width + x]; | |
std::cout << font[o]; | |
} | |
std::cout << std::endl; | |
} | |
} | |
void update(Object* state, char input, int w, int h) { | |
// 入力(input)状態によって主人公(MAN)の相対的な移動先を決定 | |
int dx = 0; | |
int dy = 0; | |
switch (input) { | |
case 'a': | |
dx = -1; | |
break; | |
case 's': | |
dx = 1; | |
break; | |
case 'w': | |
dy = -1; | |
break; | |
case 'z': | |
dy = 1; | |
break; | |
} | |
// 主人公(MAN)の場所(x, y)をゲームの状態(state)から探す | |
int i = 0; | |
for (i = 0; i < w * h; ++i) { | |
if (state[i] == OBJ_MAN || state[i] == OBJ_MAN_ON_GOAL) { | |
break; | |
} | |
} | |
int x = i % w; | |
int y = i / w; | |
// 主人公(MAN)の移動先(tx, ty)を計算 | |
int tx = x + dx; | |
int ty = y + dy; | |
if (tx < 0 || ty < 0 || tx >= w || ty >= h) { | |
return; | |
} | |
// ゲームの状態(state)を更新する | |
int p = y * w + x; | |
int tp = ty * w + tx; | |
if (state[tp] == OBJ_SPACE || state[tp] == OBJ_GOAL) { | |
// 移動先が空きかゴールであれば、主人公(MAN)の移動先と移動元の状態(state)を更新する | |
state[tp] = (state[tp] == OBJ_GOAL) ? OBJ_MAN_ON_GOAL : OBJ_MAN; | |
state[p] = (state[p] == OBJ_MAN_ON_GOAL) ? OBJ_GOAL : OBJ_SPACE; | |
} | |
else if (state[tp] == OBJ_BLOCK || state[tp] == OBJ_BLOCK_ON_GOAL) { | |
// 移動先がブロックであれば、ブロックを動かすことができるか確認する | |
int tx2 = tx + dx; | |
int ty2 = ty + dy; | |
if (tx2 < 0 || ty2 < 0 || tx2 >= w || ty2 >= h) { | |
return; | |
} | |
int tp2 = (ty + dy) * w + (tx + dx); | |
if (state[tp2] == OBJ_SPACE || state[tp2] == OBJ_GOAL) { | |
// ブロックの移動先が空きかゴールであれば、ブロック(BLOCK)と主人公(MAN)の移動先と移動元の状態(state)を更新する | |
state[tp2] = (state[tp2] == OBJ_GOAL) ? OBJ_BLOCK_ON_GOAL : OBJ_BLOCK; | |
state[tp] = (state[tp] == OBJ_BLOCK_ON_GOAL) ? OBJ_MAN_ON_GOAL : OBJ_MAN; | |
state[p] = (state[p] == OBJ_MAN_ON_GOAL) ? OBJ_GOAL : OBJ_SPACE; | |
} | |
} | |
} | |
bool checkClear(const Object* state, int width, int height) { | |
// ゲームの状態(state)にブロック(ゴールに移動されていないブロック)が存在すればfalse | |
for (int i = 0; i < width * height; ++i) { | |
if (state[i] == OBJ_BLOCK) { | |
return false; | |
} | |
} | |
return true; | |
} | |
int main() | |
{ | |
char input; | |
Object* state = new Object[gStageWidth * gStageHeight]; | |
initialize(state, gStageWidth, gStageHeight, gStageData); | |
while (true) | |
{ | |
// メインループ | |
draw(state, gStageWidth, gStageHeight); | |
if (checkClear(state, gStageWidth, gStageHeight)) { | |
break; | |
} | |
std::cout << "a:left s:right w:up z:down. What direction do you move?" << std::endl; | |
std::cin >> input; | |
update(state, input, gStageWidth, gStageHeight); | |
} | |
delete[] state; | |
state = NULL; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment