前言
使用easyX库,基于c/c++实现推箱子小游戏。
作者使用的是VS2010版本。
目录
一、游戏截图
代码中使用了两张图片,分别是熊猫头像,还有箱子,像素都是40x40;可以直接截图保存到工程目录下。图片名字请分别命名为 "player.png" "box.png" 。
二、easyX库安装
EasyX Graphics Library for C++
进入以上链接,下载并安装easyX库
三、代码实现
1.所有代码
可直接拷贝,并运行推箱子小游戏
#include "stdafx.h"
#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#define TOTALWIDTH 400
#define TOTOALHEIGHT 500
#define MAP1 0
#define MAP2 100
#define MAP3 400
#define MAP4 500
#define ESC 27
#define WIDTH 10
#define HEIGHT 10
#define TOTALLEVEL 2
typedef struct _GAME_PUTBOX
{
char direction; // 储存键盘按的方向:上 下 左 右
int x,y; // 玩家当前坐标
int boxs; // 玩家需要完成的箱子数:箱子数为0时,跳到下一关
int level; // 玩家当前关卡数
}GAME_PUTBOX;
enum MOVE_DIRECTION
{
NONE, // 无方向
MOVEUP, // 向上移动
MOVELEFT, // 向左移动
MOVEDOWN, // 向下移动
MOVERIGHT, // 向右移动
};
enum MEMBER_ARRAY
{
EMPTY, // 0 表示空地
WALL, // 1 表示墙壁
PLAYER, // 2 表示玩家
BOX, // 3 表示箱子
AIM, // 4 表示目的地
FINISH, // 5 表示箱子站在目的地上
AIMPLAYER, // 6 表示人站在目的地上
};
const int boxMap[TOTALLEVEL][HEIGHT][WIDTH] =
{ // 游戏总关卡
// level 1
{
{WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL},
{WALL, WALL, WALL, WALL, AIM, WALL, WALL, WALL, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, BOX, EMPTY, BOX, AIM, WALL, WALL},
{WALL, WALL, AIM, EMPTY, BOX, PLAYER, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, BOX, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, WALL, WALL, WALL, AIM, WALL, WALL, WALL, WALL},
{WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL}
},
// level 2
{
{WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, EMPTY, WALL},
{WALL, WALL, WALL, AIM, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY},
{WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, EMPTY, EMPTY},
{WALL, WALL, EMPTY, EMPTY, BOX, EMPTY, BOX, AIM, EMPTY, WALL},
{WALL, WALL, AIM, EMPTY, BOX, PLAYER, EMPTY, EMPTY, EMPTY, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, EMPTY, EMPTY, EMPTY, BOX, EMPTY, EMPTY, WALL, WALL},
{WALL, WALL, WALL, WALL, WALL, AIM, WALL, WALL, WALL, WALL},
{WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL, WALL}
}
};
GAME_PUTBOX player; // 玩家数据
IMAGE img_player; // 玩家图片
IMAGE img_box; // 箱子图片
int currentMap[HEIGHT][WIDTH] =
{ // 当前显示的地图,根据玩家游戏进程,实时更新
};
void putboxrun(void); // 推箱子游戏主函数
void putboxinit(void); // 初始化地图及数据
void resetputbox(void); // 重置本关卡
void drawMap(void); // 绘制地图
void putboxMove(void); // 玩家移动处理
int main(void)
{
putboxrun();
return 0;
}
/*****************//*****************//*****************//***********************
//* 函数名称:putboxrun
//* 功能描述:推箱子主函数
//*****************//*****************//*****************//************************/
void putboxrun(void)
{
int key;
putboxinit(); // 初始化地图、数据
while(1)
{
drawMap(); // 每次按键之后都会更新地图
if(!player.boxs)
{ // 完成全部箱子,等级加1,进行下一关
player.level++;
if(player.level > TOTALLEVEL)
{ // 如果完成了所有关卡,则结束游戏
break;
}
resetputbox(); // 更新下一关数据
continue;
}
key = _getch(); // 获取用户按键
player.direction = NONE; // 先清除移动方向
switch(key)
{
case 'w':
case 'W':
player.direction = MOVEUP;
break;
case 'a':
case 'A':
player.direction = MOVELEFT;
break;
case 's':
case 'S':
player.direction = MOVEDOWN;
break;
case 'd':
case 'D':
player.direction = MOVERIGHT;
break;
case ESC:
return;
case 'r':
case 'R':
resetputbox();
continue;
break;
default:
break;
}
putboxMove(); // 根据玩家移动方向,进行处理
}
_getch();
}
/*****************//*****************//*****************//***********************
//* 函数名称:putboxinit
//* 功能描述:推箱子初始化
//*****************//*****************//*****************//************************/
void putboxinit()
{
initgraph(TOTALWIDTH, TOTOALHEIGHT); // 初始化窗口,设置窗口大小
HWND window = GetHWnd(); // 获取当前窗口
SetWindowText(window, "推箱子 - by耒阳阿杰"); // 设置当前窗口的标题
outtextxy(5,5,"ESC: 退出"); // 设置提示信息
outtextxy(80,5,"R: 重置本关");
outtextxy(5,30,"W: 上 A:左 S:下 D:右");
fillrectangle(MAP1,MAP2,MAP3,MAP4); // 划分游戏区域
player.level = 1; // 初始等级为1
loadimage(&img_player,"player.png",40,40); // 加载玩家图片
loadimage(&img_box,"box.png",40,40); // 加载玩家图片
resetputbox();
}
/*****************//*****************//*****************//***********************
//* 函数名称:resetputbox
//* 功能描述:重置本关
//*****************//*****************//*****************//************************/
void resetputbox()
{
int i,j;
int uy,ux;
uy = (MAP4 - MAP2) / HEIGHT;
ux = (MAP3 - MAP1) / WIDTH;
player.boxs = 0;
for(i = 0; i < HEIGHT;i++)
{
for(j = 0; j < WIDTH; j++)
{
currentMap[i][j] = boxMap[player.level - 1][i][j];
if(currentMap[i][j] == BOX)
{ // 每检测到一次箱子,箱子数加1
player.boxs++;
}
if(currentMap[i][j] == PLAYER)
{ // 记录玩家位置
player.x = j;
player.y = i;
}
}
}
}
/*****************//*****************//*****************//***********************
//* 函数名称:drawMap
//* 功能描述:绘制关卡地图
//*****************//*****************//*****************//************************/
void drawMap()
{
int i,j;
int uy,ux;
char ch[10];
uy = (MAP4 - MAP2) / HEIGHT;
ux = (MAP3 - MAP1) / WIDTH;
sprintf_s(ch, "%1s%1u", "level: ",player.level);
settextcolor(LIGHTRED);
settextstyle(30,30,0);
outtextxy(100,60,ch); // 显示当前等级
// 绘制地图
for(i = 0; i < HEIGHT;i++)
{
for(j = 0; j < WIDTH; j++)
{
switch(currentMap[i][j])
{
case EMPTY: // 空地
setfillcolor(CYAN); // 选择填充颜色
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy); // 填充相应区域
break;
case WALL: // 墙墙
setfillcolor(DARKGRAY);
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
break;
case PLAYER: // 玩家
//setfillcolor(RED);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_player);
break;
case BOX: // 箱子
//setfillcolor(BROWN);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_box);
break;
case AIM: // 目标点
setfillcolor(GREEN);
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
break;
case FINISH: // 箱子站在目标点上
//setfillcolor(WHITE);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_box);
break;
case AIMPLAYER: // 玩家站在目标点上
//setfillcolor(RED);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_player);
break;
}
}
}
}
/*****************//*****************//*****************//***********************
//* 函数名称:putboxMove
//* 功能描述:移动处理
//*****************//*****************//*****************//************************/
void putboxMove(void)
{
int ux,uy,uux,uuy;
switch(player.direction)
{ // 根据移动的方向,计算移动一格的位置,和移动两格的位置
case NONE:
break;
case MOVEUP:
ux = player.x;
uy = player.y - 1;
uux = ux;
uuy = uy - 1;
break;
case MOVELEFT:
ux = player.x - 1;
uy = player.y;
uux = ux - 1;
uuy = uy;
break;
case MOVEDOWN:
ux = player.x;
uy = player.y + 1;
uux = ux;
uuy = uy + 1;
break;
case MOVERIGHT:
ux = player.x + 1;
uy = player.y;
uux = ux + 1;
uuy = uy;
break;
}
if( (ux < 0 || ux > HEIGHT)
|| (uy < 0 || uy > WIDTH) )
{ // 如果移动后的x或y坐标超过范围,则无效
return;
}
switch(currentMap[uy][ux])
{ // 根据下一格的内容,作出相应处理
case EMPTY: // 下一格为空地,则移动到空地
currentMap[uy][ux] = PLAYER;
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
break;
case WALL: // 下一格为墙或者玩家(玩家:黑人脸问号?),则无效退出
case PLAYER:
break;
case FINISH: // 下一格为站在目标点上的箱子
case BOX: // 下一格为箱子,则根据箱子的下一格内容,作出不同处理
switch(currentMap[uuy][uux])
{ // 根据箱子的下一格内容,作出相应处理
case EMPTY: // 箱子的下一格为空,则玩家与箱子都前进一格
currentMap[uuy][uux] = BOX;
if(currentMap[uy][ux] == FINISH)
{ // 如果箱子刚好站在目标点上,则移动后的格子为人站在目标点上
currentMap[uy][ux] = AIMPLAYER;
player.boxs++; // 箱子数++ 因为箱子离开了目标点
}
else
{ // 如果箱子站在空地上,则移动后的格子玩家
currentMap[uy][ux] = PLAYER;
}
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
break;
case WALL: // 箱子的下一格为墙、玩家(玩家:再次黑人脸问号?)、箱子,则无效退出
case PLAYER:
case BOX:
break;
case AIM: // 箱子的下一格为目标点,nice,玩家和箱子都前进一格,完成一个小目标,
currentMap[uuy][uux] = FINISH;
if(currentMap[uy][ux] == FINISH)
{ // 如果箱子刚好站在目标点上,则移动后的格子为人站在目标点上
currentMap[uy][ux] = AIMPLAYER;
player.boxs++; // 箱子数++ 因为箱子离开了目标点
}
else
{ // 如果箱子站在空地上,则移动后的格子玩家
currentMap[uy][ux] = PLAYER;
}
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
player.boxs--;
break;
case FINISH:
break;
}
break;
case AIM:
currentMap[uy][ux] = AIMPLAYER;
currentMap[player.y][player.x] = EMPTY;
player.y = uy;
player.x = ux;
break;
case AIMPLAYER: // 玩家与目标点重合
break;
}
}
2.具体实现过程
2.1初始化函数 putboxinit()
绘制游戏的窗口、提示信息、游戏地图等
void putboxinit()
{
initgraph(TOTALWIDTH, TOTOALHEIGHT); // 初始化窗口,设置窗口大小
HWND window = GetHWnd(); // 获取当前窗口
SetWindowText(window, "推箱子 - by耒阳阿杰"); // 设置当前窗口的标题
outtextxy(5,5,"ESC: 退出"); // 设置提示信息
outtextxy(80,5,"R: 重置本关");
outtextxy(5,30,"W: 上 A:左 S:下 D:右");
fillrectangle(MAP1,MAP2,MAP3,MAP4); // 划分游戏区域
player.level = 1; // 初始等级为1
loadimage(&img_player,"player.png",40,40); // 加载玩家图片
loadimage(&img_box,"box.png",40,40); // 加载玩家图片
resetputbox(); // 根据玩家等级,重置相应关卡
}
2.2重置关卡函数 resetputbox()
根据玩家当前level(默认为1),从地图库boxMap数组中读取相应的地图,写入到当前游戏地图数组currentMap;并记录玩家坐标、需要完成的箱子数量
void resetputbox()
{
int i,j;
int uy,ux;
uy = (MAP4 - MAP2) / HEIGHT;
ux = (MAP3 - MAP1) / WIDTH;
player.boxs = 0;
for(i = 0; i < HEIGHT;i++)
{
for(j = 0; j < WIDTH; j++)
{
currentMap[i][j] = boxMap[player.level - 1][i][j];
if(currentMap[i][j] == BOX)
{ // 每检测到一次箱子,箱子数加1
player.boxs++;
}
if(currentMap[i][j] == PLAYER)
{ // 记录玩家位置
player.x = j;
player.y = i;
}
}
}
}
2.3绘制地图函数 drawMap()
玩家每次按下按键之后,都会执行此函数。
游戏区域总像素是400x400;将游戏区域分为10x10,也就是分为100个像素小块,每个像素块都是40x40像素,根据currentMap数组的每个成员,对每个像素小块进行填充或贴图。
注意:玩家和箱子是直接读取的图片,所以要保证工程目录下有这两张图片!
void drawMap()
{
int i,j;
int uy,ux;
char ch[10];
uy = (MAP4 - MAP2) / HEIGHT;
ux = (MAP3 - MAP1) / WIDTH;
sprintf_s(ch, "%1s%1u", "level: ",player.level);
settextcolor(LIGHTRED);
settextstyle(30,30,0);
outtextxy(100,60,ch); // 显示当前等级
// 绘制地图
for(i = 0; i < HEIGHT;i++)
{
for(j = 0; j < WIDTH; j++)
{
switch(currentMap[i][j])
{
case EMPTY: // 空地
setfillcolor(CYAN); // 选择填充颜色
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy); // 填充相应区域
break;
case WALL: // 墙墙
setfillcolor(DARKGRAY);
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
break;
case PLAYER: // 玩家
//setfillcolor(RED);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_player);
break;
case BOX: // 箱子
//setfillcolor(BROWN);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_box);
break;
case AIM: // 目标点
setfillcolor(GREEN);
fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
break;
case FINISH: // 箱子站在目标点上
//setfillcolor(WHITE);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_box);
break;
case AIMPLAYER: // 玩家站在目标点上
//setfillcolor(RED);
//fillrectangle(MAP1 + j*ux,MAP2 + i*uy,MAP1 + (j+1)*ux,MAP2 + (i+1)*uy);
putimage(MAP1 + j*ux,MAP2 + i*uy,&img_player);
break;
}
}
}
}
2.4玩家移动处理函数 putboxMove
根据玩家按下的移动指令,作出相应处理,改变当前游戏地图数组currentMap的内容。
此函数稍微有点复杂,需要考虑的情况比较多,请理解清楚。
第一步,先根据移动的方向,得到移动方向下一格的坐标ux,uy,和移动方向下下格的坐标uux,uuy;
第二步,判断移动方向下一格的坐标ux,uy是否超出游戏地图范围,如果超过了,本次移动指令无效。
第三步,根据移动方向下一格ux,uy所在坐标的内容,作出不同处理;
如果下一格ux,uy为空地,则玩家x,y与空地ux,uy交换位置,并保存到当前游戏地图数组currentMap中;
如果下一格ux,uy为墙,或者玩家(不可能),则本次操作无效;
如果下一格ux,uy为目的地,则玩家与目的地重合,ux,uy记录为AIMPLAYER,,x,y记录为EMPTY,保存到currentMap数组中;
如果下一格为箱子,则又需要根据箱子的下一格的内容,分别进行处理:
如果下下格uux,uuy为空地,则箱子ux,uy移动到空地uux,uuy,uux,uuy记录为BOX;此时又要分别考虑箱子ux,uy原来是站在空地上,还是站在目的地上;如果箱子原来站在目的地上,则箱子从目的地离开后,需要特殊处理,需要完成的箱子数量加1,玩家x,y则与目的地ux,uy重合,ux,uy记录为AIMPLAYER;如果箱子原来站在空地上,则箱子ux,uy从空地离开后,玩家站在空地ux,uy上,ux,uy记录为PLAYER。此时又需要考虑玩家,原来是站立的位置;如果玩家原来站在目的地上,则玩家从目的地上离开后,x,y记录为AIM;如果玩家站在空地上,则x,y记录为EMPTY,并将新的uux,uuy;ux,uy; x,y保存到数组currentMap中。
如果下下格uux,uuy为墙、玩家(不可能)、箱子,则本次操作无效。
如果下下格uux,uuy为目的地,则箱子移动到目的地,uux,uuy记录为FINISH;此时又要分别考虑箱子ux,uy原来是站在空地上,还是站在目的地上;如果箱子ux,uy原来站在目的地上,则箱子从目的地离开后,需要特殊处理,需要完成的箱子数量减1,玩家则与目的地重合,ux,uy记录为AIMPLAYER。此时又需要考虑玩家,原来是站立的位置;如果玩家原来站在目的地上,则玩家从目的地上离开后,x,y记录为AIM;如果玩家站在空地上,则x,y记录为EMPTY,并将新的uux,uuy;ux,uy; x,y保存到数组currentMap中。
void putboxMove(void)
{
int ux,uy,uux,uuy;
switch(player.direction)
{ // 根据移动的方向,计算移动一格的位置,和移动两格的位置
case NONE:
break;
case MOVEUP:
ux = player.x;
uy = player.y - 1;
uux = ux;
uuy = uy - 1;
break;
case MOVELEFT:
ux = player.x - 1;
uy = player.y;
uux = ux - 1;
uuy = uy;
break;
case MOVEDOWN:
ux = player.x;
uy = player.y + 1;
uux = ux;
uuy = uy + 1;
break;
case MOVERIGHT:
ux = player.x + 1;
uy = player.y;
uux = ux + 1;
uuy = uy;
break;
}
if( (ux < 0 || ux > HEIGHT)
|| (uy < 0 || uy > WIDTH) )
{ // 如果移动后的x或y坐标超过范围,则无效
return;
}
switch(currentMap[uy][ux])
{ // 根据下一格的内容,作出相应处理
case EMPTY: // 下一格为空地,则移动到空地
currentMap[uy][ux] = PLAYER;
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
break;
case WALL: // 下一格为墙或者玩家(玩家:黑人脸问号?),则无效退出
case PLAYER:
break;
case FINISH: // 下一格为站在目标点上的箱子
case BOX: // 下一格为箱子,则根据箱子的下一格内容,作出不同处理
switch(currentMap[uuy][uux])
{ // 根据箱子的下一格内容,作出相应处理
case EMPTY: // 箱子的下一格为空,则玩家与箱子都前进一格
currentMap[uuy][uux] = BOX;
if(currentMap[uy][ux] == FINISH)
{ // 如果箱子刚好站在目标点上,则移动后的格子为人站在目标点上
currentMap[uy][ux] = AIMPLAYER;
player.boxs++; // 箱子数++ 因为箱子离开了目标点
}
else
{ // 如果箱子站在空地上,则移动后的格子玩家
currentMap[uy][ux] = PLAYER;
}
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
break;
case WALL: // 箱子的下一格为墙、玩家(玩家:再次黑人脸问号?)、箱子,则无效退出
case PLAYER:
case BOX:
break;
case AIM: // 箱子的下一格为目标点,nice,玩家和箱子都前进一格,完成一个小目标,
currentMap[uuy][uux] = FINISH;
if(currentMap[uy][ux] == FINISH)
{ // 如果箱子刚好站在目标点上,则移动后的格子为人站在目标点上
currentMap[uy][ux] = AIMPLAYER;
player.boxs++; // 箱子数++ 因为箱子离开了目标点
}
else
{ // 如果箱子站在空地上,则移动后的格子玩家
currentMap[uy][ux] = PLAYER;
}
if(currentMap[player.y][player.x] == AIMPLAYER)
{ // 如果玩家刚好站在目标点上,则移动后的格子为目标点
currentMap[player.y][player.x] = AIM;
}
else
{ // 如果玩家站在空地上,则移动后的格子为空地
currentMap[player.y][player.x] = EMPTY;
}
player.y = uy;
player.x = ux;
player.boxs--;
break;
case FINISH:
break;
}
break;
case AIM:
currentMap[uy][ux] = AIMPLAYER;
currentMap[player.y][player.x] = EMPTY;
player.y = uy;
player.x = ux;
break;
case AIMPLAYER: // 玩家与目标点重合
break;
}
}
2.5 推箱子游戏主题函数 putboxrun()
此函数整合了整个推箱子游戏的所有函数;
初始化后,会进入死循环,并根据currentMap数组的成员,绘制当前游戏画面;
之后会进行游戏结束判断,根据需要完成的箱子数量player.boxs,如果数量为0,则当前关卡完成,等级level加1;
如果等级level超过了最大等级TOTALLEVEL,则判断游戏结束,退出游戏
如果等级level没有超过最大等级TOTALLEVEL,更新下一关卡的游戏数据,并重新开始循环
如果需要完成的箱子数量不为0,则等待玩家操作键盘;
玩家按下键盘后,读取输入的值,进行处理:
如果按下了移动键(W、S、A、D),则进入移动处理函数;
如果按下了重置键(R),则重新开始本关卡;
如果按下了退出键(ESC),则退出推箱子游戏。
void putboxrun(void)
{
int key;
putboxinit(); // 初始化地图、数据
while(1)
{
drawMap(); // 每次按键之后都会更新地图
if(!player.boxs)
{ // 完成全部箱子,等级加1,进行下一关
player.level++;
if(player.level > TOTALLEVEL)
{ // 如果完成了所有关卡,则结束游戏
break;
}
resetputbox(); // 更新下一关数据
continue;
}
key = _getch(); // 获取用户按键
player.direction = NONE; // 先清除移动方向
switch(key)
{
case 'w':
case 'W':
player.direction = MOVEUP;
break;
case 'a':
case 'A':
player.direction = MOVELEFT;
break;
case 's':
case 'S':
player.direction = MOVEDOWN;
break;
case 'd':
case 'D':
player.direction = MOVERIGHT;
break;
case ESC:
return;
case 'r':
case 'R':
resetputbox();
continue;
break;
default:
break;
}
putboxMove(); // 根据玩家移动方向,进行处理
}
_getch();
}
总结
此游戏还有很多可以完善的地方:
比如地图只采用了简单的像素填充不够美观,只有玩家和箱子是贴图。后续大家可以自己制作或去网上找到更合适的图片,将游戏制作的更加精美;
游戏地图暂时只有两个,没有开始游戏、游戏结束的动画界面等。
本人也只是一枚小菜鸟,希望与大家一起学习进步。