下面可以继续制作彩色砖块。
砖块是一个游戏对象:持有一些状态数据,举例:位置,大小,速率。持有一些属性:颜色,旋转,是否可被摧毁,2D图片。
每个物体都表示成GameObject类中的一部分。
其头文件如下:
#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H
#include <GL/glew.h>
#include <glm/glm.hpp>
#include "texture.h"
#include "sprite_renderer.h"
// Container object for holding all state relevant for a single
// game object entity. Each object in the game likely needs the
// minimal of state as described within GameObject.
class GameObject
{
public:
// Object state
glm::vec2 Position, Size, Velocity;
glm::vec3 Color;
GLfloat Rotation;
GLboolean IsSolid;
GLboolean Destroyed;
// Render state
Texture2D Sprite;
// Constructor(s)
GameObject();
GameObject(glm::vec2 pos, glm::vec2 size, Texture2D sprite, glm::vec3 color = glm::vec3(1.0f), glm::vec2 velocity = glm::vec2(0.0f, 0.0f));
// Draw sprite
virtual void Draw(SpriteRenderer &renderer);
};
#endif
代码文件如下:
#include "game_object.h"
GameObject::GameObject()
: Position(0, 0), Size(1, 1), Velocity(0.0f), Color(1.0f), Rotation(0.0f), Sprite(), IsSolid(false), Destroyed(false) { }
GameObject::GameObject(glm::vec2 pos, glm::vec2 size, Texture2D sprite, glm::vec3 color, glm::vec2 velocity)
: Position(pos), Size(size), Velocity(velocity), Color(color), Rotation(0.0f), Sprite(sprite), IsSolid(false), Destroyed(false) { }
void GameObject::Draw(SpriteRenderer &renderer)
{
renderer.DrawSprite(this->Sprite, this->Position, this->Size, this->Rotation, this->Color);
}
每个砖块都是GameObject,将它们组成一个集合为:GameLevel类
#ifndef GAMELEVEL_H
#define GAMELEVEL_H
#include <vector>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include "game_object.h"
#include "sprite_renderer.h"
#include "resource_manager.h"
/// GameLevel holds all Tiles as part of a Breakout level and
/// hosts functionality to Load/render levels from the harddisk.
class GameLevel
{
public:
// Level state
std::vector<GameObject> Bricks;
// Constructor
GameLevel() { }
// Loads level from file
void Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight);
// Render level
void Draw(SpriteRenderer &renderer);
// Check if the level is completed (all non-solid tiles are destroyed)
GLboolean IsCompleted();
private:
// Initialize level from tile data
void init(std::vector<std::vector<GLuint>> tileData, GLuint levelWidth, GLuint levelHeight);
};
#endif
因为关卡数据从外部文件加载,需要明确关卡的数据结构,可能在文本文件中的表示形式如下:
1 1 1 1 1 1
2 2 0 0 2 2
3 3 4 4 3 3
0————空白
1————不可被摧毁
2及2以上————不同颜色,可被摧毁
GameLevel类使用所有数字在Load函数中加载到二维容器中,让init函数中处理这些数字,用于创建所有的游戏对象:
void GameLevel::Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight)
{
// 清空过期数据
this->Bricks.clear();
// 从文件中加载
GLuint tileCode;
GameLevel level;
std::string line;
std::ifstream fstream(file);
std::vector<std::vector<GLuint>> tileData;
if (fstream)
{
while (std::getline(fstream, line)) // 读取关卡文件的每一行
{
std::istringstream sstream(line);
std::vector<GLuint> row;
while (sstream >> tileCode) // 读取被空格分隔的每个数字
row.push_back(tileCode);
tileData.push_back(row);
}
if (tileData.size() > 0)
this->init(tileData, levelWidth, levelHeight);
}
}
被加载后的数据tileData被传送到GameLevel类的init函数:
void GameLevel::init(std::vector<std::vector<GLuint>> tileData, GLuint lvlWidth, GLuint lvlHeight)
{
// 计算每个维度的大小
GLuint height = tileData.size();
GLuint width = tileData[0].size();
GLfloat unit_width = lvlWidth / static_cast<GLfloat>(width);
GLfloat unit_height = lvlHeight / height;
// 基于tileDataC初始化关卡
for (GLuint y = 0; y < height; ++y)
{
for (GLuint x = 0; x < width; ++x)
{
// 检查砖块类型
if (tileData[y][x] == 1)
{
glm::vec2 pos(unit_width * x, unit_height * y);
glm::vec2 size(unit_width, unit_height);
GameObject obj(pos, size,
ResourceManager::GetTexture("block_solid"),
glm::vec3(0.8f, 0.8f, 0.7f)
);
obj.IsSolid = GL_TRUE;
this->Bricks.push_back(obj);
}
else if (tileData[y][x] > 1)
{
glm::vec3 color = glm::vec3(1.0f); // 默认为白色
if (tileData[y][x] == 2)
color = glm::vec3(0.2f, 0.6f, 1.0f);
else if (tileData[y][x] == 3)
color = glm::vec3(0.0f, 0.7f, 0.0f);
else if (tileData[y][x] == 4)
color = glm::vec3(0.8f, 0.8f, 0.4f);
else if (tileData[y][x] == 5)
color = glm::vec3(1.0f, 0.5f, 0.0f);
glm::vec2 pos(unit_width * x, unit_height * y);
glm::vec2 size(unit_width, unit_height);
this->Bricks.push_back(
GameObject(pos, size, ResourceManager::GetTexture("block"), color)
);
}
}
}
}
上述代码,遍历每个被加载的数字,处理后将一个对应的GameObject添加到vector容器中。每个砖块的尺寸根据砖块总数被自动计算,便于每块都适应屏幕边界
再加上Draw的实现,GameLevel类的代码文件如下:
#include "game_level.h"
#include <fstream>
#include <sstream>
void GameLevel::Load(const GLchar *file, GLuint levelWidth, GLuint levelHeight)
{
// Clear old data
this->Bricks.clear();
// Load from file
GLuint tileCode;
GameLevel level;
std::string line;
std::ifstream fstream(file);
std::vector<std::vector<GLuint>> tileData;
if (fstream)
{
while (std::getline(fstream, line)) // Read each line from level file
{
std::istringstream sstream(line);
std::vector<GLuint> row;
while (sstream >> tileCode) // Read each word seperated by spaces
row.push_back(tileCode);
tileData.push_back(row);
}