推箱子游戏

486 阅读2分钟

推箱子游戏

第1节 项目需求

实现一款推箱子游戏,效果如下图所示,具体规则:

  1. 箱子只能推动而不能拉动;
    2.如果箱子前一格是地板或箱子目的地,则可以推动一个箱子往前走一格,如果箱子已经在 箱子目的地则不能再推动;
    3.推箱子的小人不能从箱子目的地上直接穿过;
    4.注意不要把箱子推到死角上,不然就无法再推动它了;
    5.所有箱子都成功推到箱子目的地,游戏结束,过关成功!

image.png

## 第 2 节 项目实现

image.png 2.1.1 地图初始化 戏台坐标系(650 x 650):

image.png 道具表示: 墙: 0,地板: 1,箱子目的地: 2, 小人: 3, 箱子: 4, 箱子命中目标: 5

编码实现:

#include<stdlib.h>
#include<iostream>
#include<string>
#include<conio.h> //热键

using namespace std;
//#define是宏定义,只是简单的字符替换,无类型检查,不安全,不分配内存,无类型检查
#define line 9
#define column 12
#define SCREEN_WIDTH 960
#define SCREEN_HEIGHT 760
#define RATIO 61 //箱子左到右的距离
#define START_X 50
#define START_Y 100

//控制键 上下左右控制方向,q退出
#define KEY_UP    'w'
#define KEY_DOWN  's'
#define KEY_LEFT  'a'
#define KEY_RIGHT 'd'
#define KEY_QUIT  'q'
#define isValid(pos) pos.x>=0 && pos.x<line && pos.y >=0&& pos.y<column


//typedef可以声明各种类型名,在静态存储区中分配空间
typedef enum _DIRECTION  DIRECTION;
typedef enum _PROPS      PROPS;
typedef struct _POS     POS;
//游戏控制方向
enum _DIRECTION{
	UP,
	DOWN,
	LEFT,
	RIGHT
};
//使用枚举,参数默认值从0开始,逐加
enum _PROPS{
	WALL,
	FLOOR,
	BOX_DES,
	MAN,
	BOX,
	HIT,
	ALL
};
/*结构体,就是程序员自定义的一种“数据类型”
是使用多个基本数据类型、或者其他结构,组合而成的一种新的“数据类型”*/
struct _POS{
	int x;//小人所在位置的行
	int y;//小人所在位置的列
};
POS man;//struct _POS的变量
IMAGE images[ALL]; //使用的图片有6个


//游戏地图
int map[line][column]={
	{0,0,0,0,0,0,0,0,0,0,0,0},
	{0,1,0,1,1,1,1,1,1,1,0,0},
	{0,1,4,1,0,2,1,0,2,1,0,0},
	{0,1,0,1,0,1,0,0,1,1,1,0},
	{0,1,0,2,0,1,1,4,1,1,1,0},
	{0,1,1,1,0,3,1,1,1,4,1,0},
	{0,1,2,1,1,4,1,1,1,1,1,0},
	{0,1,0,0,1,0,1,1,0,0,1,0},
	{0,0,0,0,0,0,0,0,0,0,0,0},
};

bool isGameOver(){
	for(int i=0;i<line;i++){
		for(int j=0;j<column;j++){
			if(map[i][j]==BOX_DES) return false;
		}
	}
	return true;
}

