C++ 推箱子,中配版,支持玩家自己创造地图(无图形库)

游戏功能介绍:

1、玩家上下左右移动的正确实现。
2、玩家推箱子,箱子位置的变化,以及玩家是否能够推动的判断。
3、胜利检测。(判断游戏是否胜利)
4、选关模块,方便玩家选择自己想要游玩的关卡进行游玩。
5、游戏介绍模块。
6、游戏目录模块。

郑重声明:

1、该游戏代码属于原创。尤其是其中的文件检索功能,应该在目前网络上,还未出现过和我一样的关卡文件参数格式。
2、采用类和对象的方式来书写,可以供学习面向对象的朋友,提高oo思想。
3、希望看见这篇博客的朋友能够充分学习其中的知识点,提升自己,同时也欢迎各位朋友在下方评论区评论以及下来的讨论。
4、代码刚刚写完,而且制作的第3关和第4关,是引用到网上的推箱子的地图,作者因为实力有限而没有在第3关和第4关获胜,所以若有bug,请朋友们指正。

下载链接:

1、游戏下载链接:点击蓝色字前往下载
2、源代码下载链接:点击蓝色字前往下载

注意:
1、游戏下载需要三个积分。
2、源代码下载还是老规矩。(粉丝即可)

效果视频展示:

展示

地图制作方式 以及 地图添加方式:

地图制作方式:

地图制作

地图添加方式:

游戏组建添加方式(推箱子游戏)

代码分析:

1、宏定义:

#define SIZE 10		//宏意义地图大小。
#define NUMBER 10	//宏定义最大箱子数目。(可以根据玩家自己制作的地图,而修改最大箱子数。)(个人认为10个箱子应该够了。)

2、创建类和对象:

这里面创建了两个内,一个是箱子的类,另一个是玩家的类。

这两个类里面都会分别记录对象的横纵坐标。

玩家这个类里面有两个行为,一个是移动,另一个是推箱子。
箱子这个类里面有一个行为,那就是移动。

特别声明,箱子的类必须定义在玩家类的前面,因为在玩家推的行为里面会涉及到箱子的移动行为。

class Box
{
     
public:
	int placeI;	//记录箱子的横坐标。
	int placeJ;	//记录箱子的纵坐标。

	Box();		//该类的构造器。

	int move( int command );//箱子的移动行为。
};

class Player
{
     
public:
	int placeI;		//记录玩家横坐标
	int placeJ;		//记录玩家纵坐标

	Player();		//该类的构造器。

	int move( int command );	//玩家的移动行为。
	int push( int command , Box box[] , int );		//玩家的推箱子行为。
};


//玩家类的构造器。
Player::Player()
{
     
	//玩家的位置为(-1,-1),由于这个点在地图上并不存在,所以在运行过程中,若玩家的位置依旧为(-1,-1),那么可以表示文件读取错误。
	placeI = -1;
	placeJ = -1;
}

//箱子类的构造器。
Box::Box()
{
     
	//和玩家的构造器一样。
	placeI = -1;
	placeJ = -1;
}

int Player::move( int command ) 
{
     
	switch ( command )
	{
     
	//表示玩家位置向上移动一格。
	case 1:
		placeI -= 1;
		break;

	//表示玩家位置向左移动一格。
	case 2:
		placeJ -= 1;
		break;

	//表示玩家位置向下移动一格。
	case 3:
		placeI += 1;
		break;

	//表示玩家位置向右移动一格。
	case 4:
		placeJ += 1;

	default:
		break;
	}

	return 0;
}

int Player::push( int command , Box box[] , int I )
{
     
	int tempI = placeI , tempJ = placeJ;//让新产生的这两个变量指向玩家所站的地方。(这两个变量一起看的话是一个坐标。)

	//将该坐标向玩家将要移动的方向移动一格。
	switch ( command )
	{
     
	case 1:
		tempI -= 1;
		break;

	case 2:
		tempJ -= 1;
		break;
	
	case 3:
		tempI += 1;
		break;
	
	case 4:
		tempJ += 1;
		break;

	default:
		break;
	}

	//检测该坐标指向的方块是哪一个箱子。
	int temp = 0;//用于检索box里面的每个元素。
	while( temp < I )
	{
     
		if( box[temp].placeI == tempI && box[temp].placeJ == tempJ )//如果是该箱子。
		{
     
			box[temp].move( command );//调用箱子移动的行为。
			break;
		}
		temp++;
	}

	move( command );//玩家移动。

	return 0;
}

