はじめに
自分の自作ゲームでは、プレイヤー、敵、ステージなどの情報をマネージャーで管理しています。
C++11のstd::mapの使い方を含めた、自分なりの実装方法を紹介します。
マネージャーは一つしかいらないのでシングルトンで管理しています。
Manager.h
#include <map> //std::map
#include <memory> //std::unique_ptr
#include <string> //std::string
class EnemyManager
{
private:
using EnemyMap = std::map<std::string, std::unique_ptr<Enemy>>;
public:
static EnemyManager& Instance()
{
static EnemyManager instance;
return instance;
}
//エネミー登録
void Register(std::unique_ptr<Enemy> enemy, std::string name);
//エネミー取得
Enemy* GetEnemy(std::string name)const { return enemies.at(name).get(); }
//範囲For文で高速取得用
const EnemyMap& GetEnemys()const { return fieldEnemies; }
//更新処理
void Update(float elapsedTime);
private:
EnemyMap enemies;
};
上から解説します。
using EnemyMap は必要ないですが、変数名が長くなったのでまとめました。
std::mapには"IDの名前, 動的に追加するオブジェクト"という使い方をしています。
static EnemyManager& Instance() はシングルトン用
void Register では引数からマップに登録していきます。
Enemy* GetEnemy ではIDを指定してオブジェクトの取得ができます。
誤って変更されないように関数の後ろにconstをつけています。
const EnemyMap& GetEnemysではstd::mapに登録された、連想配列を取得しています。
EnemyMap enemiesはmapに登録する変数です。
Manager.cpp
void EnemyManager::Register(std::unique_ptr<Enemy> enemy, std::string name)
{
battleEnemies.emplace(name, std::move(enemy));
}
void EnemyManager::Update(float elapsedTime)
{
for (const auto& enemy : enemies)
{
enemy.second->Update(elapsedTime);
}
}
上から解説します。
Registerは登録です。
第一引数にID、
第二引数にstd::moveを使用してオブジェクトからunique_ptrの所有権を
マネージャーに渡して管理しています。
Updateで更新しています。
autoFor文を使ってmapに登録したオブジェクトを順番に更新しています。
mapを使ったfor文の中でこれが一番高速になります。
For文の中身でsecondを使うとそのオブジェクトのポインタがとれるようになっています。
つまり、これを使うとオブジェクトの関数を実行できます。
ちなみに、fistにはID名が入っています。
IDの作成
登録するときに使用するIDを作成します。
namespace EnemyID
{
static constexpr auto& Slime = "Slime";
};
static constexprについてはこちらの記事で説明をしています。
Register&Draw
基本的なマネージャーは完成です。
次は実際に登録と描画をしていきましょう
例として3体登録します。
EnemyManager& enemyManager = EnemyManager::Instance();
for (int i = 0; i < 3; ++i)
{
std::unique_ptr<Slime> slime = std::make_unique<Slime>();
char text[256]{};
char index[8]{};
sprintf_s(index, "%d", i);
strcpy_s(text, EnemyID::Slime);
strncat_s(text, index, sizeof(index));
enemyManager.Register(std::move(slime), text, i);
}
for (const auto& enemy : EnemyManager::Instance().GetEnemys())
{
//描画形式によって変わるため割愛
}
unique_ptrを使ってnewをします。(スマートポインタが破棄されると自動的にdeleteされます)
IDの設定はあくまで一例です。
ID名の後に番号をつけて分けています。
enemyManager.Registerでもstd::moveで所有権を移します。
まとめ
mapとは別にunordered_mapがありますが、どちらでも大丈夫です。
違いについては別の記事にまとめられているのでお好きなほうをお使いください。
ちなみに、
EnemyMap::iterator it = fieldEnemies.find(name);
でイテレーターを取得できます。