void gameOverScene(IMAGE *bg){
	putimage(0,0,bg);
	settextcolor(RED);
	RECT rec={0,0,SCREEN_WIDTH,SCREEN_HEIGHT};
	settextstyle(20,0,_T("宋体"));
	drawtext(_T("恭喜您~\n 您终于成功了!"),&rec,DT_CENTER | DT_VCENTER | DT_SINGLELINE);

}
/****************************************************
*改变游戏地图视图中一格对应道具并重新显示
*/
//人的下一个位置的地址指向结构体的指针变量 pos,将道具传递过来
void changeMap(POS *pos,PROPS prop){
	//下一个位置的道具变成了人
	map[pos->x][pos->y]=prop;
	putimage(START_X +pos->y*RATIO,START_Y +pos->x*RATIO ,&images[prop]);
}
/******************************************************
*实现游戏四个方向(上、下、左、右)的控制
*输入:
*direct -人前进方向
*输出:无
******************************************************/
void gameControl(DIRECTION direct){ //enum _DIRECTION与DIRECTION一样 ,传递动作direct
	POS next_pos=man;  //struct _POS就是POS,这里相当于让struct _POS的变量next_pos和next_pos=man,共同使用结构体struct _POS
	POS next_next_pos=man;
	switch(direct){
	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;
	}
	//下面是判断能不能走
	//宏展开 next_pos.x>=0&&next_pos.x<line && next_pos.y >=0&& next_pos.y<column
	//先判断人有没有越界并且判断所在位置是不是地板
	if(isValid(next_pos)&& map[next_pos.x][next_pos.y]==FLOOR){ 
		//人的前方是地板
			changeMap(&next_pos,MAN);//小人前进一格
			changeMap(&man,FLOOR);//人的位置变成了地板
			man=next_pos;//人的位置现在变成了前进一格的位置
		//如果人的前方是箱子,要求箱子的前方不能越界
	} else if(isValid(next_next_pos)&& map[next_pos.x][next_pos.y]==BOX){
		//人的前方是箱子
		//2种情况,箱子前面是地板或者是箱子目的地
		if(map[next_next_pos.x][next_next_pos.y]==FLOOR){
			changeMap(&next_next_pos,BOX);//箱子往前
			changeMap(&next_pos,MAN);//小人前进一格
			changeMap(&man,FLOOR);
			man=next_pos;
		}else if(map[next_next_pos.x][next_next_pos.y]==BOX_DES){
			changeMap(&next_next_pos,HIT);
			changeMap(&next_pos,MAN);//小人前进一格
			changeMap(&man,FLOOR);
			man=next_pos;
		}
	
	
}
}


int main(){
	IMAGE bg_img; //命名背景图片

	//创建下项目大小及背景
	initgraph(SCREEN_WIDTH,SCREEN_HEIGHT);  //命令框大小
	loadimage(&bg_img,_T("blackground.bmp"),SCREEN_WIDTH,SCREEN_HEIGHT,true); //将背景图片装载到这个项目中
	putimage(0,0,&bg_img);//输出图片

	//加载道具图标
	loadimage(&images[WALL],_T("wall_right.bmp"),RATIO,RATIO,true); //用枚举将0变成WALL,这样以后要该值就方便了,比例为61
	loadimage(&images[FLOOR],_T("floor.bmp"),RATIO,RATIO,true);//地板
	loadimage(&images[BOX_DES],_T("des.bmp"),RATIO,RATIO,true);//箱子目的地
	loadimage(&images[MAN],_T("man.bmp"),RATIO,RATIO,true);    //人
	loadimage(&images[BOX],_T("box.bmp"),RATIO,RATIO,true);    //箱子
	loadimage(&images[HIT],_T("box.bmp"),RATIO,RATIO,true);    //箱子

	for(int i=0;i<line;i++){
		for(int j=0;j<column;j++){
			if(map[i][j]==3){//如果地图的行和列在的位置就是小人,找到小人的位置了
				man.x=i;
				man.y=j;
			}
			putimage(START_X +j*RATIO,START_Y +i*RATIO ,&images[map[i][j]]);//图片放置的位置加上图片范围
		}
	}
	//游戏环节
	bool quit =false;
	do{
		if(_kbhit()){//玩家有按键
		char ch=getch();

		if(ch==KEY_UP){
			gameControl(UP);
		}else if(ch==KEY_DOWN){
			gameControl(DOWN);
		}else if(ch==KEY_LEFT){
			gameControl(LEFT);
		}else if(ch==KEY_RIGHT){
			gameControl(RIGHT);
		}else if(ch==KEY_QUIT){
			quit=true;
		}
		if(isGameOver()){
			gameOverScene(&bg_img);
			quit=true;
		}
	}
	Sleep(100);//注意要大写S
	}while(!quit);

	
	system("pause");
	return 0;
	

}

第3节.需要用到的素材

注意:将这些图片要放在同一个文件夹里面,引用的时候才不会报错 blackground.bmp

box.bmp

des.bmp

floor.bmp

man.bmp

wall_right.bmp

[## 第4节.学到新知识]

define和typedef的区别
define
1.只是简单的字符串替换,没有类型检查
2.是在编译的预处理阶段起作用
3.可以用来防止头文件的重复引用
4.不分配内存
typedef
1.有对应的数据类型,是要进行判断的
2.是在编译、运行的时候起作用
3.在静态存储区中分配空间,在程序运行过程中只有一个内存拷贝