C++命令行推箱子小游戏

364 阅读2分钟

动机

看了《游戏开发》第一章,自己动手实现了一个命令行版推箱子小游戏.

image.png

游戏架构

采用了经典的游戏主循环架构

绘制画面;
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;
        }
    }
}