C++入门——实现推箱子游戏

参考

  1. 《C和C++游戏趣味编程》 童晶

推箱子游戏

玩家通过键盘控制游戏角色将所有黄色箱子推到白色方块处
在这里插入图片描述

用字符数组存储关卡数据

游戏一共有6种元素:
(1)空白区域:玩家可以穿过,箱子可以推上去;英文名称:empty;缩写:‘e’

(2)墙:玩家不能经过,箱子不能推过去;英文名称:wall;缩写:‘w’

(3)箱子:在前面没有障碍物的情况下,玩家可以推动;英文名称:box;缩写:‘b’

(4)空白目标:需要玩家将箱子推上去;英文名称:target;缩写:‘t’

(5)完成目标:一个箱子在目标上的叠加状态;英文名称:achieved;缩写:‘a’

(6)游戏角色:可以键盘控制移动,推动箱子到达目标;英文名称:player;缩写:‘p’

定义全局变量level存储地图数据,在show()函数中根据level[i][j]的值绘制图案:

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define B_SIZE 60       // 方块大小
#define B_NUM 8         // 方块个数,一共8*8个方块

// 用字符型二维数组存储地图数据
char level[B_NUM][B_NUM + 1] = { "wwwwwwww", "wwwtbeew", "weeeeeew", "weeeeeew", "weeeeeew", "weepaeew", "weeewwww", "wwwwwwww" };

void startup()
{
	initgraph(B_NUM * B_SIZE, B_NUM * B_SIZE);
	setbkcolor(RGB(150, 150, 150));             // 灰色背景
	BeginBatchDraw();                           // 开始批量绘制
}