int Box::move( int command )
{
     
	//和玩家到移动行为一样。
	switch ( command )
	{
     
	case 1:
		placeI -= 1;
		break;

	case 2:
		placeJ -= 1;
		break;
	
	case 3:
		placeI += 1;
		break;
	
	case 4:
		placeJ += 1;
		break;

	default:
		break;
	}

	return 0;
}

声明所需的子函数:

子函数有点多,希望朋友们不要畏惧困难。
相反,我们要迎难而上。

gettingData()子函数,是用于检索地图并且加载到内存中的一个函数。
在视频中已经讲到了,我们的程序是可以允许玩家自行创造地图的,所以我们必须要从程序外部将地图读取进来。

printMap()是一个打印函数,这毋庸置疑。

getCommand()用于接收玩家在键盘中的输入。

void detect()检测玩家将要走向方向的前两个方块的内容。

int menu()主菜单函数。

int levelChoose()选关函数。

voluationForStar()初始化中点坐标函数。

weatherPass()判断是否胜利函数。

int gettingData( int smallSquare[SIZE][SIZE] , int number , Player *player , Box box[NUMBER] ,  

int *I , int star[NUMBER][2] );		//用于检索游戏数据

void printMap( int smallSquare[SIZE][SIZE] , Player player , Box box[NUMBER] , int I);	//打印地图

int getCommand();//接收用户输入。

void detect( Player *player , Box box[] , int smallSquare[SIZE][SIZE] ,int *pointerFirst , int *pointerSecond , int command , int I );		//用于探测玩家指向方向前两块区域的内容。

int menu( );		//创建菜单函数

int levelChoose( );//关卡选择

void introduce();	//游戏介绍

void voluationForStar( int star[NUMBER][2] );

int weatherPass( int star[NUMBER][2] , Box box[NUMBER] , int );

检索游戏地图函数:

这个涉及 C++的文件操作。
该函数对于有兴趣制作地图的朋友来说,建议好好学习。

int gettingData( int smallSquare[SIZE][SIZE] ,int number , Player *player , Box box[NUMBER] , int *I , int star[NUMBER][2] )
{
     
	char gameLevel[12] = {
      'N' , 'u' , 'm' , 'b' , 'e' , 'r' ,0 , '.' , 't' , 'x' , 't' , 0 };		//用于检索游戏数据。
	gameLevel[6] = number + 48 ;			//整型转化为字符型,需要统一一下。


	std::ifstream fileOfGettingGameData;

	fileOfGettingGameData.open( gameLevel );	//打开游戏配置

	if( !fileOfGettingGameData )
	{
     
		system("cls");
		std::cout << "未检测到关卡" << number << "的存在" << "\n请检查您在选择关卡时是否输入正确。" << std::endl;
		system("pause");
		return 0;
	}
	/*
	else
	{
		std::cout << "关卡初始化成功" << std::endl; 
		Sleep( 500 );
	}
	*/

	//初始化地图,(检索游戏文件)
	int tempI , tempJ , temp = 0 ;//temp用于终点的计数。
	for( tempI = 0 ; tempI < SIZE ; tempI++ )
	{
     
		for( tempJ = 0 ; tempJ < SIZE ; tempJ++ )
		{
     
			fileOfGettingGameData >> smallSquare[tempI][tempJ];

			//人物位置单独计算
			if( 3 == smallSquare[tempI][tempJ] )
			{
     
				player->placeI = tempI;
				player->placeJ = tempJ;

				smallSquare[tempI][tempJ] = 2 ;
			}

			//箱子位置单独计算
			if( 1 == smallSquare[tempI][tempJ] )
			{
     
				box[ (*I) ].placeI = tempI;
				box[ (*I) ].placeJ = tempJ;

				smallSquare[tempI][tempJ] = 2 ;

				(*I)++;
			}

			//终点位置单独计算
			if( 4 == smallSquare[tempI][tempJ] )
			{
     
				star[temp][0] = tempI;
				star[temp][1] = tempJ;

				temp++;
			}
		}
	}

	return 1;
}

打印地图函数:

system(“cls”);
该语句可以实现win32的清屏。
在函数定义在windows.h头文件中。

