1、玩家上下左右移动的正确实现。
2、玩家推箱子,箱子位置的变化,以及玩家是否能够推动的判断。
3、胜利检测。(判断游戏是否胜利)
4、选关模块,方便玩家选择自己想要游玩的关卡进行游玩。
5、游戏介绍模块。
6、游戏目录模块。
1、该游戏代码属于原创。尤其是其中的文件检索功能,应该在目前网络上,还未出现过和我一样的关卡文件参数格式。
2、采用类和对象的方式来书写,可以供学习面向对象的朋友,提高oo思想。
3、希望看见这篇博客的朋友能够充分学习其中的知识点,提升自己,同时也欢迎各位朋友在下方评论区评论以及下来的讨论。
4、代码刚刚写完,而且制作的第3关和第4关,是引用到网上的推箱子的地图,作者因为实力有限而没有在第3关和第4关获胜,所以若有bug,请朋友们指正。
1、游戏下载链接:点击蓝色字前往下载
2、源代码下载链接:点击蓝色字前往下载
注意:
1、游戏下载需要三个积分。
2、源代码下载还是老规矩。(粉丝即可)
展示
地图制作
游戏组建添加方式(推箱子游戏)
#define SIZE 10 //宏意义地图大小。
#define NUMBER 10 //宏定义最大箱子数目。(可以根据玩家自己制作的地图,而修改最大箱子数。)(个人认为10个箱子应该够了。)
这里面创建了两个内,一个是箱子的类,另一个是玩家的类。
这两个类里面都会分别记录对象的横纵坐标。
玩家这个类里面有两个行为,一个是移动,另一个是推箱子。
箱子这个类里面有一个行为,那就是移动。
特别声明,箱子的类必须定义在玩家类的前面,因为在玩家推的行为里面会涉及到箱子的移动行为。
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;
}
}
还有几个函数,我并没有在这里展示,不过有兴趣的朋友可以下载源代码好好学习。
主要是博主实在写不动了。
你呢,还希望大家能够通过我的代码,不断提高自己的编程能力,不断的提升。
游戏和源码的下载链接在上面,有大家可以下载下来反复学习哟。
其次看在小博主我写了这么多的份上,你不点个赞再走吗?