void show()
{
	int i, j;
	cleardevice();
	for (i = 0; i < B_NUM; i++)
	{
		for (j = 0; j < B_NUM; j++)
		{
			if (level[i][j] == 'e')              // 空白区域
			{
				setfillcolor(RGB(150, 150, 150));// 绘制灰色地面
				setlinecolor(RGB(150, 150, 150));
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 'w')         // 墙
			{
				setfillcolor(RGB(155, 0, 0));
				setlinecolor(RGB(150, 150, 150));// 淡红色、灰色线的方框
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 'b')         // 箱子
			{
				setfillcolor(RGB(255, 255, 0));  // 黄色方块
				setlinecolor(RGB(150, 150, 150));
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 't')         // 目标
			{
				setfillcolor(RGB(250, 250, 250));// 白色小方块
				fillrectangle((j + 0.3) * B_SIZE, (i + 0.3) * B_SIZE, (j + 0.7) * B_SIZE, (i + 0.7) * B_SIZE);
			}
			else if (level[i][j] == 'a')         // 已完成目标
			{
				setlinecolor(RGB(150, 150, 150));
				setfillcolor(RGB(255, 255, 0));  // 黄色小方块
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
				setfillcolor(RGB(250, 250, 250));// 白色小方块
				fillrectangle((j + 0.3) * B_SIZE, (i + 0.3) * B_SIZE, (j + 0.7) * B_SIZE, (i + 0.7) * B_SIZE);
			}
			else if (level[i][j] == 'p')         // 玩家
			{
				setfillcolor(RGB(255, 0, 0));    // 红色圆脸
				fillcircle((j + 0.5) * B_SIZE, (i + 0.5) * B_SIZE, 0.4 * B_SIZE);
				setfillcolor(RGB(80, 80, 80));   // 黑色眼睛
				setlinecolor(RGB(80, 80, 80));
				fillcircle((j + 0.3) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
				fillcircle((j + 0.7) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
				setlinestyle(PS_SOLID, 3);
				line((j + 0.35) * B_SIZE, (i + 0.7) * B_SIZE, (j + 0.65) * B_SIZE, (i + 0.7) * B_SIZE);
				setlinestyle(PS_SOLID, 1);
			}
		}
	}
	FlushBatchDraw();
}

void update()
{

}

int main()
{
	startup();
	while (1)
	{
		show();
		update();
	}
	return 0;
}

键盘控制角色移动

定义结构体Player记录玩家位置:

struct Player
{
	int i;
	int j;
};
Player player;

遍历二维数组level,找到’p’的位置,即玩家位置,赋值给player,再把’p’变成’e’:

int i, j;
for (i = 0; i < B_NUM; i++)
{
	for (j = 0; j < B_NUM; j++)
	{
		if (level[i][j] == 'p')
		{
			player.i = i;
			player.j = j;
			level[i][j] = 'e';
		}
	}
}

将show()中对玩家的绘制独立出来,根据player中存储的位置,绘制玩家:


```cpp
void show()
{
	i = player.i;
	j = player.j;
	setfillcolor(RGB(255, 0, 0));    // 红色圆脸
	fillcircle((j + 0.5) * B_SIZE, (i + 0.5) * B_SIZE, 0.4 * B_SIZE);
	setfillcolor(RGB(80, 80, 80));   // 黑色眼睛
	setlinecolor(RGB(80, 80, 80));
	fillcircle((j + 0.3) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
	fillcircle((j + 0.7) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
	setlinestyle(PS_SOLID, 3);
	line((j + 0.35) * B_SIZE, (i + 0.7) * B_SIZE, (j + 0.65) * B_SIZE, (i + 0.7) * B_SIZE);
	setlinestyle(PS_SOLID, 1);
	FlushBatchDraw();
}

在update()中,根据用户按键,控制角色移动:

if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
{
	int goal_i = player.i;    // 定义变量存储目标位置
	int goal_j = player.j;
	int goalNext_i = goal_i;  // 存储目标位置再前一个位置
	int goalNext_j = goal_j;

	if (input == 'A')         // 向左
	{
		goal_j = player.j - 1;
		goalNext_j = goal_j - 1;
	}
	else if (input == 'D')    // 向右
	{
		goal_j = player.j + 1;
		goalNext_j = goal_j + 1;
	}
	else if (input == 'S')    // 向下
	{
		goal_i = player.i + 1;
		goalNext_i = goal_i + 1;
	}
	else if (input == 'W')    // 向上
	{
		goal_i = player.i - 1;
		goalNext_i = goal_i - 1;
	}

元素更新

假设角色向右移动,有以下13种情况:
(1)角色右边是empty。此时可以移动

(2)角色右边是target。此时可以移动

(3)角色右边是wall。此时不可以移动

(4)角色右边是box,再右边是empty。此时角色和box一起右移

(5)角色右边是box,再右边是target。此时角色和box一起右移

(6)角色右边是achieved,再右边是empty。此时角色和box一起右移

(7)角色右边是achieved,再右边是achieved。此时角色和box一起右移

(8)角色右边是box,再右边是wall。此时不可以移动

(9)角色右边是box,再右边是box。此时不可以移动

(10)角色右边是box,再右边是achieved。此时不可以移动

(11)角色右边是achieved,再右边是wall。此时不可以移动

(12)角色右边是achieved,再右边是box。此时不可以移动

(13)角色右边是achieved,再右边是achieved。此时不可以移动

根据用户的不同输入,变量goal_i、goal_j存储游戏角色移动的目标位置,goalNext_i、goalNext_j存储目标位置再向前的一个位置。共有6种情况会移动游戏角色及更新关卡元素,在update()中实现相应的处理:

void update()
{
	if (_kbhit())
	{
		char input = _getch();
		if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
		{
			int goal_i = player.i;    // 定义变量存储目标位置
			int goal_j = player.j;
			int goalNext_i = goal_i;  // 存储目标位置再前一个位置
			int goalNext_j = goal_j;

			if (input == 'A')         // 向左
			{
				goal_j = player.j - 1;
				goalNext_j = goal_j - 1;
			}
			else if (input == 'D')    // 向右
			{
				goal_j = player.j + 1;
				goalNext_j = goal_j + 1;
			}
			else if (input == 'S')    // 向下
			{
				goal_i = player.i + 1;
				goalNext_i = goal_i + 1;
			}
			else if (input == 'W')    // 向上
			{
				goal_i = player.i - 1;
				goalNext_i = goal_i - 1;
			}

			// 根据不同情况,更新元素
			if (level[goal_i][goal_j] == 'e' || level[goal_i][goal_j] == 't') // 目标位置是empty或target
			{
				player.i = goal_i;
				player.j = goal_j;
			}
			else if (level[goal_i][goal_j] == 'b' && level[goalNext_i][goalNext_j] == 'e') // 目标位置是box,再前面是empty
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 'e';                                               // 目标位置变成empty
				level[goalNext_i][goalNext_j] = 'b';                                       // 再前面变成box
			}
			else if (level[goal_i][goal_j] == 'b' && level[goalNext_i][goalNext_j] == 't') // 目标是box,再前面是target
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 'e';                                               // 目标位置变成empty
				level[goalNext_i][goalNext_j] = 'a';                                       // 再前面变成achieved
			}
			else if (level[goal_i][goal_j] == 'a' && level[goalNext_i][goalNext_j] == 'e') // 目标位置是achieved,再前面是empty
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 't';                                               // 目标位置变成target
				level[goalNext_i][goalNext_j] = 'b';                                       // 再前面变成box
			}
			else if (level[goal_i][goal_j] == 'a' && level[goalNext_i][goalNext_j] == 't') // 目标位置是achieved,再前面是target
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 't';                                               // 目标位置变成target
				level[goalNext_i][goalNext_j] = 'a';                                       // 再前面变成achieved
			}
			else
			{
				return;
			}
		}
	}
}

游戏胜利判断

定义全局变量存储target个数、和achieved个数:

int targetNum, achievedNum;

在startup()中遍历二维数组时,如果地图元素为target或achieved,则将targetNum加1:

for (i = 0; i < B_NUM; i++)
{
	for (j = 0; j < B_NUM; j++)
	{
		if (level[i][j] == 'p')
		{
			player.i = i;
			player.j = j;
			level[i][j] = 'e';
		}
		else if (level[i][j] == 't' || level[i][j] == 'a')
		{
			targetNum++;
		}
	}
}

在update()函数中统计元素更新后的achieved个数:

achievedNum = 0;
int i, j;
for (i = 0; i < B_NUM; i++)
{
	for (j = 0; j < B_NUM; j++)
	{
		if (level[i][j] == 'a')
		{
			achievedNum++;
		}
	}
}

如果完成目标,在show()中显示游戏胜利信息:

if (achievedNum == targetNum)    // 如果完成目标
{
	setbkmode(TRANSPARENT);      // 透明显示文字
	settextcolor(RGB(0, 255, 255));
	settextstyle(80, 0, _T("宋体"));
	outtextxy(80, 200, _T("游戏胜利"));
}
FlushBatchDraw();

多关卡实现

定义三维数组levels存储所有关卡地图数据,定义currentLevelNum表示当前玩到第几关,二维数组level存储正在玩的这一关的地图数据:

// 获取当前管的地图数据
for (i = 0; i < B_NUM; i++)
{
	for (j = 0; j < B_NUM; j++)
	{
		level[i][j] = levels[currentLevelNum][i][j];
	}
}

在update()中,如果当前关卡完成,就将currentLevelNum加1,并调用startup()开始下一关的初始化:

if (achievedNum == targetNum)
{
	show();																		   // 调用显示游戏胜利画面
	if (currentLevelNum < LEVEL_TOTALNUM - 1)
	{
		currentLevelNum++;
		startup();                                                                 // 开始下一关的初始化
	}
}

在show()中,如果还有未完成的关卡,显示将要开始第几关游戏;如果所有关卡都完成了,提示游戏胜利:

if (achievedNum == targetNum)    // 如果完成目标
{
	setbkmode(TRANSPARENT);      // 透明显示文字
	settextcolor(RGB(0, 255, 100));
	TCHAR str[20];
	if (currentLevelNum < LEVEL_TOTALNUM - 1) // 还有未完成的关卡
	{
		settextstyle(50, 0, _T("宋体"));
		swprintf_s(str, _T("开始第%d关", currentLevelNum + 2));  // 提示开始第几关
		outtextxy(120, 160, str);
		outtextxy(10, 250, _T("按空格键重玩当前关"));
	}
	else
	{
		settextstyle(80, 0, _T("宋体"));
		outtextxy(80, 200, _T("游戏胜利"));
	}
	FlushBatchDraw();
	Sleep(2500);
}

完整代码

在这里插入图片描述

#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#define B_SIZE 60       // 方块大小
#define B_NUM 8         // 方块个数,一共8*8个方块
#define LEVEL_TOTALNUM 5 // 一共多少关卡

struct Player
{
	int i;
	int j;
};
Player player;

char levels[LEVEL_TOTALNUM][B_NUM][B_NUM + 1] =
{
	{"wwwwwwww", "wwwtbeew", "weeeeeew", "weeeeeew", "weeeeeew", "wepbteew", "weeewwww", "wwwwwwww"},
	{"wwwwwwww", "wwweewww", "wpetbwww", "weeebeww", "wewteeww", "weeeeeww", "weepwwww", "wwwwwwww"},
	{"wwwwwwww", "wwpeewww", "weeweeww", "webabeww", "weeteeww", "wwetewww", "wwwwwwww", "wwwwwwww"},
	{"wwwwwwww", "wwwwwwww", "weeeewww", "weeettew", "webbbpew", "weewetww", "wwwwwwww", "wwwwwwww"},
	{"wwwwwwww", "wwwwwwww", "wwteewww", "weewebpw", "weewewew", "weaeebtw", "weeeewww", "wwwwwwww"}
};
int currentLevelNum = 0;
char level[B_NUM][B_NUM + 1];
int targetNum, achievedNum;

void startup()
{
	initgraph(B_NUM * B_SIZE, B_NUM * B_SIZE);
	setbkcolor(RGB(150, 150, 150));             // 灰色背景
	BeginBatchDraw();                           // 开始批量绘制
	int i, j;

	// 获取当前管的地图数据
	for (i = 0; i < B_NUM; i++)
	{
		for (j = 0; j < B_NUM; j++)
		{
			level[i][j] = levels[currentLevelNum][i][j];
		}
	}

	targetNum = 0;
	achievedNum = 0;
	for (i = 0; i < B_NUM; i++)
	{
		for (j = 0; j < B_NUM; j++)
		{
			if (level[i][j] == 'p')
			{
				player.i = i;
				player.j = j;
				level[i][j] = 'e';
			}
			else if (level[i][j] == 't' || level[i][j] == 'a')
			{
				targetNum++;
			}
		}
	}
}

void show()
{
	int i, j;
	cleardevice();
	for (i = 0; i < B_NUM; i++)
	{
		for (j = 0; j < B_NUM; j++)
		{
			if (level[i][j] == 'e')              // 空白区域
			{
				setfillcolor(RGB(150, 150, 150));// 绘制灰色地面
				setlinecolor(RGB(150, 150, 150));
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 'w')         // 墙
			{
				setfillcolor(RGB(155, 0, 0));
				setlinecolor(RGB(150, 150, 150));// 淡红色、灰色线的方框
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 'b')         // 箱子
			{
				setfillcolor(RGB(255, 255, 0));  // 黄色方块
				setlinecolor(RGB(150, 150, 150));
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
			}
			else if (level[i][j] == 't')         // 目标
			{
				setfillcolor(RGB(250, 250, 250));// 白色小方块
				fillrectangle((j + 0.3) * B_SIZE, (i + 0.3) * B_SIZE, (j + 0.7) * B_SIZE, (i + 0.7) * B_SIZE);
			}
			else if (level[i][j] == 'a')         // 已完成目标
			{
				setlinecolor(RGB(150, 150, 150));
				setfillcolor(RGB(255, 255, 0));  // 黄色小方块
				fillrectangle(j * B_SIZE, i * B_SIZE, (j + 1) * B_SIZE, (i + 1) * B_SIZE);
				setfillcolor(RGB(250, 250, 250));// 白色小方块
				fillrectangle((j + 0.3) * B_SIZE, (i + 0.3) * B_SIZE, (j + 0.7) * B_SIZE, (i + 0.7) * B_SIZE);
			}
		}
	}
	// 绘制玩家
	i = player.i;
	j = player.j;
	setfillcolor(RGB(255, 0, 0));    // 红色圆脸
	fillcircle((j + 0.5) * B_SIZE, (i + 0.5) * B_SIZE, 0.4 * B_SIZE);
	setfillcolor(RGB(80, 80, 80));   // 黑色眼睛
	setlinecolor(RGB(80, 80, 80));
	fillcircle((j + 0.3) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
	fillcircle((j + 0.7) * B_SIZE, (i + 0.45) * B_SIZE, 0.08 * B_SIZE);
	setlinestyle(PS_SOLID, 3);
	line((j + 0.35) * B_SIZE, (i + 0.7) * B_SIZE, (j + 0.65) * B_SIZE, (i + 0.7) * B_SIZE);
	setlinestyle(PS_SOLID, 1);

	if (achievedNum == targetNum)    // 如果完成目标
	{
		setbkmode(TRANSPARENT);      // 透明显示文字
		settextcolor(RGB(0, 255, 100));
		TCHAR str[20];
		if (currentLevelNum < LEVEL_TOTALNUM - 1) // 还有未完成的关卡
		{
			settextstyle(50, 0, _T("宋体"));
			swprintf_s(str, _T("开始第%d关"), currentLevelNum + 2);  // 提示开始第几关
			outtextxy(120, 160, str);
			outtextxy(10, 250, _T("按空格键重玩当前关"));
		}
		else
		{
			settextstyle(80, 0, _T("宋体"));
			outtextxy(80, 200, _T("游戏胜利"));
		}
		FlushBatchDraw();
		Sleep(2500);
	}
	FlushBatchDraw();
}

void update()
{
	if (_kbhit() && (achievedNum < targetNum))
	{
		char input = _getch();
		if (input == ' ')
		{
			startup();
		}
		if (input == 'A' || input == 'S' || input == 'D' || input == 'W')
		{
			int goal_i = player.i;    // 定义变量存储目标位置
			int goal_j = player.j;
			int goalNext_i = goal_i;  // 存储目标位置再前一个位置
			int goalNext_j = goal_j;

			if (input == 'A')         // 向左
			{
				goal_j = player.j - 1;
				goalNext_j = goal_j - 1;
			}
			else if (input == 'D')    // 向右
			{
				goal_j = player.j + 1;
				goalNext_j = goal_j + 1;
			}
			else if (input == 'S')    // 向下
			{
				goal_i = player.i + 1;
				goalNext_i = goal_i + 1;
			}
			else if (input == 'W')    // 向上
			{
				goal_i = player.i - 1;
				goalNext_i = goal_i - 1;
			}

			// 根据不同情况,更新元素
			if (level[goal_i][goal_j] == 'e' || level[goal_i][goal_j] == 't') // 目标位置是empty或target
			{
				player.i = goal_i;
				player.j = goal_j;
			}
			else if (level[goal_i][goal_j] == 'b' && level[goalNext_i][goalNext_j] == 'e') // 目标位置是box,再前面是empty
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 'e';                                               // 目标位置变成empty
				level[goalNext_i][goalNext_j] = 'b';                                       // 再前面变成box
			}
			else if (level[goal_i][goal_j] == 'b' && level[goalNext_i][goalNext_j] == 't') // 目标是box,再前面是target
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 'e';                                               // 目标位置变成empty
				level[goalNext_i][goalNext_j] = 'a';                                       // 再前面变成achieved
			}
			else if (level[goal_i][goal_j] == 'a' && level[goalNext_i][goalNext_j] == 'e') // 目标位置是achieved,再前面是empty
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 't';                                               // 目标位置变成target
				level[goalNext_i][goalNext_j] = 'b';                                       // 再前面变成box
			}
			else if (level[goal_i][goal_j] == 'a' && level[goalNext_i][goalNext_j] == 't') // 目标位置是achieved,再前面是target
			{
				player.i = goal_i;                                                         // 玩家移动到目标位置
				player.j = goal_j;
				level[goal_i][goal_j] = 't';                                               // 目标位置变成target
				level[goalNext_i][goalNext_j] = 'a';                                       // 再前面变成achieved
			}
			else
			{
				return;
			}
		}
		achievedNum = 0;
		int i, j;
		for (i = 0; i < B_NUM; i++)
		{
			for (j = 0; j < B_NUM; j++)
			{
				if (level[i][j] == 'a')
				{
					achievedNum++;
				}
			}
		}
		
		if (achievedNum == targetNum)
		{
			show();																		   // 调用显示游戏胜利画面
			if (currentLevelNum < LEVEL_TOTALNUM - 1)
			{
				currentLevelNum++;
				startup();                                                                 // 开始下一关的初始化
			}
		}
	}
}

int main()
{
	startup();
	while (1)
	{
		show();
		update();
	}
	return 0;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值