void printMap( int smallSquare[SIZE][SIZE] , Player player , Box box[NUMBER] , int I )
{
     
	int tempI , tempJ , temp ;//temp用于检索数列box
	int weatherPrintBox = 0;	//0表示没有打印箱子,1表示打印了。

	system("cls");
	printf(" 0 1 2 3 4 5 6 7 8 9\n");
	for( tempI = 0 ; tempI < SIZE ; tempI++ )
	{
     
		printf("%d" , tempI);
		for( tempJ = 0 ; tempJ < SIZE ; tempJ++ )
		{
     
			weatherPrintBox = 0;
			for( temp = 0 ; temp < I ; temp++ )
			{
     
				if( tempI == box[temp].placeI && tempJ == box[temp].placeJ )
				{
     
					printf("■");			//打印箱子。
					weatherPrintBox = 1;
					break;
				}
			}

			if( tempI == player.placeI && tempJ == player.placeJ )
			{
     
				std::cout << "▼";				//打印人物。
			}
			else if( 2 == smallSquare[tempI][tempJ] && 0 == weatherPrintBox )//&&运算符后面的语句表示:如果打印了箱子就不必再打印箱子下面的道路。
			{
     
				std::cout << "  ";				//打印道路。
			}
			else if( 0 == smallSquare[tempI][tempJ] )
			{
     
				std::cout << "▓";				//打印墙壁。
			}
			else if( 4 == smallSquare[tempI][tempJ] && 0 == weatherPrintBox )//&&运算符后面的语句表示:如果打印了箱子就不必再打印箱子下面的终点。
			{
     
				std::cout << "☆";				//打印终点。
			}
		}
		std::cout << "\n";
	}
}

获取玩家指令的函数:

_getch();
这个函数定义在conio.h
如果有朋友知道,其他的表示方式,比如说:cin.get();也是可以相互替换的。

int getCommand()
{
     
	int commandReturn = -1;		//表示没有输入

	switch( _getch() )
	{
     
	case 'W':
	case 'w':
		commandReturn = 1;		//表示向上
		break;

	case 'A':
	case 'a':
		commandReturn = 2;		//表示向左
		break;

	case 'S':
	case 's':
		commandReturn = 3;		//表示向下
		break;

	case 'D':
	case 'd':
		commandReturn = 4;		//表示向右
		break;

	case 'B':
	case 'b':
		commandReturn = 5;		//表示返回选关界面
		break;

	case 10 :
	case 13 :
		commandReturn = 6;		//表示回车
		break;

	default:
		commandReturn = -2;		//表示用户错误输入
		break;
	}

	return ( commandReturn );
}

探测玩家指向方向前两格方块内容的函数:

这是代码里面比较复杂的一块。
不讲了。

