推箱子小游戏自制100关,有兴趣可以试一试。(头文件和源文件分开)游戏截图已发。

 因为地图是从文件中读取,所以关卡地图发成了资源。

 key.cpp

#include <conio.h>
#include "pushBox.h"

//键盘操作(改变核心数据)
void key(){
	unsigned char key = 0;

	key = _getch();//调用getch方法,获取按下的键盘某个键的键值;此方法为阻塞方法,会卡在这里,直到用户按下了某个按键

	switch (key){
	case 'A':
	case 'a'://a,左
		{
	 direction=3;
	 manMove();
	  Direction[_key]=direction;
	 _key++;
		}
		break;
	case 'D':
	case 'd'://d,右
		{
	 direction=4;
	 manMove();
	  Direction[_key]=direction;
	 _key++;
		}
		break;
	case 'W':
	case 'w'://w,上
		{
     direction=1;
	 manMove();
	  Direction[_key]=direction;
	 _key++;
		}
		break;
	case 'S':
	case 's'://s,下
		{
	 direction=2;
	 manMove();
	  Direction[_key]=direction;
	 _key++;
		}
		break;
	case 'N':
	case 'n':
		//按下N键,直接跳关到下一关
		nextLevel();
		break;
	case 'B':
	case 'b':
		//按下B键,直接跳关到上一关
		beforeLevel();
		break;
	case 'C':
	case 'c':
		//按下C键,返回上一步
		goback();
		break;
			break;
	case 'R':
	case 'r':
		//按下R键,重新开始
		restart();
		break;
	case 'G':
	case 'g':
		//按下G键,选择关卡
		xuanguan();
		break;
	}
}

 main.cpp

#include <conio.h>
#include "paint.h"
#include "key.h"
#include "pushBox.h"


int main(){

	//load_level_1();//加载第一关地图
	loadMapFromFile(1);//加载第一关地图
	
	initPaint();
	startDurationTimer();//开启游戏主窗口之后,打开定时器
	while(1){
		paintAll();
		key();
	}

	return 0;
}

paint.cpp

#include <stdio.h>
#include "EasyXPng.h"
#include "pushBox.h"
#include "paint.h"

//1.定义图片对象
IMAGE pic_back;
IMAGE_PNG pic_man;
IMAGE pic_outer_wall;
IMAGE pic_inner_wall;
IMAGE pic_ball;
IMAGE pic_box;
IMAGE pic_box_on_ball;

int pikaquCount = 0;//当前该绘制哪一张皮卡丘图片
IMAGE_PNG pikaqu[16];//存放皮卡丘的16张动画图片

HANDLE hMutex;

//加载所有图片
void loadAllImages(){
	//2.加载图片文件到图片对象
	loadimage(&pic_back, "imgs/back.jpg", WINDOW_WIDTH, WINDOW_HEIGHT);
	loadimage(&pic_outer_wall, "imgs/wall_outside.bmp", GRID_SIZE, GRID_SIZE);
	loadimage(&pic_inner_wall, "imgs/wall_inside.bmp", GRID_SIZE, GRID_SIZE);
	loadimage(&pic_ball, "imgs/ball.bmp", GRID_SIZE, GRID_SIZE);
	loadimage(&pic_box, "imgs/box.bmp", GRID_SIZE, GRID_SIZE);
	loadimage(&pic_box_on_ball, "imgs/box_pushed.bmp", GRID_SIZE, GRID_SIZE);
	
	//加载17张皮卡丘的动画图片

	loadimage(&pikaqu[0], "imgs/pikaqu/pi01.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[1], "imgs/pikaqu/pi02.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[2], "imgs/pikaqu/pi03.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[3], "imgs/pikaqu/pi04.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[4], "imgs/pikaqu/pi05.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[5], "imgs/pikaqu/pi06.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[6], "imgs/pikaqu/pi07.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[7], "imgs/pikaqu/pi08.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[8], "imgs/pikaqu/pi09.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[9], "imgs/pikaqu/pi10.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[10], "imgs/pikaqu/pi01.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[11], "imgs/pikaqu/pi02.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[12], "imgs/pikaqu/pi03.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[13], "imgs/pikaqu/pi04.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[14], "imgs/pikaqu/pi05.png", GRID_SIZE, GRID_SIZE);
	loadimage(&pikaqu[15], "imgs/pikaqu/pi06.png", GRID_SIZE, GRID_SIZE);

}


//加载所有图片资源,并打开主窗口
void initPaint(){
	hMutex = CreateMutex(NULL,FALSE,NULL);
	loadAllImages();

	//打开主窗口,指定窗口宽和高
	initgraphEx(WINDOW_WIDTH, WINDOW_HEIGHT);
}


