C++で開発を行う上でSTLは非常に強力な機能です。
今回は実際のゲーム開発におけるSTLの使用例を説明します。
STLとは
C++言語に標準で付属しているクラスライブラリの一つで、C++言語によるプログラミングで頻繁に用いられる汎用的なデータ構造やアルゴリズムをまとめたものである。
※IT用語辞典 Weblio辞書より抜粋
今回は自分がゲーム開発でよく使う項目を抜粋して紹介します。
- array
- vector
- list
- unordered_map
- unordered_set
array
固定長配列を提供します。
サイズを取得することもできるため、通常の配列を使うよりは使いやすい。
array.cpp
# include <iostream>
# include <array>
int main()
{
std::array<int, 5> n = { 5, 3, 1, 4, 7 };
for (size_t i = 0; i < n.size(); ++i) {
std::cout << n[i] << std::endl;
}
for (auto i : n) {
std::cout << i << std::endl;
}
for (auto it = std::begin(n); it != std::end(n); ++it) {
std::cout << *it << std::endl;
}
}
vector
可変長配列を提供します。
連続された領域を約束されており、ゲームではバイナリファイルやテキストを行単位で読み込みなどで使うことが多い。
・得意
ランダムアクセスが高速
事前に領域を確保してあるのであれば後方追加が高速
・不得意
前方挿入、途中挿入のコストが大きい
使用例
vector.cpp
# include <cstdio>
# include <cstring>
# include <vector>
# include <algorithm>
class File
{
public :
// バイナリファイルを読み込む
static std::vector<char> ReadAllBytes(const std::string& path)
{
FILE* fp = std::fopen(path.c_str(), "rb");
if (fp == nullptr) return std::vector<char>();
std::fseek(fp, 0, SEEK_END);
std::vector<char> buffer(ftell(fp));
std::fseek(fp, 0, SEEK_SET);
std::fread(&buffer[0], sizeof(char), buffer.size(), fp);
std::fclose(fp);
return buffer;
}
// テキストファイルを読み込む
static std::string ReadAllText(const std::string& path)
{
FILE* fp = std::fopen(path.c_str(), "rb");
if (fp == nullptr) return std::string();
std::fseek(fp, 0, SEEK_END);
std::string ret;
size_t length = ftell(fp);
ret.resize(ftell(fp));
std::fseek(fp, 0, SEEK_SET);
std::fread(&ret[0], sizeof(char), ret.length(), fp);
std::fclose(fp);
return ret;
}
// テキストファイルをライン分解して読み込む
static std::vector<std::string> ReadAllLines(const std::string& path)
{
// ファイル読み込み
auto str = File::ReadAllText(path);
if (str.empty()) return std::vector<std::string>();
// 改行数を取得
size_t count = std::count(std::begin(str), std::end(str), '\n');
if (str[str.length() - 1] != '\n') ++count;
// 戻す変数を設定
std::vector<std::string> ret;
ret.reserve(count);
// 環境によって改行コード変更
static const std::string token = "\r\n";
// 分解処理
const char* src = str.c_str();
const char* end = str.c_str() + str.length();
while (src < end) {
const char* pp = std::strstr(src, token.c_str());
if (pp == nullptr) {
ret.push_back(std::string(src, end));
break;
}
ret.push_back(std::string(src, pp - src));
src = pp + token.length();
}
return ret;
}
};
int main()
{
auto png_buffer = File::ReadAllBytes("test.png");
auto text= File::ReadAllText("test.txt");
auto lines = File::ReadAllLines("test.txt");
}
list
双方向リストを提供します。
シューティングで敵や弾の管理など、追加、削除が多々行われる内容に適しています。
・得意
追加、削除
・不得意
ランダムアクセスが苦手
使用例
list.cpp
# include <list>
/*!
* @brief 弾
*/
class Bullet
{
public :
/*!
* @brief 種類
*/
enum class Type
{
};
/*!
* @brief ヒットの種類
*/
enum class HitType
{
Player,
Enemy,
};
Bullet(Type type, HitType hit_type, int x, int y)
: type_(type), hit_type_(hit_type), x_(x), y_(y)
{
}
bool Update()
{
// タイプによって飛ばし方を変更
// 弾を削除した場合はfalseを返す
if (end_) {
return false;
}
return true;
}
private :
Type type_;
HitType hit_type_;
int x_;
int y_;
bool end_ = false;
};
/*!
* @brief 弾のマネージャー
*/
class BulletManager
{
public :
/*!
* @brief 弾の作成
*/
void Create(Bullet::Type type, Bullet::HitType hit_type, int x, int y)
{
Bullet bullet(type, hit_type, x, y);
bullet_.push_back(bullet);
}
/*!
* @brief 更新
*/
void Update()
{
// 更新し、弾が削除された場合はリストから消す
for (auto it = std::begin(bullet_); it != std::end(bullet_);) {
if (!it->Update()) {
it = bullet_.erase(it);
} else {
++it;
}
}
}
private :
std::list<Bullet> bullet_;
};