C++一直都是游戏开发的主流语言,特别是游戏引擎的开发。学了半年C++,网上找了一些C++推箱子的小游戏,涉及到的知识面太广,从容器到BFS等等算法,尤其是推多个箱子的时候。本人水平有限,只做到了固定地图,并且只有一个箱子可以推。能基本的实现后退,重新开始,上下关卡切换的功能。因为大部分游戏都是在Windows C++平台下编写运行的,很少有linux下也可以运行的推箱子游戏。
//Sokoban.h
class Sokoban
{
private:
enum {L = 21,H = 10};
int Pex,Pey,OrgPex,OrgPey,OldPex,OldPey;//人的实时位置坐标,上一步人的坐标,人的初始位置坐标
int Boxx,Boxy,OrgBoxx,OrgBoxy,OldBoxx,OldBoxy;//箱子的实时位置,上一步箱子的初始位置坐标,箱子的初始位置坐标
int Tx,Ty; //目标位置的坐标
int Succeed; //判断成功的标志位
int dx[4],dy[4];//方向数组
int dir;
public:
Sokoban();//构造函数
~Sokoban();//析构函数
void Initial();//地图初始化函数
void Show();//地图刷新函数
void Button();//按键判断函数
void Move();//箱子移动函数
bool Check(int x,int y);//验证越界函数
void Rest();//重新开始
};
//Sokoban.cpp
#include"Sokoban.h"
int level = 1;//游戏关卡
char map[10][22]={"0"};//用来时时更新地图
char oldmap[10][22]={"0"};//前一步的地图
char orgmap[10][22]={"0"};//最初的地图
void OldMap()
{
int i = 0,j = 0;
for(i = 0;i<10;i++)
{
for(j = 0;j<21;j++)
{
oldmap[i][j] = map[i][j];
}
}
}
void OrgMap()
{
int i = 0,j = 0;
for(i = 0;i<10;i++)
{
for(j = 0;j<21;j++)
{
orgmap[i][j] = map[i][j];
}
}
}
void Back()
{
int i = 0,j = 0;
for(i = 0;i<10;i++)
{
for(j = 0;j<21;j++)
{
map[i][j] = oldmap[i][j];
}
}
}
void Sokoban::Rest()
{
int i = 0,j = 0;
for(i = 0;i<10;i++)
{
for(j = 0;j<21;j++)
{
map[i][j] = orgmap[i][j];
}
}
Pex = OrgPex;//最初的地图恢复
Pey = OrgPey;
Boxx = OrgBoxx;
Boxy = OrgBoxy;
}
Sokoban::Sokoban()
{
dir = -1;
Succeed = 0;
//0~3分别代表上右下左
dx[0] = -1; dx[1] = 0; dx[2] = 1; dx[3] = 0;
dy[0] = 0; dy[1] = 1; dy[2] = 0; dy[3] = -1;
}
Sokoban::~Sokoban()
{
}
void Sokoban::Initial()
{
char a[10][22]={//123456789012345678901
/*1*/ "#####################",
/*2*/ "#X # # # #",
/*3*/ "## # ###### O #",
/*4*/ "## # # ##### #",
/*5*/ "## # # ##### ###### #",
/*6*/ "## # # T #",
/*7*/ "#### # # ######## #",
/*8*/ "## # # # #",
/*9*/ "## # # #",
/*10*/ "#####################"
};
char b[10][22]={//123456789012345678901
/*1*/ "#####################",
/*2*/ "#X # #",
/*3*/ "## # ############## #",
/*4*/ "# # # # # # # #",
/*5*/ "# # ## # ##",
/*6*/ "# # # # #",
/*7*/ "# ## # # ####### O #",
/*8*/ "# T # # # #",
/*9*/ "## # # #",
/*10*/ "#####################"
};
char c[10][22]={//123456789012345678901
/*1*/ "#####################",
/*2*/ "# # # # #",
/*3*/ "## # # ##### #",
/*4*/ "# # # O ## # # #",
/*5*/ "## # ####### ## # #",
/*6*/ "# X #",
/*7*/ "#### ## ## ## ## ###",
/*8*/ "## # # # # #",
/*9*/ "##T #",
/*10*/ "#####################"
};
char d[10][22]={//123456789012345678901
/*1*/ "X# ################",
/*2*/ " # # # #",
/*3*/ " # # # ###### # #",
/*4*/ " # # # ## # # #",
/*5*/ " # # ####### ## # #",
/*6*/ " # #",
/*7*/ " # # ## ##### ## # #",
/*8*/ " # # # # # O# #",
/*9*/ " #T# #",
/*10*/ " ##################"
};
char e[10][22]={//123456789012345678901
/*1*/ "#####################",
/*2*/ " # # # #",
/*3*/ " ##### #",
/*4*/ " # # # ## # # #",
/*5*/ " # ### ## ## ## # #",
/*6*/ " # #",
/*7*/ " ##### # ## ## ###",
/*8*/ " # # # # # # O #",
/*9*/ "T# #",
/*10*/ "X####################"
};
/********************地图初始化**************/
char (*pc)[22] = a;
switch(level)
{
case 0: break;
case 1: pc = a;break;
case 2: pc = b;break;
case 3: pc = c;break;
case 4: pc = d;break;
case 5: pc = e;break;
}
/**********************人,箱子,目标位置的初始化****************/
int i = 0,j = 0;
for(;i<10;i++)
{
for(j = 0;j<21;j++)
{
map[i][j] = pc[i][j];
if(map[i][j] == 'X')
{
OrgPex = Pex = i;
OrgPey = Pey = j;
}
else if(map[i][j] == 'O')
{
OrgBoxx = Boxx = i;
OrgBoxy = Boxy = j;
}
else if(map[i][j] == 'T')
{
Tx = i;
Ty = j;
}
}
}
OrgMap();//最初的地图
Sokoban::Show();
}
void Sokoban::Show()
{
int i = 0,j = 0;
int key = 0;
while(true)
{
usleep(500000);//刷新
Sokoban::Button();
Sokoban::Move();
system("clear");
//打印地图
for(i = 0;i<10;i++)
{
for(j = 0;j<21;j++)
{
printf("%c",map[i][j]);
}
cout<//画面
cout<cout << " _________________________________" << endl;
cout << "| C++语言推箱子游戏 (共5关) |" << endl;
cout << "| 游戏规则: 当前关卡: "<" |" << endl;
cout << "| X:人 O:箱子 |" << endl;
cout << "| #:障碍物 T:目的地 |" << endl;
cout << "| 操作说明: Q:退出 | " << endl;
cout << "| W:上 S:下 |"<cout << "| D:右 A:左 |"<cout << "| R:重新开始 B:后退 |"<cout << "| U:上张地图 N:下张地图 |"<cout << "|————————————————————————————————|" << endl;
cout << "| 友请提示: |" << endl;
cout << "| 每次移动过后只能后退一次! |" << endl;
cout << "| 人将箱子推到目的地即过关! |" << endl;
cout << "| 输入按键后按一次回车才能生效! |" << endl;
cout << "|————————————————————————————————|" << endl;
//箱子成功到达目的地
if(Succeed)
{
if(level == 5)
{
cout<<"太厉害了!!!你差点就和游戏开发者一样聪明了"<break;
}
else if(level == 0)
{
sleep(1);
break;
}
else
{
cout<<" 恭喜你啊,智商可以啊,有本是来通关阿!"<cout<<"输入Q回车退出游戏,输入任意键回车等待2秒即可开启下一关卡"<1);
if(kbhit() != 0)
{
while(kbhit() != 0)
key = getchar();
if(key == 113)//按q
break;
else
{
usleep(500000);
Succeed = 0;
level++;//等级+1,地图就会更新
Sokoban::Initial();
}
}
}
}
}
}
void Sokoban::Button()
{
int key = 0;
if(kbhit() != 0) //检查当前是否有键盘输入,若有则返回一个非0值,否则返回0
{
while(kbhit() != 0) //可能存在多个按键,要全部取完,以最后一个为主
key = getchar(); //将按键从控制台中取出并保存到key中
switch(key)
{
//上
case 119: dir = 0;
break;
//右
case 100: dir = 1;
break;
//下
case 115: dir = 2;
break;
//左
case 97: dir = 3;
break;
//后退
case 98: {
Back();
Pex = OldPex;
Pey = OldPey;
Boxx = OldBoxx;
Boxy = OldBoxy;
break;
}
//重新开始
case 114: {
Rest();
break;
}
//切换到下张图
case 110:{
level++;
if(level == 6)
level = 1;
Sokoban::Initial();
break;
}
//切换到上张图
case 117:{
level--;
if(level == 0)
level = 5;
Sokoban::Initial();
break;
}
//退出
case 113:
{
level = 0;
Succeed = 1;
break;
}
}
}
}
//人推箱子移动函数
void Sokoban::Move()
{
int x,y;
//有按键的时候
if(dir != -1)
{
OldPex = Pex;
OldPey = Pey;
OldBoxx = Boxx;
OldBoxy = Boxy;
//人所推向的坐标位置
x = Pex + dx[dir];
y = Pey + dy[dir];
//1.人所推位置为空,就走向该位置,并且人初始位置不在目标位置上
if(Check(x,y) && map[x][y] == ' ' && (Pex != Tx || Pey != Ty))
{
OldMap();
map[Pex][Pey] = ' ';
map[x][y] = 'X';
Pex = x;
Pey = y;
dir = -1;
}
else //2.人所推的位置是箱子,就将箱子移向人走的方向这点,并且初始位置不在目标位置上
if(Check(x,y) && map[x][y] == 'O' && Check( x+dx[dir],y+dy[dir] ) && map[ x+dx[dir] ][ y+dy[dir] ] == ' ' && (Pex != Tx || Pey != Ty))
{
OldMap();
map[Pex][Pey] = ' '; //人的位置改变
Pex = x;
Pey = y;
map[Boxx][Boxy] = 'X'; //箱子位置改变,且人移动到箱子的位置
map[x+dx[dir]][y+dy[dir]] = 'O';//箱子移动的位置
Boxx = x + dx[dir];
Boxy = y + dy[dir];
dir = -1;
}
else //3.人将箱子推向的方向刚好是目的地
if(Check(x,y) && map[x][y] == 'O' && Check( x+dx[dir],y+dy[dir] ) && map[ x+dx[dir] ][ y+dy[dir] ] == 'T')
{
OldMap();
map[Pex][Pey] = ' '; //人的移动前位置改变
Pex = x;
Pey = y;
map[Boxx][Boxy] = 'X'; //箱子位置改变,且人移动到箱子的位置
map[x+dx[dir]][y+dy[dir]] = 'O';//箱子移动的位置
Boxx = x + dx[dir];
Boxy = y + dy[dir];
dir = -1;
Succeed = 1;
}else //4.人走到目标位置
if(Check(x,y) && map[x][y] == 'T')
{
OldMap();
map[Pex][Pey] = ' ';
map[x][y] = 'X';
Pex = x;
Pey = y;
dir = -1;
}else //5.人离开目标位置且推不到箱子
if(Pex == Tx && Pey == Ty && Check(x,y) && map[x][y] == ' ')
{
OldMap();
map[Tx][Ty] = 'T';//人的位置变成目标地址
map[x][y] = 'X'; //人移动后的位置
Pex = x;
Pey = y;
dir = -1;
}
else //6.人离开目标位置且能推到箱子
if(Pex == Tx && Pey == Ty && Check(x,y) && map[x][y] == 'O' && Check( x+dx[dir],y+dy[dir] ) && map[ x+dx[dir] ][ y+dy[dir] ] == ' ')
{
OldMap();
map[Pex][Pey] = 'T';//人的位置变成目标地址
map[x][y] = 'X'; //人的移动后的位置
Pex = x;
Pey = y;
map[Boxx][Boxy] = 'X'; //箱子位置改变,且人移动到箱子的位置
map[x+dx[dir]][y+dy[dir]] = 'O';//箱子移动的位置
Boxx = x + dx[dir];
Boxy = y + dy[dir];
dir = -1;
}
}
}
//判断越界情况
bool Sokoban::Check(int x,int y)
{
if(x < 0 || x >= H || y < 0 || y >= L)
return 0;
else
return 1;
}
//-----------------------------------------------------
//kbhit.h//按键的程序
#ifndef KEY_H
#define KEY_H
#include
#include
#include"kbhit.h"
#include
#include
#include
#include
#include
int kbhit(void);
#endif
//---------------------------------------------
//kbhit.cpp
#include"kbhit.h"
int kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &rdfs);
}
//---------------------------------------------
//main.cpp
#include"Sokoban.h"
using namespace std;
int main()
{
Sokoban s;
s.Initial();
return 0;
}
//运行方式