本项目是基于 EasyX 实现的,游戏规则如下:
这里的 x y 坐标和数学中的定义不太一样,这里的 x 坐标往下是增大,y 坐标往右是增大。
#include
#include
#include
#include
using namespace std
#define RATIO 61 //地图中每个小方块的大小
#define SCREEN_WIDTH 960 //背景宽度
#define SCREEN_HEIGHT 768 //背景高度
#define LINE 9 //地图行数
#define COLUMN 12 //地图列数
#define START_X 100 //地图开始的横坐标
#define START_Y 150 //地图开始的纵坐标
enum _PROPS {
WALL, //墙:0
FLOOR, //地板:1
BOX_DES,//箱子目的地:2
MAN, //小人:3
BOX, //箱子:4
HIT, //箱子命中目标:5
ALL //道具数量:6
};
struct _POS {
int x; //小人所在的二维数组的行
int y; //小人所在的二维数组的列
};
IMAGE images[ALL]; //存储道具图标
struct _POS man;
/**************************************
* 游戏地图
* 墙:0 地板:1 箱子目的地:2
* 小人:3 箱子:4 箱子命中目标:5
**************************************/
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},
};
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.bmp"), RATIO, RATIO, true);
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] == BOX_DES) nums_hit++;
//获取小人初始位置
if (map[i][j] == MAN) {
man.x = i;
man.y = j;
}
putimage(START_X + j * RATIO, START_Y + i * RATIO, &images[map[i][j]]);
}
}
return 0;
}
热键定义:左=>a 下=>s 上=>w 右=>d 退出=>q
#include
//控制键上、下、左、右控制方向,'q'退出
#define KEY_UP 'w'
#define KEY_LEFT 'a'
#define KEY_RIGHT 'd'
#define KEY_DOWN 's'
#define KEY_QUIT 'q'
//游戏控制方向
enum _DIRECTION
{
UP,
DOWN,
LEFT,
RIGHT
};
//主函数
//...
//游戏环节
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()) {
quit = true;
gameOverScence(&bg_img);
}
}
Sleep(100);
} while (quit == false); //!quit
//...
难点:
实现上面两个需求我们可以设置额外的两个变量:
/******************************************
* 改变并打印当前图标在地图中的位置
* 输入:pos - 当前下标
* prop - 当前图标
* 输出:void
******************************************/
void changeMap(struct _POS& pos, enum _PROPS prop){
map[pos.x][pos.y] = prop;
putimage(START_X + pos.y * RATIO, START_Y + pos.x * RATIO, &images[prop]);
}
/******************************************
* 判断当前位置是否越界
* 输入:pos 当前下标
* 输出:bool
******************************************/
bool isValid(struct _POS pos){
if (map[pos.x][pos.y] == WALL) return false;
if (pos.x < 0 && pos.x >= LINE && pos.y < 0 && pos.y >= COLUMN)
return false;
return true;
}
/******************************************
* 实现游戏四个方向的控制(上、下、左、右)
* 输入:direct - 人前进的方向
* 输出:void
******************************************/
void gameControl(enum _DIRECTION direct)
{
struct _POS next_pos = man;
struct _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;
}
if (isValid(next_pos) && map[next_pos.x][next_pos.y] == FLOOR){ //如果小人前面是地板
changeMap(next_pos, MAN);
changeMap(man, last);
last = FLOOR;
man = next_pos;
}
else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX_DES) { //如果小人前面是箱子目的地
changeMap(man, FLOOR);
//小人可以经过HIT,但不能改变其在地图上的值
putimage(START_X + next_pos.y * RATIO, START_Y + next_pos.x * RATIO, &images[MAN]);
man = next_pos;
last = BOX_DES;
}
else if (isValid(next_next_pos) && (map[next_pos.x][next_pos.y] == BOX || map[next_pos.x][next_pos.y] == HIT)) { //如果小人前面是箱子或者已经在目的地上的箱子
//如果要将箱子从目的地上推开,要用更新last变量,并且nums_hit要加1
bool is_hit = false;
//如果箱子推不动,就直接退出,以免更改last变量导致下次移动出现bug
if (!(map[next_next_pos.x][next_next_pos.y] == FLOOR || map[next_next_pos.x][next_next_pos.y] == BOX_DES))
return;
//last需要在最后更新,防止覆盖之前的数据,因为接下来还要更新箱子和小人的位置
if (map[next_pos.x][next_pos.y] == HIT) {
is_hit = true;
nums_hit++;
}
if (map[next_next_pos.x][next_next_pos.y] == FLOOR) {
changeMap(next_next_pos, BOX);
changeMap(next_pos, MAN);
changeMap(man, last);
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, last);
man = next_pos;
nums_hit--;
}
//更新小人上次访问的位置
if (is_hit) last = BOX_DES;
else last = FLOOR;
}
}
/*****************************************************
* 判断游戏是否结束
* 输入:无
* 输出:bool - true表示游戏结束,false表示游戏未结束
******************************************************/
bool isGameOver(){
if (nums_hit != 0) return false;
return true;
}
/******************************************
* 游戏结束通关场景
* 函数里参数:
* DT_CENTER - 水平居中
* DT_VCENTER - 垂直居中
* DT_SINGLELINE - 文字显示在一行
* 输入:bg - 图片变量指针
* 输出:无
******************************************/
void gameOverScence(IMAGE* bg) {
putimage(0, 0, bg);
settextcolor(WHITE);
RECT rec = { 0,0,SCREEN_WIDTH,SCREEN_HEIGHT }; //定义一个矩形
settextstyle(20, 0, _T("宋体")); //设置字的大小与风格
drawtext(_T("恭喜您~\n游戏成功通关!"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
#pragma once
#include
#include
#include
#include
#include
using namespace std;
#define RATIO 61 //地图中每个小方块的大小
#define SCREEN_WIDTH 960 //背景宽度
#define SCREEN_HEIGHT 768 //背景高度
#define LINE 9 //地图行数
#define COLUMN 12 //地图列数
#define START_X 100 //地图开始的横坐标
#define START_Y 150 //地图开始的纵坐标
//控制键上、下、左、右控制方向,'q'退出
#define KEY_UP 'w'
#define KEY_LEFT 'a'
#define KEY_RIGHT 'd'
#define KEY_DOWN 's'
#define KEY_QUIT 'q'
enum _PROPS {
WALL, //墙:0
FLOOR, //地板:1
BOX_DES,//箱子目的地:2
MAN, //小人:3
BOX, //箱子:4
HIT, //箱子命中目标:5
ALL //道具数量:6
};
//游戏控制方向
enum _DIRECTION
{
UP,
DOWN,
LEFT,
RIGHT
};
struct _POS {
int x; //小人所在的二维数组的行
int y; //小人所在的二维数组的列
};
IMAGE images[ALL]; //存储道具图标
struct _POS man; //小人在数组中的位置
int nums_hit; //表示需要放的箱子数量
enum _PROPS last = FLOOR; //记录小人上次待的位置是什么
#include "box_man.h"
/**************************************
* 游戏地图
* 墙:0 地板:1 箱子目的地:2
* 小人:3 箱子:4 箱子命中目标:5
**************************************/
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},
};
/******************************************
* 改变并打印当前图标在地图中的位置
* 输入:pos - 当前下标
* prop - 当前图标
* 输出:void
******************************************/
void changeMap(struct _POS& pos, enum _PROPS prop){
map[pos.x][pos.y] = prop;
putimage(START_X + pos.y * RATIO, START_Y + pos.x * RATIO, &images[prop]);
}
/******************************************
* 判断当前位置是否越界
* 输入:pos 当前下标
* 输出:bool
******************************************/
bool isValid(struct _POS pos){
if (map[pos.x][pos.y] == WALL) return false;
if (pos.x < 0 && pos.x >= LINE && pos.y < 0 && pos.y >= COLUMN)
return false;
return true;
}
/******************************************
* 实现游戏四个方向的控制(上、下、左、右)
* 输入:direct - 人前进的方向
* 输出:void
******************************************/
void gameControl(enum _DIRECTION direct)
{
struct _POS next_pos = man;
struct _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;
}
if (isValid(next_pos) && map[next_pos.x][next_pos.y] == FLOOR){
changeMap(next_pos, MAN);
changeMap(man, last);
last = FLOOR;
man = next_pos;
}
else if (isValid(next_pos) && map[next_pos.x][next_pos.y] == BOX_DES) {
changeMap(man, FLOOR);
//小人可以经过HIT,但不能改变其在地图上的值
putimage(START_X + next_pos.y * RATIO, START_Y + next_pos.x * RATIO, &images[MAN]);
man = next_pos;
last = BOX_DES;
}
else if (isValid(next_next_pos) && (map[next_pos.x][next_pos.y] == BOX || map[next_pos.x][next_pos.y] == HIT)) {
//如果要将箱子从目的地上推开,要用更新last变量,并且nums_hit要加1
bool is_hit = false;
//如果箱子推不动,就直接退出,以免更改last变量导致下次移动出现bug
if (!(map[next_next_pos.x][next_next_pos.y] == FLOOR || map[next_next_pos.x][next_next_pos.y] == BOX_DES))
return;
if (map[next_pos.x][next_pos.y] == HIT) {
is_hit = true;
nums_hit++;
}
if (map[next_next_pos.x][next_next_pos.y] == FLOOR) {
changeMap(next_next_pos, BOX);
changeMap(next_pos, MAN);
changeMap(man, last);
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, last);
man = next_pos;
nums_hit--;
}
//更新小人上次访问的位置
if (is_hit) last = BOX_DES;
else last = FLOOR;
}
}
/*****************************************************
* 判断游戏是否结束
* 输入:无
* 输出:bool - true表示游戏结束,false表示游戏未结束
******************************************************/
bool isGameOver(){
if (nums_hit != 0) return false;
return true;
}
/******************************************
* 游戏结束通关场景
* 函数里参数:
* DT_CENTER - 水平居中
* DT_VCENTER - 垂直居中
* DT_SINGLELINE - 文字显示在一行
* 输入:bg - 图片变量指针
* 输出:无
******************************************/
void gameOverScence(IMAGE* bg) {
putimage(0, 0, bg);
settextcolor(WHITE);
RECT rec = { 0,0,SCREEN_WIDTH,SCREEN_HEIGHT }; //定义一个矩形
settextstyle(20, 0, _T("宋体")); //设置字的大小与风格
drawtext(_T("恭喜您~\n游戏成功通关!"), &rec, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
int main()
{
IMAGE bg_img; //存储背景
nums_hit = 0; //初始化目标箱子数量
//初始化背景
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.bmp"), RATIO, RATIO, true);
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] == BOX_DES) nums_hit++;
//获取小人初始位置
if (map[i][j] == MAN) {
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()) {
quit = true;
gameOverScence(&bg_img);
}
}
Sleep(100);
} while (quit == false); //!quit
system("pause");
closegraph(); //释放资源
return 0;
}
项目的图片资源放在这里啦(里面包含了 EasyX 的安装包):
链接:https://pan.baidu.com/s/1Bx6vBRnPZOe8JK8idv2KHw
提取码:xh9v