#include// 引入标准输入输出 #include // 引入window程序相关 #include // 引入easy_x图形库 #include // 获取热键需要的头文件 #include // 引入字符串 #include // 引入常用的函数 #include // 引入时间函数 // =============函数原型的声明============= void gameControl(enum _DIRECTION direction); void changeMap(int row,int column, enum _PROPS prop); bool isGameOver(); void gameOver(IMAGE *bg); // ========== 地图窗口的一些宏定义 ========== #define WINDOW_WIDTH 960 // 窗口的宽度 #define WINDOW_HEIGHT 768 // 窗口的高度 #define ROW 9 // 地图道具有多少行 #define COLUMN 12 // 地图道具有多少列 #define BMP_SIZE 61 // 道具图片的大小 宽度和高度 #define BRESIZE_TRUE true // 是否自动拉伸图片 是 #define START_X 100 // 偏移的x值 #define START_Y 150 // 偏移的y值 // ========== 玩家热键的一些宏定义 ========== #define KEY_UP 'w' #define KEY_DOWN 's' #define KEY_LEFT 'a' #define KEY_RIGHT 'd' #define KEY_Quit 'q' // ========== 判断是否越界 ========== #define isValid(next_pos) (next_pos.x >= 0 && next_pos.x < ROW && next_pos.y >=0 && next_pos.y < COLUMN) // 定义玩家位置的结构体 typedef struct _player_pos { int x; // 玩家所在的行 int y; // 玩家所在的列 }PlayerPos; // 把道具定义为枚举类型 // 墙0,地板1,箱子目的地2,小人3,箱子4,箱子命中目标5 // 使用枚举优化程序可读性 enum _PROPS { WALL, // 0 墙 FLOOR, // 1 地板 BOX_DES, // 2 箱子目的地 MAN, // 3 小人 BOX, // 4 箱子 HIT, // 5 箱子命中目标 ALL // 道具总共有多少种 }; // 游戏方向的控制 enum _DIRECTION { UP, DOWN, LEFT, RIGHT }; // 地图里面的道具分别是什么的数组 比如0,0 是 WALL 墙壁 // 墙0,地板1,箱子目的地2,小人3,箱子4,箱子命中目标5 int map[ROW][COLUMN] = { // 9行12列 { WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL }, { WALL, FLOOR, WALL, FLOOR, FLOOR, FLOOR, FLOOR, FLOOR, FLOOR, FLOOR, WALL, WALL }, { WALL, FLOOR, BOX, FLOOR, WALL, BOX_DES, FLOOR, WALL, BOX_DES, FLOOR, WALL, WALL }, { WALL, FLOOR, WALL, FLOOR, WALL, FLOOR, WALL, WALL, FLOOR, FLOOR, FLOOR, WALL }, { WALL, FLOOR, WALL, BOX_DES, WALL, FLOOR, FLOOR, BOX, FLOOR, FLOOR, FLOOR, WALL }, { WALL, FLOOR, FLOOR, FLOOR, FLOOR, MAN, FLOOR, FLOOR, FLOOR, BOX, FLOOR, WALL }, { WALL, FLOOR, BOX_DES, FLOOR, FLOOR, BOX, FLOOR, FLOOR, FLOOR, FLOOR, FLOOR, WALL }, { WALL, FLOOR, WALL, WALL, FLOOR, WALL, FLOOR, FLOOR, WALL, WALL, FLOOR, WALL }, { WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL } }; // 道具的数组 ALL总共有多少种道具 IMAGE images[ALL]; // 定义玩家位置 PlayerPos man; // =============函数体的定义处============= void changeMap(int row, int column, enum _PROPS prop){ map[row][column] = prop; // 把地图里面对应坐标道具修改为我们要修改的道具 方便后面看看地图里面的箱子是不是已经都到目的地了 // 更新地图道具位置 通过偏移量 和 行列 计算出要放的位置 putimage(START_X+column* BMP_SIZE,START_Y+row*BMP_SIZE,&images[map[row][column]]); // 如果修改的是玩家的位置 更新玩家的坐标 if(prop == MAN){ // 每次改完玩家的位置 都要把玩家的位置更新为最新的位置 以便于下次按键的时候能获取到玩家的最新位置 man.x = row; man.y = column; } } void gameControl(enum _DIRECTION direction){ // 每次按键获取玩家的最新位置 int x = man.x; // 人所在的行 int y = man.y; // 人所在的列 // 保存即将要移动的下一个位置 PlayerPos next_pos = man; // 刚开始的位置就是玩家现在所在的位置 PlayerPos next_next_pos = man; // 箱子前面那个位置 就是人的前面是箱子,箱子的前面是什么,就是人的前面两个格子 switch (direction) { // 根据要移动的方向去修改即将要移动的位置 case UP: next_pos.x--; // 往上一行 next_next_pos.x -= 2; // 往前两行 break; case DOWN: next_pos.x++; // 往下一行 next_next_pos.x += 2; // 往下两行 break; case LEFT: next_pos.y--; // 往左一列 next_next_pos.y -= 2; // 往左两列 break; case RIGHT: next_pos.y++; // 往右一列 next_next_pos.y += 2; // 往右两列 break; } // ====================== 边界地板方案1 升级升级版 ======================= if (isValid(next_pos) && map[next_pos.x][next_pos.y] == FLOOR) { // 如果下一步不会越界边界,并且下一步要移动的地方是一个地板 changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 changeMap(next_pos.x, next_pos.y, MAN); // 把下一步要移动的位置变为人 }// ====================== 人推箱子 ====================== else if(isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX){ // 如果下一步不会越界边界,并且下一步要移动的地方是一个箱子 // 并且箱子前面是地板或者箱子目的地 if(isValid(next_next_pos) && map[next_next_pos.x][next_next_pos.y] == FLOOR){ // 如果下一步的下一步不会越界边界 并且箱子的下一个位置是地板 那么人就可以带着箱子前进 changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 changeMap(next_pos.x, next_pos.y, MAN); // 把下一步要移动的位置变为人 也就是说把原本的箱子的位置变成人 changeMap(next_next_pos.x, next_next_pos.y, BOX); // 把下一步的下一步要移动的位置变成箱子 也就是说把原本箱子的下一个变成箱子的现在所在处 }else if(isValid(next_next_pos) && map[next_next_pos.x][next_next_pos.y] == BOX_DES) { // 如果下一步的下一步不会越界边界 并且箱子的下一个位置是箱子目的地 那么人就可以带着箱子前进 // 并且还要修改状态为5 就是箱子已经抵达目的地 HIT 不同的点 就是一个改为箱子 一个 改为箱子目的地 changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 changeMap(next_pos.x, next_pos.y, MAN); // 把下一步要移动的位置变为人 也就是说把原本的箱子的位置变成人 changeMap(next_next_pos.x, next_next_pos.y, HIT); // 把下一步的下一步要移动的位置变成箱子 也就是说把原本箱子的下一个变成箱子的现在所在处 // 改为了箱子目的地之后,箱子就再也推动不了了,因为我们最外面只处理了 人前面是地板和箱子的状况,并没有处理人前面是箱子目的地的状况 } } // ====================== 边界地板方案1 升级版 ======================= // ====================== 边界地板方案1 ======================= //switch (direction) //{ // case UP:// w 向上 那么要判断上面能不能走 // // if((x - 1) >= 0 && map[x-1][y] == FLOOR){ // // 如果人在的位置 - 1 大于等于0 就证明 往上走不会超过边界,并且上面还要是一块地板 我们才能向上走 // changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 // changeMap(x-1,y,MAN); // 把上一行这一列的位置修改为玩家 // } // break; // case DOWN:// s 向下 那么要判断下面能不能走 // // if((x+1) <= (ROW-1) && map[x+1][y] == FLOOR){ // // 如果人在的位置 + 1 小于等于等于最下面那一行 就证明 往下走不会超过边界,并且下面还要是一块地板 我们才能向下走 // changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 // changeMap(x+1,y,MAN); // 把下一行这一列的位置修改为玩家 // } // break; // case LEFT:// a 向左 那么要判断左面能不能走 // // if ((y - 1) >= 0 && map[x][y-1] == FLOOR) { // // 如果人在的位置 - 1 大于等于0 就证明 往左走不会超过边界,并且左面还要是一块地板 我们才能向左走 // changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 // changeMap(x, y - 1, MAN); // 把这一行,往左一列的位置修改为玩家 // } // break; // case RIGHT:// d 向右 那么要判断右面能不能走 // // if ((y + 1) <= (COLUMN - 1) && map[x][y + 1] == FLOOR) { // // 如果人在的位置 + 1 小于等于最右边那行 就证明 往右走不会超过边界,并且右面还要是一块地板 我们才能向右走 // changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板 // changeMap(x, y + 1, MAN); // 把这一行,往右一列的位置修改为玩家 // } // break; //} } bool isGameOver(){ int desCount = 0; // 遍历地图道具 for (int i = 0; i < ROW; i++) { for (int j = 0; j < COLUMN; j++) { if(map[i][j] == BOX_DES){ // 如果还存在箱子目的地 还有箱子没命中 desCount++; } } } return desCount == 0; // 如果是0个那么就是true 如果不是0个那么就是false } void gameOver(IMAGE *bg){ putimage(0,0,bg); // 喷涂背景图片 settextcolor(WHITE); // 设置要显示的字体颜色 RECT rec = {0,0,WINDOW_WIDTH,WINDOW_HEIGHT}; // 定义一个矩形 左上角 和右下角的坐标 settextstyle(20,0,_T("宋体")); // 设置文字样式 // 绘制文字 以上面的矩形为参照物 DT_CENTER 水平居中 DT_VCENTER 垂直居中 DT_SINGLELINE 文字显示单行 drawtext(_T("恭喜您~n您终于成为了一个合格的搬箱子老司机"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } using namespace std; // 推箱子游戏程序的主入口 int main(void) { // 背景图片 IMAGE bg_img; // 初始化窗口 宽度高度 flag 绘图环境的样式,默认为 NULL initgraph(WINDOW_WIDTH, WINDOW_HEIGHT); // 加载背景图片 到bg_img去 图片的宽度和高度 为0则原本的宽高 是否拉伸图片 loadimage(&bg_img, _T("blackground.bmp"), WINDOW_WIDTH, WINDOW_HEIGHT, BRESIZE_TRUE); // 显示图片 从0 0点开始绘制 putimage(0,0, &bg_img); // 加载地图道具 wall_right 墙壁 floor 地板 des箱子目的地 man人 box箱子 箱子命中目标也显示箱子 loadimage(&images[WALL],_T("wall_right.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); loadimage(&images[FLOOR],_T("floor.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); loadimage(&images[BOX_DES],_T("des.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); loadimage(&images[MAN],_T("man.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); loadimage(&images[BOX],_T("box.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); loadimage(&images[HIT],_T("box.bmp"), BMP_SIZE, BMP_SIZE, BRESIZE_TRUE); // 起始位置的x y int start_x = START_X; int start_y = START_Y; // 绘制地图道具 遍历二维数组 显示对应的道具 for(int i = 0; i < ROW; i++){ for (int j = 0; j < COLUMN; j++) { // 如果要显示的道具是玩家,就把玩家的位置给保存起来 MAN == MAN if(map[i][j] == MAN){ // 玩家的代号是3 MAN 3 man.x = i; // 行 man.y = j; // 列 // 这样我们就知道玩家在几行几列的位置了 } // 要显示的道具是什么 &images[WALL] 等 我们就可以拿到对应的图片 然后放在对应的位置 putimage(start_x, start_y, &images[map[i][j]]); // 内层循环移动 x 的位置 每次移动一张图片的宽度 start_x += BMP_SIZE; } // 下一行 x 要恢复到最前面的位置 再往后移动 start_x = START_X; // 一行结束 下一行 y 的位置要向下走一个图片的高度 start_y += BMP_SIZE; } // =================================地图初始化完毕以后的操作================================= // 游戏环节 获取键盘热键 使用easy_x bool isQuit = false; // 游戏是否退出 do{ // 获取热键 // 判断键盘是否有敲击 因为键盘不一定有使用 if(_kbhit()){ // 玩家敲击了按键 看看按了什么键 char ch = _getch(); switch (ch) { case KEY_UP: // w 向上 那么要判断上面能不能走 gameControl(UP); break; case KEY_DOWN: // s 向下 那么要判断下面能不能走 gameControl(DOWN); break; case KEY_LEFT: // a 向左 那么要判断左边能不能走 gameControl(LEFT); break; case KEY_RIGHT: // d 向右 那么要判断右边能不能走 gameControl(RIGHT); break; case KEY_Quit: // q 退出游戏 isQuit = true; break; // 其它按键不需要做处理 } // 处理完按键的操作之后,我们判断游戏是否已经结束了 很简单 我们就循环 然后判断 des是不是全部消失了,因为到达之后会变成hit if (isGameOver()) { // 如果游戏结束,游戏没结束就不用做别的处理 gameOver(&bg_img); // 游戏结束 isQuit = true; // 退出循环 不再让用户对游戏进行操作 } }else{ Sleep(100); // 如果玩家没有按键 那么休眠0.1s 再去做下一次的判断 避免 浪费cpu } } while (!isQuit); // 如果isQuit 为true了 !true 就是false 就退出游戏 // 游戏结束 释放绘图窗口 system("pause"); // 防止关闭屏幕太快 closegraph(); return 0; }



