Old revision
Revision as of 2024-01-22 23:00
---
title: コンポーネントを追加する
date: 2024-01-22
tags: 編集中
parent: ../docs
---
`nodec_game_engine`で、新規にコンポーネントを追加する方法を説明します。
===
# 環境
このチュートリアルでは、以下の環境での操作を想定しています。
サンプルプロジェクト:
* <https://github.com/nodec-project/hello-nodec-game>
# 目的
`nodec_game_engine`では、ECS(Entity Component System)を採用しています。
このため、ゲームオブジェクトは、コンポーネントを持つことで機能を実現します。
このチュートリアルでは、新規にコンポーネントを追加する方法を説明します。
たとえば、敵を表すコンポーネント(`Enemy`)を新規で追加してみましょう。
# 手順
# 1. コンポーネントクラスを作成する
プロジェクトディレクトリ内の`src/components`ディレクトリに、`enemy.hpp`を新しく作成し、以下の内容を記述します。
```cpp
// (1)
#ifndef HELLO_NODEC_GAME__COMPONENTS__ENEMY_HPP_
#define HELLO_NODEC_GAME__COMPONENTS__ENEMY_HPP_
#include <nodec_scene_serialization/serializable_component.hpp>
namespace hello_nodec_game {
namespace components { // (2)
// (3)
struct Enemy : public nodec_scene_serialization::BaseSerializableComponent {
// (4)
Enemy()
: BaseSerializableComponent(this) {}
// (5)
float hp{100.f}
// (6)
template<class Archive>
void serialize(Archive &archive) {
archive(cereal::make_nvp("hp", hp));
}
};
} // namespace components
} // namespace hello_nodec_game
// (7)
NODEC_SCENE_REGISTER_SERIALIZABLE_COMPONENT(hello_nodec_game::components::Bullet)
#endif
```
説明:
(1):
インクルードガードを記述します。
多くの場合、`<プロジェクト名>__<名前空間パス>__<ファイル名>_`という形式で、インクルードガードを記述します^[注.include-guard]。
(2):
`hello_nodec_game`名前空間内に、`components`名前空間を作成します。
この名前空間内に、コンポーネントクラスを記述します。
(3):
`Enemy`クラスを定義します。
`nodec_scene_serialization::BaseSerializableComponent`を継承しています。
`nodec_scene_serialization::BaseSerializableComponent`は、`nodec_scene_serialization`ライブラリによって提供される、シリアライズ可能なコンポーネントの基底クラスです。
`nodec_scene_serialization::BaseSerializableComponent`を継承することで、シリアライズ可能なコンポーネントとして振る舞うことができます。
シリアライズ可能なコンポーネントになることで以下のことが可能になります。
* コンポーネントの内容をファイルに保存し、読み込むことができる
* エディタ上でコンポーネントを複製することができる
* アニメーション等でプロパティ名によって動的に値を変更することができる
(4):
`nodec_scene_serialization::BaseSerializableComponent`のコンストラクタに、`this`を渡しています。
これは、継承元の`BaseSerializableComponent`が派生先の型情報を取得するために必要です。
(5):
`hp`というメンバ変数を定義しています。
`Enemy`コンポーネントして持つべき情報を定義していきます。
(6):
`serialize`メンバ関数を定義しています。
シリアライズ可能なコンポーネントとして振る舞うためには、この関数を定義する必要があります。
`cereal::make_nvp`マクロを使って、`hp`メンバ変数をシリアライズしています。
`cereal::make_nvp`マクロの第一引数に、プロパティ名を指定し、第二引数に、シリアライズする変数を指定します。
プロパティ名は、メンバー変数名と同じにすることを推奨します。
(7):
`NODEC_SCENE_REGISTER_SERIALIZABLE_COMPONENT`マクロを使って、`Enemy`クラスを登録しています。
このマクロを使うことで、シリアライズ可能なコンポーネントとして振る舞うことができます。
# 2. コンポーネントクラスをシリアライズシステムに登録する
`Enemy`クラスは`BaseSerializableComponent`を継承しており、シリアライズ可能なクラスですが、
エンジンに、このクラスがシリアライズ可能なクラスであることを伝える必要があります。
`src/app.cpp`内の`Register components in serialization.`ブロック内で以下を追記します。
```cpp
// Register components in serialization.
{
scene_serialization_.register_component<Bullet>();
scene_serialization_.register_component<PlayerControl>();
scene_serialization_.register_component<Enemy>(); // 追記
}
```
# (オプション) 3. エディタにコンポーネントを登録する
追加で、エディタにコンポーネントを登録することで以下のことが行えます。
* エディタ上でコンポーネントを追加できるようになる
* エディタ上でコンポーネントのプロパティを編集できるようになる
`Enemy`コンポーネント用のエディタクラスを定義します。
`src/editors/`ディレクトリ内に、`enemy_editor.hpp`を新しく作成し、以下の内容を記述します。
```cpp
#ifndef HELLO_NODEC_GAME__EDITORS__ENEMY_EDITOR_HPP_
#define HELLO_NODEC_GAME__EDITORS__ENEMY_EDITOR_HPP_
#include <imgui.h>
#include <nodec_scene_editor/component_editor.hpp>
#include "../components/enemy.hpp"
namespace hello_nodec_game {
namespace editors {
class EnemyEditor
: public nodec_scene_editor::BasicComponentEditor<components::Enemy> {
public:
void on_inspector_gui(components::Enemy &enemy,
const nodec_scene_editor::InspectorGuiContext &context) override {
ImGui::DragFloat("Hp", &enemy.hp);
}
};
} // namespace editors
} // namespace hello_nodec_game
#endif
```
# 注釈
[注.include-guard]: \
[GoogleのC++コーディングガイドライン](https://google.github.io/styleguide/cppguide.html#The__define_Guard)をもとにしています。\
単語間の区切りは、アンダースコア2つで表しています。単語内境界と区別するためです。