//根据核心数据,绘制界面所有元素
void paintAll(){

	WaitForSingleObject(hMutex,INFINITE);
	//利用双缓冲技术,把图像绘制到双缓冲中,加速图像绘制、显示的过程
	BeginBatchDrawEx();//开始批量绘图,此刻起,绘制的所有内容,都是绘制在内存中(缓冲区)的,显示器无法看到

	//3.把加载完毕的图片对象,贴到窗口上
	putimage(0, 0, &pic_back);//绘制背景

	//绘制人物所在游戏范围
	setlinecolor(YELLOW);
	setlinestyle(PS_SOLID, 3);
	rectangle(BEGIN_X, BEGIN_Y, BEGIN_X + GRID_N * GRID_SIZE, BEGIN_Y + GRID_N * GRID_SIZE);

	setlinecolor(RED);
	setlinestyle(PS_SOLID, 3);
	rectangle(5, 90,280,500);

	//根据核心地图数据,遍历它,把每一格元素,根据下标计算出它的像素位置,并绘制到界面上
	for(int k = 0; k < 3; k++){
		for(int x = 0; x < GRID_N; x++){
			for(int y = 0; y < GRID_N; y++){
				switch(map[k][x][y]){
				case OUTERE_WALL:
					putimage(BEGIN_X + x * GRID_SIZE, BEGIN_Y + y * GRID_SIZE, &pic_outer_wall);
					break;
				case INNER_WALL:
					putimage(BEGIN_X + x * GRID_SIZE, BEGIN_Y + y * GRID_SIZE, &pic_inner_wall);
					break;
				case BALL:
					putimage(BEGIN_X + x * GRID_SIZE, BEGIN_Y + y * GRID_SIZE, &pic_ball);
					break;
				case BOX:
					if(map[1][x][y] == BALL){
						putimage(BEGIN_X + x * GRID_SIZE, BEGIN_Y + y * GRID_SIZE, &pic_box_on_ball);
					}else{
						putimage(BEGIN_X + x * GRID_SIZE, BEGIN_Y + y * GRID_SIZE, &pic_box);
					}
					break;
				}
			}
		}
	}
		//putimage(BEGIN_X + manX * GRID_SIZE, BEGIN_Y + manY * GRID_SIZE, &pic_man);//绘制人物图片
	//绘制当前某一帧的人物图像(根据定时器的推动,自行计算出,该绘制哪一副皮卡丘图片)
	putimage(BEGIN_X + manX * GRID_SIZE, BEGIN_Y + manY * GRID_SIZE, &pikaqu[pikaquCount]);



	//绘制游戏选项
	setbkmode(TRANSPARENT);//设定文字绘制的背景为透明
	settextstyle(20, 0, "宋体");//设置文字大小和字体
	settextcolor(YELLOW);//设置文字颜色

	char buffer[1024];
	sprintf(buffer, "当前关卡:第%d关", level);
	char steps[1024];
	sprintf(steps,"步数:%d步",step);
	outtextxy(10, 100, buffer);//在界面上显示当前关卡数
	outtextxy(10, 200, "上一关:B");
	outtextxy(10, 250, "下一关:N");
	outtextxy(10, 300,steps);
	outtextxy(10, 350, "返回上一步:C");
	outtextxy(10, 400, "重新开始:R");
	outtextxy(10, 450, "选关:G:输入关卡+回车");
	//绘制当前关卡经过的时长
	sprintf(buffer, "经过时长:%d秒", duration / 10);
	outtextxy(10, 150, buffer);

	EndBatchDrawEx();//结束批量绘图

	ReleaseMutex(hMutex);
}

pushBox.cpp

#include <stdio.h>
#include "EasyXPng.h"
#include "paint.h"
#include "pushBox.h"

//定义人的坐标(格子数)
int manX = 0;
int manY = 0;

//定义地图,存放界面中所有格子元素
//第0层存放内墙
//第1层存放小球
//第2层存放:外墙、箱子、人
int map[3][GRID_N][GRID_N] = {0};

//当前第几关
int level = 1;
int step=0;
int _key=1;
int direction=0;
int Direction[1024]={0};

//当前关卡已度过的时长,单位为0.1秒
int duration = 0;

//此方法为定时器自动调用的回调方法,此方法会被每隔0.1秒钟调用一次,我们在此,对当前关卡时长总数进行加1
VOID CALLBACK currentTimer(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
	duration++;//当前关卡时长总数进行加1

	//同时,让皮卡丘动画的当前图像下标,加1,推动动画
	pikaquCount++;
	if(pikaquCount >= 16){
		pikaquCount = 0;
	}

	paintAll();//重新绘制界面所有内容
}

//调用此方法,开启一个间隔时间为0.1秒(100毫秒)的定时器
void startDurationTimer()
{
	KillTimer(GetHWnd(), 1);//杀掉ID为1的定时器
	SetTimer(GetHWnd(), 1, 150, currentTimer);//开启一个ID为1的定时器,时间间隔为0.1秒
}

