i.说明:
其实这个游戏做了很多遍了,第一次是用EasyX,第二次是学完GitHub上的learnOpenGL,第三次是在unity上,看了B站上unity的教程在unity上做了之后给我的感触很大,以前真是有什么来什么,现在看了游戏的教程才略懂循序渐进,现在是第四次,还是用opengl,做的就比较合理、简明了。
做这个游戏算是对learnOpenGL教程的一个复习吧,并且融合了学习unity后的一些巧妙的思想。unity提供给我们游戏的框架,也就是把那些可复用的核心代码封装到无数类中供我们使用,我们自己从底层做也是这样。所以有关渲染的内容,如着色器、纹理的配置、使用均封装在单独的类和方法中,只需要调用几个方法就能轻松的使用。有关渲染的类可以看通过消砖块的游戏对OpenGL(glfw)、图形学、游戏编程的一个小的总结这篇文章中的具体解释。本文主要记录游戏功能实现的细节以及从unity中学到的一些思想。
素材及源码:素材
链接:https://pan.baidu.com/s/1GpWYE7zEhBZnxE_5qOV_Ow
提取码:lbfn
ii.游戏相关的主要功能实现:
1.游戏关卡(地图)的设定、加载:
所谓游戏关卡,在unity中就是不同的场景,对自己做就是渲染不同的精灵。
这里用了两种方式: 从文件中加载和随机生成。(用1、2、3、4.。。。代表对应元素)
(1)从文件中读取需要自己去想怎么设置各种元素个数,方便控制:
void GameLevel::Load(const char *file, unsigned int levelWidth, unsigned int levelHeight)
{
// clear old data
this->Bricks.clear();
// load from file
unsigned int tileCode;
GameLevel level;
std::string line;
std::ifstream fstream(file);
std::vector<std::vector<unsigned int>> tileData;
if (fstream)
{
while (std::getline(fstream, line)) // read each line from level file
{
std::istringstream sstream(line);
std::vector<unsigned int> row;
while (sstream >> tileCode) // read each word seperated by spaces
row.push_back(tileCode);
tileData.push_back(row);
}
if (tileData.size() > 0)
this->init(tileData, levelWidth, levelHeight);
}
}
(2)随机生成可以节省我们自己去想地图的时间,这也是我从unity做坦克大战时的做法:也很合理
void GameLevel::Load(unsigned int levelWidth, unsigned int levelHeight)
{
// clear old data
this->Bricks.clear();
int tileData[12][16];//16*12大小的地图
srand((unsigned)time(NULL));
unsigned int type,tmp;
//最外围不能有元素,故0和15不会出现
//先是不涉及基地的前9层
for(int i=1;i<9;i++){
for(int j=1;j<15;j++){
tmp=rand()%16;//16种可能(0-15)控制不同元素出现的概率
if(tmp==13) type=1;//13为砖墙(1)
else if(tmp==14) type=3;//14为水(3)
else if(tmp==15) type=4;//15为草(4)
else if(tmp>=6&&tmp<=12) type=0;//6-12没东西(5)
else type=2;//0-5为土墙(2)
tileData[i][j]=type;
}
}
//基地的9-11层:中间为基地,基地周围一圈土墙
for(int i=9;i<11;i++){
for(int j=1;j<15;j++){
if(i==9&&j>=6&&j<10){
//基地上方土墙
type=2;
}else{
tmp=rand()%16;//(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15;0-4代表土墙,5-12为地面,13,14,15为砖墙、水、草)
if(tmp==13) type=1;
else if(tmp==14) type=3;
else if(tmp==15) type=4;
else if(tmp>=5&&tmp<=12) type=0;
else type=2;
}
tileData[i][j]=type;
}
}
//基地左右两侧的土墙
tileData[10][9]=2;
tileData[10][6]=2;
tileData[11][9]=2;
tileData[11][6]=2;
this->init(tileData, levelWidth, levelHeight);
}
2.玩家运动:
显然按上下左右玩家不仅要向对应方向移动,还要朝向那个方向,这里还是从unity中得到的灵感,我们有两种方式控制:旋转物体或者改变物体的纹理(上下左右四幅)。如果是我们自己实现这一切的话,显然改变物体的纹理更为容易,旋转还要考虑当前朝向,不可取。此外还有不
// 移动坦克
if (this->Keys[GLFW_KEY_A])
{
PlayerTank->Sprite=ResourceManager::GetTexture("p1tankL");
PlayerTank->Velocity=glm::vec2(-TANK_VELOCITY,0);
PlayerTank->Move(dt,this->Width,this->Height);
PlayerTank->dir=L;
}
if (this->Keys[GLFW_KEY_D])
{
PlayerTank->Sprite=ResourceManager::GetTexture("p1tankR");
PlayerTank->Velocity=glm::vec2(TANK_VELOCITY,0);
PlayerTank->Move(dt,this->Width,this->Height);
PlayerTank->dir=R;
}
if (this->Keys[GLFW_KEY_W])
{
PlayerTank->Sprite=ResourceManager::GetTexture("p1tankU");
PlayerTank->Velocity=glm