动机
看了《游戏开发》第一章,自己动手实现了一个命令行版推箱子小游戏.
游戏架构
采用了经典的游戏主循环架构
绘制画面;
while (true) {
获取输入;
更新游戏状态;
重新绘制画面;
if (游戏结束) {
break;
}
}
代码实现
#include <iostream>
//游戏对象枚举
enum Object {
UN_KNOWN,
WALL,
EMPTY_BLOCK,
BOX,
BOX_ON_GOAL,
MAN,
MAN_ON_GOAL,
GOAL
};
//方向枚举
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
};
//地图配置
//#墙壁 p人物 o箱子 .目的地 x在目的地的箱子 q在目的地的人物
char MAP[] = "\
########\
#______#\
#p__oo_#\
#___.._#\
#___xx_#\
########";
const int MAP_WIDTH = 8;
const int MAP_HEIGHT = 6;
const char OBJECT_CHAR_TABLE[] = {'?', '#', ' ', 'o', 'x', 'p', 'q', '.'};
//将配置转为游戏对象
Object* loadMapObject() {
Object * objectTable = new Object[MAP_WIDTH * MAP_HEIGHT];
char * mapItem = MAP;
Object* object = objectTable;
while (*mapItem != '\0') {
switch (*mapItem) {
case '#':
*object = WALL;
break;
case '_':
*object = EMPTY_BLOCK;
break;
case 'o':
*object = BOX;
break;
case '.':
*object = GOAL;
break;
case 'x':
*object = BOX_ON_GOAL;
break;
case 'q':
*object = MAN_ON_GOAL;
break;
case 'p':
*object = MAN;
break;
default:
*object = UN_KNOWN;
}
mapItem++;
object++;
}
return objectTable;
}
//检查游戏状态
bool isWin(Object * objectTable) {
//找不到箱子,说明已过关
for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) {
if (objectTable[i] == BOX) {
return false;
}
}
return true;
}
int findManPos(Object* objectTable) {
for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) {
if (objectTable[i] == MAN || objectTable[i] == MAN_ON_GOAL) {
return i;
}
}
return -1;
}
int moveX(int x, Direction direction) {
if (direction == LEFT) {
return x - 1;
} else if (direction == RIGHT) {
return x + 1;
} else {
return x;
}
}
int moveY(int y, Direction direction) {
if (direction == UP) {
return y - 1;
} else if (direction == DOWN) {
return y + 1;
} else {
return y;
}
}
bool isPosValid(int x, int y) {
return !(x < 0 || x >= MAP_WIDTH || y < 0 || y > MAP_HEIGHT);
}
//移动人物
void move(Object * objectTable, Direction direction) {
int manPos = findManPos(objectTable);
int manY = manPos / MAP_WIDTH;
int manX = manPos % MAP_WIDTH;
Object man = objectTable[manPos];
int targetX = moveX(manX, direction);
int targetY = moveY(manY, direction);
//边界检查
if (!isPosValid(targetX, targetY)) {
return;
}
//根据前方对象,执行不同的行为
int targetIdx = targetY * MAP_WIDTH + targetX;
Object preObject = objectTable[targetIdx];
if (preObject == EMPTY_BLOCK) {
//前方是空地
objectTable[targetIdx] = MAN;
} else if (preObject == GOAL) {
//前方是目的地
objectTable[targetIdx] = MAN_ON_GOAL;
} else if (preObject == WALL || preObject == UN_KNOWN) {
//前方是墙,什么也不做
return;
} else if (preObject == BOX || preObject == BOX_ON_GOAL) {
//前方是箱子,还要看再前面能不能走
int forwardX = moveX(targetX, direction);
int forwardY = moveY(targetY, direction);
//前方坐标到达边界
if (!isPosValid(forwardX, forwardY)) {
return;
}
int forwardIdx = forwardY * MAP_WIDTH + forwardX;
int forwardObject = objectTable[forwardIdx];
//前方还是箱子
if (forwardObject == BOX || forwardObject == BOX_ON_GOAL) {
return;
}
//前方是墙或未知对象
if (forwardObject == WALL || forwardObject == UN_KNOWN) {
return;
}
//前方可推进
if (forwardObject == EMPTY_BLOCK || forwardObject == GOAL) {
objectTable[targetIdx] = preObject == BOX ? MAN : MAN_ON_GOAL;
objectTable[forwardIdx] = forwardObject == EMPTY_BLOCK ? BOX : BOX_ON_GOAL;
}
}
//改变原本的位置
objectTable[manPos] = man == MAN ? EMPTY_BLOCK : GOAL;
}
void printObjectTable(Object * objectTable) {
for (int i = 0; i < MAP_HEIGHT; i++) {
for (int j = 0; j < MAP_WIDTH; j++) {
std::cout << OBJECT_CHAR_TABLE[objectTable[i * MAP_WIDTH + j]] << " ";
}
std::cout << std::endl;
}
}
using namespace std;
//CTRL + F5运行程序
int main()
{
Object * objectTable = loadMapObject();
printObjectTable(objectTable);
//主循环
while (true) {
cout << "Please input command(w: up, s: down, a: left, d: right):";
//1.获取输入指令
char command;
cin >> command;
Direction direction;
switch (command)
{
case 'w':
direction = UP;
break;
case 's':
direction = DOWN;
break;
case 'a':
direction = LEFT;
break;
case 'd':
direction = RIGHT;
break;
default:
cout << "invalid command!" << endl;
continue;
}
//2.更新游戏状态
move(objectTable, direction);
//3.重新绘制游戏画面
printObjectTable(objectTable);
//4.检测游戏是否结束
if (isWin(objectTable)) {
cout << "game finish" << endl;
break;
}
}
}