//调用此方法,给定关卡数字,内部会从关卡文件夹中加载对应的地图文件(比如:100.txt)
void loadMapFromFile(int _level){
	FILE * fp;//文件指针,指向一个已经打开的文件

	char buffer[1024];//定义字符串缓存,用于存放拼接好的字符串内容

	sprintf(buffer, "levels/%d.txt", _level);//根据格式化,把当前关卡数值,拼接到文件路径字符串中

	fp = fopen(buffer, "r");//打开指定的地图文件

	int element;//临时存放从文件中读取出来的一块元素的值
	//比如:1为外墙、2为内墙、3为小球、4为箱子(下面没有小球)、5为人、6为在小球上的箱子

	for(int y  = 0; y < GRID_N; y++){
		for(int x = 0; x < GRID_N; x++){
			fscanf(fp, "%d", &element);//从fp指向的已打开文件中,读取一个数字,存放到element变量中(代表某种游戏元素)

			switch(element){
			case OUTERE_WALL:
				map[2][x][y] = OUTERE_WALL;
				break;
			case INNER_WALL:
				map[0][x][y] = INNER_WALL;
				break;
			case BALL:
				map[1][x][y] = BALL;
				map[0][x][y] = INNER_WALL;
				break;
			case BOX:
				map[2][x][y] = BOX;
				map[0][x][y] = INNER_WALL;
				break;
			case MAN:
				manX = x;
				manY = y;
				map[0][x][y] = INNER_WALL;
				break;
			case BOX_ON_BALL:
				map[2][x][y] = BOX;
				map[1][x][y] = BALL;
				map[0][x][y] = INNER_WALL;
				break;
			}
		}
	}

	fclose(fp);//文件打开用完后,记得关闭
}
void xuanguan()
{
	scanf("%d",&level);
	level--;
	nextLevel();
}

//此方法,负责判断输赢:1:胜利;0:未胜利
int isWin(){
	for(int x  = 0; x < GRID_N; x++){
		for(int y = 0; y < GRID_N; y++){
			if(map[2][x][y] == BOX && map[1][x][y] != BALL){
				return 0;
			}
		}
	}
	_key=1;
	step=0;
	return  1;
}

//清空地图
void clearMap(){
	for(int k = 0; k < 3; k++){
		for(int x = 0; x < GRID_N; x++){
			for(int y = 0; y < GRID_N; y++){
				map[k][x][y] = EMPTY;
			}
		}
	}
	_key=1;
	Direction[0]=0;
	step=0;
}

//此方法,负责加载下一关地图(根据当前第几关)
void nextLevel(){
	if(level == 100){
		level = 1;
	}else
	{
		step=0;
		_key=1;
       level++;
	   }
	
	clearMap();//在加载新地图前,清空当前地图数据

	loadMapFromFile(level);//根据当前关卡,加载对应地图文件
}

//此方法,负责加载上一关地图(根据当前第几关)
void beforeLevel(){
	if(level > 1){
		step=0;
		_key=1;
		level--;
	}else{
		return;
	}

	clearMap();//在加载新地图前,清空当前地图数据

	loadMapFromFile(level);//根据当前关卡,加载对应地图文件
}
void restart(){//重玩函数
	clearMap();
loadMapFromFile(level);
}


//调用此方法,检查是否产生输赢,若胜利则加载下一关地图!
int checkAndPass(){
	if(isWin() == 1){
		nextLevel();
		return 1;
	}

	return 0;
}
void goback(){
	if(_key>0){
	_key--;
	direction=Direction[_key];
	switch(direction){
	case 0:break;
	case 10:{
	map[2][manX][manY] = BOX;
	map[2][manX][manY - 1] = EMPTY;
	manY++;
	step--;
		   }
	break;
	case 20:{
	map[2][manX][manY] = BOX;
	map[2][manX][manY + 1] = EMPTY;
	manY--;
	step--;
		   }
	break;
	case 30:{
	map[2][manX][manY] = BOX;
	map[2][manX-1][manY] = EMPTY;
	manX++;
	step--;
		   }
	break;
	case 40:{
	map[2][manX][manY] = BOX;
	map[2][manX+1][manY] = EMPTY;
	manX--;
	step--;
		   }
	break;
	case 1:{
	manY++;
	step--;
		   }
	break;
	case 2:{
	manY--;
	step--;
		   }
	break;
	case 3:{
	manX++;
	step--;
		   }
	break;
	case 4:{
	manX--;
	step--;
		   }
	break;
	}
	}
	else
		step=0;
		
}
	