void detect( Player *player , Box box[] , int smallSquare[SIZE][SIZE] , int *pointerFirst , int *pointerSecond , int command , int I )
{
     
	int tempFirstI , tempFirstJ , tempSecondI , tempSecondJ ;		//用于记录玩家位置的前两个位置的坐标。
	int temp;		//用于检索box里的每一个元素。

	switch ( command )
	{
     
	case 1:
		//计算玩家朝向的两个方向的方块的坐标,并且将其赋值。
		tempFirstI = player->placeI - 1;
		tempFirstJ = player->placeJ;

		tempSecondI = player->placeI - 2;
		tempSecondJ = player->placeJ;

		if( tempFirstI >= 0 && tempFirstJ >= 0 )		//检索范围未超出地图的情况。
		{
     
			*pointerFirst = smallSquare [tempFirstI][tempFirstJ];
		}
		//范围超出地图的区块,将其按墙壁计算。
		else
		{
     
			*pointerFirst = 0;
		}

		if( tempSecondI >= 0 && tempSecondJ >= 0 )		//检索范围未超出地图的情况。
		{
     
			*pointerSecond = smallSquare[tempSecondI][tempSecondJ];
		}
		//范围超出地图的区块,将其按墙壁计算。
		else
		{
     
			*pointerSecond = 0;
		}

		//检索玩家所指向方向的前两个方块是否为箱子
		for( temp = 0 ; temp < I ; temp++ )
		{
     
			if( box[temp].placeI == tempFirstI && box[temp].placeJ == tempFirstJ )
			{
     
				*pointerFirst = 1;
			}
			else if( box[temp].placeI == tempSecondI && box[temp].placeJ == tempSecondJ )
			{
     
				*pointerSecond = 1;
			}
		}
	break;
/************其后的注释与上面保持一样,请朋友们融会贯通。(写长一点,给自己营造一种感觉,自己都写了那么多行了。嘘~~~~~~~~)*****************************/
	case 2:
		tempFirstI = player->placeI;
		tempFirstJ = player->placeJ - 1;

		tempSecondI = player->placeI;
		tempSecondJ = player->placeJ - 2;

		if( tempFirstI >= 0 && tempFirstJ >= 0 )		//检索范围未超出地图的情况。
		{
     
			*pointerFirst = smallSquare [tempFirstI][tempFirstJ];
		}
		//范围超出地图的区块,将其按墙壁计算。
		else
		{
     
			*pointerFirst = 0;
		}

		if( tempSecondI >= 0 && tempSecondJ >= 0 )
		{
     
			*pointerSecond = smallSquare[tempSecondI][tempSecondJ];
		}
		else
		{
     
			*pointerSecond = 0;
		}

		for( temp = 0 ; temp < I ; temp++ )
		{
     
			if( box[temp].placeI == tempFirstI && box[temp].placeJ == tempFirstJ )
			{
     
				*pointerFirst = 1;
			}
			else if( box[temp].placeI == tempSecondI && box[temp].placeJ == tempSecondJ )
			{
     
				*pointerSecond = 1;
			}
		}
	break;
	
	case 3:
		tempFirstI = player->placeI + 1;
		tempFirstJ = player->placeJ;

		tempSecondI = player->placeI + 2;
		tempSecondJ = player->placeJ;

		if( tempFirstI >= 0 && tempFirstJ >= 0 )		//检索范围未超出地图的情况。
		{
     
			*pointerFirst = smallSquare [tempFirstI][tempFirstJ];
		}
		//范围超出地图的区块,将其按墙壁计算。
		else
		{
     
			*pointerFirst = 0;
		}

		if( tempSecondI >= 0 && tempSecondJ >= 0 )
		{
     
			*pointerSecond = smallSquare[tempSecondI][tempSecondJ];
		}
		else
		{
     
			*pointerSecond = 0;
		}

		for( temp = 0 ; temp < I ; temp++ )
		{
     
			if( box[temp].placeI == tempFirstI && box[temp].placeJ == tempFirstJ )
			{
     
				*pointerFirst = 1;
			}
			else if( box[temp].placeI == tempSecondI && box[temp].placeJ == tempSecondJ )
			{
     
				*pointerSecond = 1;
			}
		}
	break;
	
	case 4:
		tempFirstI = player->placeI;
		tempFirstJ = player->placeJ + 1;

		tempSecondI = player->placeI;
		tempSecondJ = player->placeJ + 2;

		if( tempFirstI >= 0 && tempFirstJ >= 0 )		//检索范围未超出地图的情况。
		{
     
			*pointerFirst = smallSquare [tempFirstI][tempFirstJ];
		}
		//范围超出地图的区块,将其按墙壁计算。
		else
		{
     
			*pointerFirst = 0;
		}

		if( tempSecondI >= 0 && tempSecondJ >= 0 )
		{
     
			*pointerSecond = smallSquare[tempSecondI][tempSecondJ];
		}
		else
		{
     
			*pointerSecond = 0;
		}

		for( temp = 0 ; temp < I ; temp++ )
		{
     
			if( box[temp].placeI == tempFirstI && box[temp].placeJ == tempFirstJ )
			{
     
				*pointerFirst = 1;
			}
			else if( box[temp].placeI == tempSecondI && box[temp].placeJ == tempSecondJ )
			{
     
				*pointerSecond = 1;
			}
		}
	break;

	default:
		break;
	}
}

还有几个函数,我并没有在这里展示,不过有兴趣的朋友可以下载源代码好好学习。
主要是博主实在写不动了。
你呢,还希望大家能够通过我的代码,不断提高自己的编程能力,不断的提升。
游戏和源码的下载链接在上面,有大家可以下载下来反复学习哟。

其次看在小博主我写了这么多的份上,你不点个赞再走吗?

你可能感兴趣的:(c++,游戏)