推箱子游戏
第1节 项目需求
实现一款推箱子游戏,效果如下图所示,具体规则:
- 箱子只能推动而不能拉动;
2.如果箱子前一格是地板或箱子目的地,则可以推动一个箱子往前走一格,如果箱子已经在 箱子目的地则不能再推动;
3.推箱子的小人不能从箱子目的地上直接穿过;
4.注意不要把箱子推到死角上,不然就无法再推动它了;
5.所有箱子都成功推到箱子目的地,游戏结束,过关成功!
## 第 2 节 项目实现
2.1.1 地图初始化
戏台坐标系(650 x 650):
道具表示:
墙: 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节.需要用到的素材
注意:将这些图片要放在同一个文件夹里面,引用的时候才不会报错
[## 第4节.学到新知识]
define和typedef的区别
define
1.只是简单的字符串替换,没有类型检查
2.是在编译的预处理阶段起作用
3.可以用来防止头文件的重复引用
4.不分配内存
typedef
1.有对应的数据类型,是要进行判断的
2.是在编译、运行的时候起作用
3.在静态存储区中分配空间,在程序运行过程中只有一个内存拷贝