IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c++推箱子游戏 -> 正文阅读

[C++知识库]c++推箱子游戏

#include <iostream>  // 引入标准输入输出
#include <Windows.h> // 引入window程序相关
#include <graphics.h> // 引入easy_x图形库
#include <conio.h> // 获取热键需要的头文件
#include <string> // 引入字符串
#include <stdlib.h> // 引入常用的函数
#include <time.h> // 引入时间函数

// =============函数原型的声明=============
/*
	实现玩家的方向控制
	@param direction 输入的方向
*/
void gameControl(enum _DIRECTION direction);
/*
	改变地图道具
	@param row 需要改变道具的行
	@param column 需要改变道具的列
	@param prop 要改变为什么道具
*/
void changeMap(int row,int column, enum _PROPS prop);
/*
	判断游戏是否结束
*/
bool isGameOver();
/*
	游戏结束场景
	@param bg 指定游戏结束之后要显示的背景图
*/
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;

// =============函数体的定义处=============
/*
	改变地图道具
	@param row 需要改变道具的行
	@param column 需要改变道具的列
	@param prop 要改变为什么道具
*/
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;
	}
}
/*
	实现玩家的方向控制
	@param direction 输入的方向
*/
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 升级版 =======================
	/*if (next_pos.x >= 0 && next_pos.x < ROW && next_pos.y >=0 && next_pos.y < COLUMN && map[next_pos.x][next_pos.y] == FLOOR) {
		// 如果下一步不会越界边界,并且下一步要移动的地方的一个地板
		changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板
		changeMap(next_pos.x, next_pos.y, MAN); // 把下一步要移动的位置变为人
	}*/

	// ====================== 边界地板方案1 =======================
	//switch (direction)
	//{
	//	case UP:// w 向上 那么要判断上面能不能走
	//		/*
	//			1.处理前进方向是地板的情况
	//			 先判断人的上边能不能走 是不是空地 如果能走 人要往上边走 然后地板要往下边走 并且还要判断是否越界超出了地图
	//		*/ 
	//		if((x - 1) >= 0 && map[x-1][y] == FLOOR){
	//			// 如果人在的位置 - 1 大于等于0 就证明 往上走不会超过边界,并且上面还要是一块地板 我们才能向上走
	//			changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板
	//			changeMap(x-1,y,MAN); // 把上一行这一列的位置修改为玩家
	//		}
	//		break;
	//	case DOWN:// s 向下 那么要判断下面能不能走
	//		/*
	//			1.处理前进方向是地板的情况
	//			 先判断人的下边能不能走 是不是空地 如果能走 人要往下边走 然后地板要往上边走 并且还要判断是否越界超出了地图
	//		*/
	//		if((x+1) <= (ROW-1) && map[x+1][y] == FLOOR){
	//			// 如果人在的位置 + 1 小于等于等于最下面那一行 就证明 往下走不会超过边界,并且下面还要是一块地板 我们才能向下走				
	//			changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板
	//			changeMap(x+1,y,MAN); // 把下一行这一列的位置修改为玩家
 //			}
	//		break;
	//	case LEFT:// a 向左 那么要判断左面能不能走
	//		/*
	//			1.处理前进方向是地板的情况
	//			 先判断人的左边能不能走 是不是空地 如果能走 人要往左边走 然后地板要往右边走 并且还要判断是否越界超出了地图
	//		*/
	//		if ((y - 1) >= 0 && map[x][y-1] == FLOOR) {
	//			// 如果人在的位置 - 1 大于等于0 就证明 往左走不会超过边界,并且左面还要是一块地板 我们才能向左走
	//			changeMap(x, y, FLOOR); // 把当前玩家所在的位置修改为地板
	//			changeMap(x, y - 1, MAN); // 把这一行,往左一列的位置修改为玩家
	//		}
	//		break;
	//	case RIGHT:// d 向右 那么要判断右面能不能走
	//		/*
	//			1.处理前进方向是地板的情况
	//			 先判断人的右边能不能走 是不是空地 如果能走 人要往右边走 然后地板要往左边走 并且还要判断是否越界超出了地图
	//		*/
	//		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
}
/*
	游戏结束场景
	@param bg 指定游戏结束之后要显示的背景图
*/
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;
}

image-20211007201335947

image-20211007201356403

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-08 11:36:46  更:2021-10-08 11:38:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 1:53:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码