//外部调用此方法,由核心业务模块,自行完成人物移动
//direction,方向:1:上、2:下、3:左、4:右
void manMove(){
	switch(direction){
	case 1://上
		if(map[2][manX][manY - 1] == OUTERE_WALL){
			direction=0;
			break;
		}

		if(map[2][manX][manY - 1] == BOX)
		{
			//上方是箱子,但还需判断箱子是否能被推动
			if(map[2][manX][manY - 2] == EMPTY)
			{
				//箱子上方没有东西,可以推动
				map[2][manX][manY - 2] = BOX;
				map[2][manX][manY - 1] = EMPTY;	
				direction=10;
				if(checkAndPass() == 1)
				{	direction=0;
					break;
				}	
			}
			else
			{
				direction=0;
				//箱子上方有东西,不能推动,且人也不能移动
				break;
			}
		}

		if(manY > 0)
		{
			manY--;
			step++;
		}
		break;
	case 2://下
		if(map[2][manX][manY + 1] == OUTERE_WALL){
			direction=0;
			break;
		}

		if(map[2][manX][manY + 1] == BOX){
			//下方是箱子,但还需判断箱子是否能被推动
			if(map[2][manX][manY + 2] == EMPTY){
				//箱子下方没有东西,可以推动
				map[2][manX][manY + 2] = BOX;
				map[2][manX][manY + 1] = EMPTY;
				if(checkAndPass() == 1){
						direction=0;
					break;
				}
				else 	direction=20;
			}else{
				direction=0;
				//箱子下方有东西,不能推动,且人也不能移动
				break;
			}
		}

		if(manY < GRID_N - 1){
			manY++;
			step++;
		}
		break;
	case 3://左
		if(map[2][manX  - 1][manY] == OUTERE_WALL){
			direction=0;
			break;//终止移动
		}

		if(map[2][manX  - 1][manY] == BOX){
			//左侧是箱子,但还需判断箱子是否能被推动
			if(map[2][manX - 2][manY] == EMPTY){
				//箱子左侧没有东西,可以推动
				map[2][manX - 2][manY] = BOX;
				map[2][manX  - 1][manY] = EMPTY;
				if(checkAndPass() == 1){
						direction=0;
					break;
				}
				else direction=30;
			}else{
					direction=0;
				//箱子左侧有东西,不能推动,且人也不能移动
				break;
			}
		}

		if(manX > 0){
			manX--;
	    	step++;
		}
		break;
	case 4://右
		if(map[2][manX + 1][manY] == OUTERE_WALL){
			direction=0;
			break;
		}

		if(map[2][manX  + 1][manY] == BOX){
			//右侧是箱子,但还需判断箱子是否能被推动
			if(map[2][manX + 2][manY] == EMPTY){
				//箱子右侧没有东西,可以推动
				map[2][manX + 2][manY] = BOX;
				map[2][manX + 1][manY] = EMPTY;
				if(checkAndPass() == 1){
				     	_key=1;
					 	direction=0;
					break;
				}
				else direction=40;
			}else{
				direction=0;
				//箱子左侧有东西,不能推动,且人也不能移动
				break;
			}
		}

		if(manX < GRID_N - 1){
			manX++;
			step++;
		}
		break;
	}
}

key.h

#ifndef KEY_H
#define KEY_H

void key();

#endif

paint.h

#ifndef PAINT_H
#define PAINT_H

#define WINDOW_WIDTH 1024 //窗口宽度,像素数
#define WINDOW_HEIGHT 768 //窗口高度,像素数
#define GRID_SIZE 40 //格子大小,像素数
#define BEGIN_X 300 //棋盘左侧偏移量,像素数
#define BEGIN_Y 50 //棋盘上方偏移量,像素数


extern int pikaquCount;//当前该绘制哪一张皮卡丘图片


void paintAll();
void initPaint();

#endif

pushBox.h

#ifndef PUSHBOX_H
#define PUSHBOX_H

#define GRID_N 16 //游戏场景范围的格子数



//定义游戏界面中格子中的元素类型
#define EMPTY 0 //空
#define OUTERE_WALL 1 //外墙
#define INNER_WALL 2 //内墙
#define BALL 3 //小球
#define BOX 4 //箱子
#define MAN 5 //人
#define BOX_ON_BALL 6 //箱子在小球上面,箱子要变色

//声明变量和方法
extern int manX;
extern int manY;
extern int map[3][GRID_N][GRID_N];
extern int level;
extern int step;
extern int direction;
extern int Direction[1024];
extern int _key;
extern int duration;

void goback();
void restart();
void xuanguan();

void loadMapFromFile(int _level);//调用此方法,给定关卡数字,内部会从关卡文件夹中加载对应的地图文件(比如:100.txt)

//加载下一关地图(根据当前第几关)
void nextLevel();
//加载上一关地图(根据当前第几关)
void beforeLevel();


//外部调用此方法,完成人物移动
void manMove();

//调用此方法,开启一个间隔时间为1秒的定时器
void startDurationTimer();

#endif

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

star marks

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值