因为地图是从文件中读取,所以关卡地图发成了资源。
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