[ChaEngine] ゲームループで動く要素
ChaEngineを使うにあたり覚えておくべきことの1つに、
Managerクラス, SystemObject, Object があります。
それぞれの違いは、ゲームを起動してから終了するまでのライフタイムにあります。
各ライフタイム
ここでのライフタイムとは、そのクラスのインスタンスがいつ生成されて、いつ解放されるか
存在していてアクセスできる時間を指します。
ここでは卒業制作で作る予定のゲームに合わせて、
「タイトルシーン」「ロビーシーン」「プレイシーン」「結果シーン」の例で説明します。
以下ゲームを起動してから終了するまでのライフタイムを図にしたものです。
それぞれの特徴は以下のテーブルのようになります。
| ライフタイム | 2つ以上のインスタンス | Get関数の呼び出し方 | 継承するクラス | |
|---|---|---|---|---|
Managerクラス |
ゲーム起動中ずっと | ❌ | Get<クラス型>() |
IManager |
SystemObject |
シーン | ❌ | Get<SystemObject>() |
SystemObjectBase |
Object |
シーンの任意タイミング | ✅ | Get<Objectの型>(GameObjectの変数) |
ObjectBase |
以下 Managerクラス, SystemObject, Object について詳しく説明します。
Manager クラス
ManagerクラスはIManagerクラスを継承する必要があります。
推奨される書き方は以下の通りです。
#include <Base/Base.h>
class XXXManager : public IManager
{
public:
XXXManager() = default;
~XXXManager() = default;
// 自動で呼ばれませんが、初期化処理用のメンバ関数を用意することを推奨します。
// 戻り値をbool型として 成功 / 失敗 を返すことを推奨します。
// bool Initialize();
// 自動で呼ばれませんが、更新処理用のメンバ関数を用意することを推奨します。
// 戻り値をbool型として 処理が行われたか / 行われていないか を返すことを推奨します。
// bool Update();
// Managerとして登録した場合、ゲーム終了時に呼ばれます。IManagerクラスによりoverride必須です。
void Release() override;
};
GameLoop::Run関数内で直接生成、初期化、更新を行うことを想定しています。
Manager クラスのインスタンス生成方法
GameLoop::Run関数の最初の方に行います。
生成は以下のコードで可能です。
ManagerRegistry::Add<クラス型>(クラスのコンストラクタ可変長引数);
戻り値は生成されたインスタンスの参照ポインタ(クラス型&)です。
推奨される書き方は以下の通りです。
XXXManager& xxxManager = ManagerRegistry::Add<XXXManager>(/*コンストラクタ引数*/);
// もし初期化処理があれば
// bool succeed = xxxManager.Initialize();
Managerクラスの更新処理方法
GameLoop::Run関数のwhileループ (ゲームループ内) で行います。
推奨される書き方は以下の通りです。
// もし更新処理があれば
// bool invoked = xxxManager.Update();
Manager インスタンスの参照方法
SystemObjectインスタンスの参照は2パターンあります。
GameObjectメンバ関数の場合、以下のコードで参照可能です。
Get<参照したいManagerクラスの型>();
GameObjectメンバ関数を含むすべての場所で以下のコードで参照可能です。
ManagerRegistry::Ref<参照したいManagerクラスの型>();
いずれも戻り値はインスタンスの参照ポインタ(クラス型&)です。
Objectと違い、引数にGameObjectは不要です。
Manager クラスの特殊ケース
現在実装済みの以下のクラスも、IManagerクラスを継承したManagerクラスです。
XXXManagerを付けていません。
DeltaTime➞ フレーム間時間(秒)を取得するfloat型に暗黙的キャスト可能ScreenSize➞ 描画領域のクライアントサイズ = ScreenSize を取得するVec2型に暗黙的キャスト可能
この特殊ケースにより、ObjectBaseを継承している場合は以下のように値を取得できます。
const float DT = Get<DeltaTime>(); // フレーム間時間(秒)を取得!
const Vec2 SCREEN_SIZE = Get<ScreenSize>(); // クライアントサイズ(浮動小数点数だけどピクセル単位)を取得!
SystemObject
SystemObjectはSystemObjectBaseクラスを継承する必要があります。
SystemObjectは名前の通りObjectの機能を持ちます。
推奨される書き方は以下の通りです。
#include <Game/Game.h>
struct XXXSystemData
{
// データはここに
};
// <>の中にXXXSystemを書き忘れないでください! ObjectBaseと違うため注意です!
// XXXSystemDataは省略可能です。
struct XXXSystem : SystemObjectBase<XXXSystem, XXXSystemData>
{
// オブジェクト生成後最初のゲームループ開始時自動で呼ばれます。
// overrideは任意です。
void Start() override;
// オブジェクト生成後毎フレーム自動で呼ばれます。
// overrideは任意です。
void Update() override;
// 毎フレーム全GameObjectのUpdateメンバ関数が呼ばれた後に自動で呼ばれます。
// overrideは任意です。
void LateUpdate() override;
// オブジェクト生成後毎フレーム自動、描画処理中自動で呼ばれます。
// overrideは任意です。
void Draw() override;
};
SystemObjectBaseを継承すると自動でObjectBaseクラスとISystemObjectクラスを継承します。
インスタンス生成はObjectと同様にできるため省略します。詳しくはObjectのインスタンス生成方法を参照してください。
1つのシーン内で2つ以上生成することはできません。 ※そのシーンで使わない場合、生成しない選択もあります。
SystemObject インスタンスの参照方法
SystemObjectインスタンスの参照はGameObject(SystemObjectを含む)のメンバ関数内で以下のコードで可能です。
Get<SystemObjectの型>();
戻り値は生成されたインスタンスの参照ポインタ(指定したSystemObjectの型&)です。
Objectと違い、引数にGameObjectは不要です。
Object
ObjectはObjectBaseクラスを継承する必要があります。
推奨される書き方は以下の通りです。
#include <Game/Game.h>
struct XXXData
{
// データはここに書く
};
struct XXX : ObjectBase<XXXData>
{
// オブジェクト生成後最初のゲームループ開始時自動で呼ばれます。
// overrideは任意です。
void Start() override;
// オブジェクト生成後毎フレーム自動で呼ばれます。
// overrideは任意です。
void Update() override;
// 毎フレーム全GameObjectのUpdateメンバ関数が呼ばれた後に自動で呼ばれます。
// overrideは任意です。
void LateUpdate() override;
// オブジェクト生成後毎フレーム自動、描画処理中自動で呼ばれます。
// overrideは任意です。
void Draw() override;
};
Object インスタンス生成方法
各シーンのStartメンバ関数内または、シーンオブジェクトからInstantiateメンバ関数から生成しなければなりません。
シーンのStartメンバ関数内であれば以下のコードで生成可能です。
Instantiate<Object型>(コンストラクタ可変長引数);
各Objectのメンバ関数内であれば以下のコードで生成可能です。
GetScene().Instantiate<生成したいObject型>(コンストラクタ可変長引数);
いずれも戻り値はGameObject型です。参照ポインタではないので注意してください!
GameObject型について
GameObjectはObjectのハンドラです。
ObjectをInstantiateメンバ関数でインスタンスを生成した際に、
戻り値としてGameObjectを取得可能です。
GameObject型の変数名は頭に「obj」をつける書き方を推奨します。
各シーンのStartメンバ関数内であれば以下のコードで取得可能です。
GameObject objXXX = Instantiate<XXX>(/*コンストラクタ可変長引数*/);
各Objectのメンバ関数内であれば以下のコードで取得可能です。
GameObject objXXX = GetScene().Instantiate<XXX>(/*コンストラクタ可変長引数*/);
Objectのメンバ関数内で以下のコードを書くことで、GameObjectからObject本体を取得できます。
Get<取得したいObject型>(GameObjectハンドラ);
