BaseCrossDx12ドキュメント
【Sample101】スタンダードな最初のサンプル
このサンプルは
Samples/Sample101ディレクトリ内にあります。VisualStdioで該当ソリューション(VS2022では
BaseCrossDx12VS2022.sln、VS2026では
BaseCrossDx12VS2026.sln)を開いてください。ビルド後実行すると、以下の画面が現れます。
ソリューションエクスプローラーは以下のようになっています。
リソースの登録
まず、このプロジェクトで使用する
リソースを登録します。
リソースとはテクスチャやメッシュなど、複数オブジェクトに実装されるデータを使いまわしできるように登録することです。
このサンプルでは
Scene.cppで行っています。
void Scene::CreateAssetResources(ID3D12Device* pDevice, ID3D12GraphicsCommandList* pCommandList)
{
//テクスチャ
auto texFile = App::GetRelativeAssetsDir() + L"wall.jpg";
auto texture = BaseTexture::CreateTextureFlomFile(pCommandList, texFile);
RegisterTexture(L"WALL_TX", texture);
texFile = App::GetRelativeAssetsDir() + L"sky.jpg";
texture = BaseTexture::CreateTextureFlomFile(pCommandList, texFile);
RegisterTexture(L"SKY_TX", texture);
texFile = App::GetRelativeAssetsDir() + L"trace.png";
texture = BaseTexture::CreateTextureFlomFile(pCommandList, texFile);
RegisterTexture(L"TRACE_TX", texture);
texFile = App::GetRelativeAssetsDir() + L"trace3.png";
texture = BaseTexture::CreateTextureFlomFile(pCommandList, texFile);
RegisterTexture(L"TRACE3_TX", texture);
//ステージ作成
ResetActiveStage<GameStage>(pDevice);
}
このようにここではいくつかのテクスチャをリソースとして登録しています。
こうすることで今後、例えば
L"WALL_TX"という名前でこのテクスチャを設定することができます。
カメラの定義
このプロジェクトのカメラは
MyCamera.h/cppに記述されます。
PerspecCamera(遠近法カメラ)を継承して作ります。
MyCamera.h
以下は宣言です。
//--------------------------------------------------------------------------------------
// MyCameraカメラ
//--------------------------------------------------------------------------------------
class MyCamera : public PerspecCamera {
std::weak_ptr<GameObject> m_TargetObject; //目標となるオブジェクト
float m_ToTargetLerp; //目標を追いかける際の補間値
Vec3 m_TargetToAt; //目標から視点を調整する位置ベクトル
float m_RadY;
float m_RadXZ;
//カメラの上下スピード
float m_CameraUpDownSpeed;
//カメラを下げる下限角度
float m_CameraUnderRot;
//腕の長さの設定
float m_ArmLen;
float m_MaxArm;
float m_MinArm;
//回転スピード
float m_RotSpeed;
//ズームスピード
float m_ZoomSpeed;
//左右スティック変更のモード
bool m_LRBaseMode;
//上下スティック変更のモード
bool m_UDBaseMode;
public:
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ
*/
//--------------------------------------------------------------------------------------
MyCamera();
//--------------------------------------------------------------------------------------
/*!
@brief コンストラクタ
@param[in] ArmLen 最初のArmの長さ
*/
//--------------------------------------------------------------------------------------
MyCamera(float ArmLen);
//--------------------------------------------------------------------------------------
/*!
@brief デストラクタ
*/
//--------------------------------------------------------------------------------------
virtual ~MyCamera();
//--------------------------------------------------------------------------------------
/*!
@brief カメラの位置を設定する
@param[in] Eye カメラ位置
@return なし
*/
//--------------------------------------------------------------------------------------
virtual void SetEye(const Vec3& Eye)override;
//--------------------------------------------------------------------------------------
/*!
@brief カメラの位置を設定する
@param[in] x x位置
@param[in] y y位置
@param[in] z z位置
@return なし
*/
//--------------------------------------------------------------------------------------
virtual void SetEye(float x, float y, float z)override;
//--------------------------------------------------------------------------------------
/*!
@brief カメラの目標オブジェクトを得る
@return カメラの目標
*/
//--------------------------------------------------------------------------------------
std::shared_ptr<GameObject> GetTargetObject() const;
//--------------------------------------------------------------------------------------
/*!
@brief カメラの目標オブジェクトを設定する
@param[in] Obj カメラの目標オブジェクト
@return なし
*/
//--------------------------------------------------------------------------------------
void SetTargetObject(const std::shared_ptr<GameObject>& Obj);
//--------------------------------------------------------------------------------------
/*!
@brief オブジェクトを追いかける場合の補間値を得る
@return オブジェクトを追いかける場合の補間値
*/
//--------------------------------------------------------------------------------------
float GetToTargetLerp() const;
//--------------------------------------------------------------------------------------
/*!
@brief オブジェクトを追いかける場合の補間値を設定する
@param[in] f オブジェクトを追いかける場合の補間値
@return なし
*/
//--------------------------------------------------------------------------------------
void SetToTargetLerp(float f);
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離を得る
@return EyeとAtの距離
*/
//--------------------------------------------------------------------------------------
float GetArmLengh() const;
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離を更新する(現在のEyeとAtから求める)
@return なし
*/
//--------------------------------------------------------------------------------------
void UpdateArmLengh();
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離の最大値を得る
@return EyeとAtの距離の最大値
*/
//--------------------------------------------------------------------------------------
float GetMaxArm() const;
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離の最大値を設定する
@param[in] f EyeとAtの距離の最大値
@return なし
*/
//--------------------------------------------------------------------------------------
void SetMaxArm(float f);
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離の最小値を得る
@return EyeとAtの距離の最小値
*/
//--------------------------------------------------------------------------------------
float GetMinArm() const;
//--------------------------------------------------------------------------------------
/*!
@brief EyeとAtの距離の最小値設定する
@param[in] f EyeとAtの距離の最小値
@return なし
*/
//--------------------------------------------------------------------------------------
void SetMinArm(float f);
//--------------------------------------------------------------------------------------
/*!
@brief 回転スピードを得る
@return 回転スピード(0.0f以上)
*/
//--------------------------------------------------------------------------------------
float GetRotSpeed() const;
//--------------------------------------------------------------------------------------
/*!
@brief 回転スピードを設定する
@param[in] f 回転スピード(マイナスを入力してもプラスになる)
@return なし
*/
//--------------------------------------------------------------------------------------
void SetRotSpeed(float f);
//--------------------------------------------------------------------------------------
/*!
@brief ターゲットからAtへの調整ベクトルを得る
@return ターゲットからAtへの調整ベクトル
*/
//--------------------------------------------------------------------------------------
Vec3 GetTargetToAt() const;
//--------------------------------------------------------------------------------------
/*!
@brief ターゲットからAtへの調整ベクトルを設定する
@param[in] v ターゲットからAtへの調整ベクトルを
@return なし
*/
//--------------------------------------------------------------------------------------
void SetTargetToAt(const Vec3& v);
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの左右変更をBaseモードにするかどうかを得る
@return Baseモードならtrue(デフォルト)
*/
//--------------------------------------------------------------------------------------
bool GetLRBaseMode() const;
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの左右変更をBaseモードにするかどうかを得る
@return Baseモードならtrue(デフォルト)
*/
//--------------------------------------------------------------------------------------
bool IsLRBaseMode() const;
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの左右変更をBaseモードにするかどうかを設定する
@param[in] b Baseモードならtrue
@return なし
*/
//--------------------------------------------------------------------------------------
void SetLRBaseMode(bool b);
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの上下変更をBaseモードにするかどうかを得る
@return Baseモードならtrue(デフォルト)
*/
//--------------------------------------------------------------------------------------
bool GetUDBaseMode() const;
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの上下変更をBaseモードにするかどうかを得る
@return Baseモードならtrue(デフォルト)
*/
//--------------------------------------------------------------------------------------
bool IsUDBaseMode() const;
//--------------------------------------------------------------------------------------
/*!
@brief Rスティックの上下変更をBaseモードにするかどうかを設定する
@param[in] b Baseモードならtrue
@return なし
*/
//--------------------------------------------------------------------------------------
void SetUDBaseMode(bool b);
//--------------------------------------------------------------------------------------
/*!
@brief カメラの視点を設定する
@param[in] At 視点位置
@return なし
*/
//--------------------------------------------------------------------------------------
virtual void SetAt(const Vec3& At)override;
//--------------------------------------------------------------------------------------
/*!
@brief カメラの視点を設定する
@param[in] x x位置
@param[in] y y位置
@param[in] z z位置
@return なし
*/
//--------------------------------------------------------------------------------------
virtual void SetAt(float x, float y, float z)override;
//--------------------------------------------------------------------------------------
/*!
@brief 更新処理
@return なし
*/
//--------------------------------------------------------------------------------------
virtual void OnUpdate(double elapsedTime)override;
};
実装内容はコメントの通りです。これは、プレイヤーをはじめ、他のオブジェクトでも参照されます。
スタンダードサンプルでは、このカメラが多用されています。
しかし
カメラは各ゲームにおいて
非常に重要なオブジェクトです。各ゲームにおいて
カメラの演出は個性の現れとも言えます。ですのでこの
MyCameraクラスを参考にしながら、ぜひ、ゲーム独自のカメラ設計を行っていただきたいと思います。
カメラに絶対に必要なメンバは以下の3つです。
PerspecCameraの親の
Cameraクラスのメンバですが
Vec3 m_eye; //カメラ位置
Vec3 m_at; //注目点
Vec3 m_up; //カメラの傾き(アップ)
この3つのメンバを
動的にどのように変化させるかがカメラの本質です。
例えば映画を撮影する場合、カメラマンは監督の指示のもと、
視点(m_eye)を変えたり、
映す場所(m_at)を変えたり
カメラの傾き(m_up)を変えたりします。これを動的に変化させることで、ゲームの演出を行います。
このほかに
画角や、実際に画面に表示させる場合の
縦横比(アスペクト比)があります。
PerspecCameraのメンバですがこちらも重要です。
float m_fovY; //射影角度
float m_aspect; //アスペクト比
これらはデフォルトの値があり(射影角度は45度、アスペクト比はゲーム画面の縦横比)、それを変える演出が必要な場合動的に変更します。
構造は単純ですので、ぜひ、
作成するゲームの独自カメラを宣言定義することをお勧めします。
ステージ
スタンダードサンプルでは
ステージという概念があります。
もちろん、一番重要なのは
シーンですがその
シーンに
現在アクティブなステージというオブジェクトを取り入れています。
ゲームには
タイトル画面、ゲーム画面、クリア画面などなど様々な状態があります。これを実現するのが
ステージです。
ステージは
スタンダードサンプルのなかのライブラリ
StandardLib/Objects/Headers/Stage.hにヘッダがあり
StandardLib/Objects/Sources/Stage.cppに実体があります。
ステージクラスは結構大きなクラスです。テンプレート関数含め様々な機能を実装しています。興味ある人は、ぜひ参照してください。
しかし、皆さんはこれらの関数を熟知する必要はありません。
ゲームステージ
例えば
ゲーム画面であれば
Stageクラスを継承して
GameStageクラスなどを作成すればよいのです。
Sample101ではコンテンツ側に
GameStage.h/cppがあり、そのヘッダは以下のようになっています。
//--------------------------------------------------------------------------------------
// ゲームステージ
//--------------------------------------------------------------------------------------
class GameStage : public Stage {
std::shared_ptr<Camera> m_camera;
std::shared_ptr<LightSet> m_lightSet;
public:
GameStage(ID3D12Device* pDevice):
Stage(pDevice)
{}
virtual ~GameStage() {}
//構築時処理
virtual void OnCreate()override;
//カメラをを得る
virtual std::shared_ptr<Camera> GetCamera() const override {
return m_camera;
}
//ライトセットを得る
virtual std::shared_ptr<LightSet> GetLightSet() const override {
return m_lightSet;
}
//カメラとライト更新
virtual void UpdateCameraLight(double elapsedTime)override {
m_camera->OnUpdate(elapsedTime);
m_lightSet->OnUpdate(elapsedTime);
}
//ボックスオブジェクトの作成
void CreateFixedBox();
//壁模様のボックスの作成
void CreateWallBox();
//追いかけるオブジェクトの作成
void CreateSeekObject();
//プレイヤーの作成
void CreatePlayer();
};
Stageクラスの派生クラスで記述しなければいけないのは、ごくわずかです。すなわち
カメラとライト、
カメラとライトの更新、あとは配置するオブジェクトの構築、更新処理です。
配置オブジェクトの定義
ボックスオブジェクトの定義
まず一番簡単な
ボックスオブジェクトの作成です。これは
CreateFixedBox()関数で実装されます。
内容は以下です。
//ボックスオブジェクトの作成
void GameStage::CreateFixedBox() {
TransParam param;
param.scale = Vec3(50.0f, 1.0f, 50.0f);
param.position = Vec3(0.0f, -0.5, 0.0f);
AddGameObject<FixedBox>(param);
param.scale = Vec3(5.0f, 1.0f, 5.0f);
param.position = Vec3(10.0f, 0.0, 10.0f);
AddGameObject<FixedBox>(param);
param.position = Vec3(10.0f, 0.0, 10.0f);
param.quaternion = Quat(Vec3(-1, 0, 1), XM_PIDIV4);
AddGameObject<FixedBox>(param);
param.position = Vec3(-10.0f, 0.0, 10.0f);
param.quaternion = Quat(Vec3(0, 1, 1), XM_PIDIV4);
AddGameObject<FixedBox>(param);
}
ここでは4つの
FixedBoxが構築されます。
FixedBoxはコンストラクタに
TransParam構造体をとりますので、各オブジェクトごとに内容を設定して
AddGameObject<FixedBox>(param);
のように呼び出します。
AddGameObject()テンプレート関数はステージ方式におけるかなり重要な関数です。この関数は指定のクラス(ここではFixedBox)を構築して
FixedBox::OnCreate()関数を呼び出します。
以下は
FixedBox::OnCreate()関数です。
Character.cppにあります。
void FixedBox::OnCreate() {
//OBB衝突j判定を付ける
auto ptrColl = AddComponent<CollisionObb>();
//この衝突判定は、動かない、という特徴をつける
ptrColl->SetFixed(true);
//影(シャドウマップ)をつける
auto ptrShadow = AddComponent<Shadowmap>();
//L"DEFAULT_CUBE"というメッシュをつける
ptrShadow->AddBaseMesh(L"DEFAULT_CUBE");
//描画コンポーネントに、BcPNTStaticDraw、を指定する
auto ptrDraw = AddComponent<BcPNTStaticDraw>();
//ここにもL"DEFAULT_CUBE"というメッシュをつける
ptrDraw->AddBaseMesh(L"DEFAULT_CUBE");
//L"SKY_TX"というテクスチャをつける
ptrDraw->AddBaseTexture(L"SKY_TX");
//自分自身に影が移りこむようにする
ptrDraw->SetOwnShadowActive(true);
}
ここで
コンポーネントが出てきます。
コンポーネントというのは、各オブジェクトにおける
特徴というか
性格(擬人化してますが)みたいなものです。
コンポーネントを実装するには、上記してるように
//OBB衝突j判定を付ける
auto ptrColl = AddComponent<CollisionObb>();
//この衝突判定は、動かない、という特徴をつける
ptrColl->SetFixed(true);
のように記述します。これにより、このゲームオブジェクトは
CollisionObb(直方体の衝突判定)を付けることになります。
そして
ptrColl->SetFixed(true);という記述で
衝突によって後ずさりなどの影響を受けない設定になります。
続いて
シャドウマップ(影)のコンポーネントを実装します。
//影(シャドウマップ)をつける
auto ptrShadow = AddComponent<Shadowmap>();
//L"DEFAULT_CUBE"というメッシュをつける
ptrShadow->AddBaseMesh(L"DEFAULT_CUBE");
ここで出てくる
L"DEFAULT_CUBE"は、
BaseCrossDx12が基本的に持っている
メッシュの形状を指定しています。
BasicLib内の
BaseScene::CreateDefaultResources()に実装があります。
FixedBoxは直方体のオブジェクトなので、立方体(すなわちL"DEFAULT_CUBE")の形状のメッシュで、あとは拡大縮小や傾きによってゲームステージ上の形状を決定します。
続いて
描画方法に指定です。
//描画コンポーネントに、BcPNTStaticDraw、を指定する
auto ptrDraw = AddComponent<BcPNTStaticDraw>();
//ここにもL"DEFAULT_CUBE"というメッシュをつける
ptrDraw->AddBaseMesh(L"DEFAULT_CUBE");
描画も
コンポーネントとして指定します。
BcPNTStaticDrawというコンポーネントは
ベーシックな(Bc)、位置と法線とテクスチャ情報(PNT)の頂点情報を持つ、
動的に頂点は変更しない(Static)、描画コンポーネントという意味です。
BaseCrossDx12には、他にも様々な描画コンポーネントがあります。
描画コンポーネントを使うことで、DirectX12が持つ細かな描画設定を記述しなくて済みます。
しかし、独自の描画を実装したければ、その方法もあります。(サンプルは後述します)
描画コンポーネントにも
L"DEFAULT_CUBE"を指定します。
追いかけるオブジェクトの定義
このサンプルでは、プレイヤーを動かすと追いかけるオブジェクトがあります。これは
SeekObjectで、以下のように
Character.h/cppで記述します。以下は宣言です。
//--------------------------------------------------------------------------------------
// 追いかける配置オブジェクト
//--------------------------------------------------------------------------------------
class SeekObject : public GameObject {
//ステートマシーン
std::unique_ptr< StateMachine<SeekObject> > m_StateMachine;
Vec3 m_StartPos;
float m_StateChangeSize;
//フォース
Vec3 m_Force;
//速度
Vec3 m_Velocity;
float m_Weight;
float m_MaxSpeed;
float m_MaxForce;
float m_Decl;
public:
//構築と破棄
SeekObject(const std::shared_ptr<Stage>& StagePtr, const Vec3& startPos);
virtual ~SeekObject();
//初期化
virtual void OnCreate() override;
//アクセサ
const std::unique_ptr<StateMachine<SeekObject>>& GetStateMachine() {
return m_StateMachine;
}
float GetStateChangeSize() const {
return m_StateChangeSize;
}
const Vec3& GetForce()const {
return m_Force;
}
void SetForce(const Vec3& f) {
m_Force = f;
}
void AddForce(const Vec3& f) {
m_Force += f;
}
const Vec3& GetVelocity()const {
return m_Velocity;
}
void SetVelocity(const Vec3& v) {
m_Velocity = v;
}
float GetWeight()const {
return m_Weight;
}
float GetMaxSpeed()const {
return m_MaxSpeed;
}
float GetMaxForce()const {
return m_MaxForce;
}
float GetDecl() const { return m_Decl; }
void SetDecl(float f) { m_Decl = f; }
void RotToHead(float LerpFact);
void ApplyForce();
Vec3 GetTargetPos()const;
//操作
virtual void OnUpdate(double elapsedTime) override;
};
このオブジェクトは
ステートマシンという仕組みが実装されています。
例えば
OnUpdate処理で、いろんな条件次第で動きを変えたいときがあります。簡単な分岐であれば
if else 文や、
switch 文などで分岐できますが、複雑な処理になると不具合が出やすくなります。
そんなときは、オブジェクトに、複数の
ステート(状態)というクラスを用意して、何かの条件でそれらのステートを適応して更新処理を記述します。そうすることで可読性が高くなるばかりでなく、不具合も少なくなります。
ステートマシンを実装するには、まず、メンバ変数に
//ステートマシーン
std::unique_ptr< StateMachine<SeekObject> > m_StateMachine;
のような変数を用意します。
そのうえで、必要なステートクラスを実装します。
以下は、その中の
SeekFarStateです。
//--------------------------------------------------------------------------------------
// class SeekFarState : public ObjState<SeekObject>;
// 用途: プレイヤーから遠いときの移動
//--------------------------------------------------------------------------------------
class SeekFarState : public ObjState<SeekObject>
{
SeekFarState() {}
public:
static std::shared_ptr<SeekFarState> Instance();
virtual void Enter(const std::shared_ptr<SeekObject>& Obj)override;
virtual void Execute(const std::shared_ptr<SeekObject>& Obj)override;
virtual void Exit(const std::shared_ptr<SeekObject>& Obj)override;
};
コンストラクタはprivateにしておきます。オブジェクトの生成は、staticなInstance関数で行います。
ステートは
Enter、Execute、Exit関数を用意します
この関数はこのステートに入った時に、一度だけ呼ばれます。
この関数はEnter関数が呼ばれた次のターンから、毎ターン呼ばれます。
この関数はこのステートから抜けるときに一度だけばれます。
以下は実体です。ここで重要なのは
SeekFarState::Execute()関数です。
void SeekFarState::Execute(const std::shared_ptr<SeekObject>& Obj) {
auto trans = Obj->GetComponent<Transform>();
auto seekComp = Obj->GetComponent<Seek>();
auto separationComp = Obj->GetComponent<Separation>();
//フォースの初期化
Vec3 force(0.0f);
//フォースの計算
force = seekComp->Execute(Obj->GetVelocity(), Obj->GetTargetPos(), trans->GetWorldPosition())
* Obj->GetWeight();
force += separationComp->Execute(force);
//そのforceを設定する
Obj->SetForce(force);
//その結果をもとにオブジェクトの位置に反映する
Obj->ApplyForce();
//結果として、ターゲットとの間の距離がObj->GetStateChangeSize()
//より近ければ、ステートSeekNearStateに変換
float f = bsm::bsmUtil::length(Obj->GetComponent<Transform>()->GetPosition() - Obj->GetTargetPos());
if (f < Obj->GetStateChangeSize()) {
Obj->GetStateMachine()->ChangeState(SeekNearState::Instance());
}
}
ここでは以下のように
Seekと
Separationというコンポーネントが実装されています。これらは
ステアリング行動コンポーネントという種類で
フォースという、その物体にかけられる
力によって物体を動かす仕組みです。
ニュートンの第2法則にのっとった処理です。
Seekという行動は
目標物を追いかける(ここではプレイヤー)コンポーネントです。
しかし
Seekは追いかけるだけなので、お互いにぶつかり合っても気にしません。それではちょっと頭が悪いので
Separationという
常にお互いに距離をあけるコンポーネントも実装します。
auto seekComp = Obj->GetComponent<Seek>();
auto separationComp = Obj->GetComponent<Separation>();
それぞれ
Execute()という関数を持ち、その関数にパラメータを渡すことで
フォースを変化させます。
//フォースの初期化
Vec3 force(0.0f);
//フォースの計算
force = seekComp->Execute(Obj->GetVelocity(), Obj->GetTargetPos(), trans->GetWorldPosition())
* Obj->GetWeight();
force += separationComp->Execute(force);
Seekには
速度、目標物、現在位置をパラメータにして
Execute()関数を呼び出し、
Separationには
現在のフォースをパラメータにして
Execute()関数を呼び出します。最終的に決定した
フォースをオブジェクト側に設定し
//そのforceを設定する
Obj->SetForce(force);
最後に、実際に位置情報を更新します。
//その結果をもとにオブジェクトの位置に反映する
Obj->ApplyForce();
Seekは
Separationと同時に反映しても、追いかける相手が急停止したときなども
通り過ぎてしまう欠点があります。それを補うのが、目標に近づいたときに
ステートを変更する処理です。
//結果として、ターゲットとの間の距離がObj->GetStateChangeSize()
//より近ければ、ステートSeekNearStateに変換
float f = bsm::bsmUtil::length(Obj->GetComponent<Transform>()->GetPosition() - Obj->GetTargetPos());
if (f < Obj->GetStateChangeSize()) {
Obj->GetStateMachine()->ChangeState(SeekNearState::Instance());
}
このように
SeekNearStateにステートを変更します。
以下は
SeekNearStateの中身です。
void SeekNearState::Execute(const std::shared_ptr<SeekObject>& Obj) {
auto trans = Obj->GetComponent<Transform>();
auto arriveComp = Obj->GetComponent<Arrive>();
auto separationComp = Obj->GetComponent<Separation>();
//フォースの初期化
Vec3 force(0.0f);
//フォースの計算
force = arriveComp->Execute(force,Obj->GetVelocity(), Obj->GetTargetPos());
force += separationComp->Execute(force);
//そのforceを設定する
Obj->SetForce(force);
//その結果をもとにオブジェクトの位置に反映する
Obj->ApplyForce();
//結果として、ターゲットとの間の距離がObj->GetStateChangeSize()
//より遠ければ、ステートSeekFarStateに変換
float f = bsm::bsmUtil::length(Obj->GetComponent<Transform>()->GetPosition() - Obj->GetTargetPos());
if (f >= Obj->GetStateChangeSize()) {
Obj->GetStateMachine()->ChangeState(SeekFarState::Instance());
}
}
ここでは
Arriveという新たな行動コンポーネントを使用します。これは
到着するを実装する行動コンポーネントです。引き続き
Separationも使います。
auto arriveComp = Obj->GetComponent<Arrive>();
auto separationComp = Obj->GetComponent<Separation>();
Arriveの
Execute()関数には
フォース、速度、目標地点を渡します。
//フォースの計算
force = arriveComp->Execute(force,Obj->GetVelocity(), Obj->GetTargetPos());
このように実装し、プレイヤーが動くなどで一定距離より遠ざかった場合に
SeekFarStateにステートを変更します。
//結果として、ターゲットとの間の距離がObj->GetStateChangeSize()
//より遠ければ、ステートSeekFarStateに変換
float f = bsm::bsmUtil::length(Obj->GetComponent<Transform>()->GetPosition() - Obj->GetTargetPos());
if (f >= Obj->GetStateChangeSize()) {
Obj->GetStateMachine()->ChangeState(SeekFarState::Instance());
}
Player.h/cpp
最後に
プレイヤーです
プレイヤーはコントローラーで操作できます。コントローラーを動かすと、Playerに見立てたボールを動かすことができます。以下は
Playerクラスです。
//--------------------------------------------------------------------------------------
/// プレイヤー
//--------------------------------------------------------------------------------------
class Player : public GameObject {
//プレイヤーが使用するコントローラとキーボードの入力
Vec2 GetInputState() const;
// コントローラから方向ベクトルを得る
Vec3 GetMoveVector() const;
//プレイヤーの移動
void MovePlayer();
//入力ハンドラー
InputHandler<Player> m_InputHandler;
//スピード
float m_Speed;
public:
Player(const std::shared_ptr<Stage>& StagePtr, const TransParam& param);
virtual ~Player() {}
//構築時処理
virtual void OnCreate()override;
//更新時処理
virtual void OnUpdate(double elapsedTime);
//Aボタン
void OnPushA();
//Bボタン
void OnPushB(){}
};
以下、実体です。
Player.cppに記述されます。
Player::OnCreate関数
void Player::OnCreate() {
GetStage()->SetSharedGameObject(L"Player",GetThis<Player>());
auto ptrShadow = AddComponent<Shadowmap>();
ptrShadow->AddBaseMesh(L"DEFAULT_SPHERE");
//CollisionSphere衝突判定を付ける
auto ptrColl = AddComponent<CollisionSphere>();
//重力をつける
auto ptrGra = AddComponent<Gravity>();
auto ptrDraw = AddComponent<BcPNTStaticDraw>();
ptrDraw->AddBaseMesh(L"DEFAULT_SPHERE");
ptrDraw->AddBaseTexture(L"TRACE3_TX");
//透明処理
SetAlphaActive(true);
//カメラを得る
auto ptrCamera = std::dynamic_pointer_cast<MyCamera>(GetStage()->GetCamera());
if (ptrCamera) {
//MyCameraである
//MyCameraに注目するオブジェクト(プレイヤー)の設定
ptrCamera->SetTargetObject(GetThis<GameObject>());
ptrCamera->SetTargetToAt(Vec3(0, 0.25f, 0));
}
}
ここで
AddComponent関数によっていくつかのコンポーネントが設定されています。
auto ptrShadow = AddComponent<Shadowmap>();
ptrShadow->AddBaseMesh(L"DEFAULT_SPHERE");
//CollisionSphere衝突判定を付ける
auto ptrColl = AddComponent<CollisionSphere>();
//重力をつける
auto ptrGra = AddComponent<Gravity>();
auto ptrDraw = AddComponent<BcPNTStaticDraw>();
ptrDraw->AddBaseMesh(L"DEFAULT_SPHERE");
ptrDraw->AddBaseTexture(L"TRACE3_TX");
がコンポーネント関連の処理です。
CollisionSphereは球体の衝突判定です。
Gravityは重力です。以上が更新系のコンポーネントです。
Shadowmapは影を作成するコンポーネントです。
BcPNTStaticDrawは描画系のコンポーネントでスタティックなメッシュに適用します。ここでの
PNTは
ポジション、法線、テクスチャを含む頂点フォーマットという意味です。
ptrDraw->AddBaseTexture(L"TRACE3_TX");
という記述は、
Scene::CreateAssetResources関数でリソース登録したテクスチャです。
また
ptrDraw->AddBaseMesh(L"DEFAULT_SPHERE");
という記述で
L"DEFAULT_SPHERE"という名前のメッシュを設定しています。
L"DEFAULT_なんたら"という記述は、あらかじめ登録されているプリミティブな形状のメッシュを使います。
登録されている形状は、ベーシックライブラリ中
BaseScene::CreateDefaultResources関数で登録されています。
ここでは、プレイヤーの更新処理を行います。
void Player::OnUpdate(double elapsedTime) {
//コントローラチェックして入力があればコマンド呼び出し
m_InputHandler.PushHandle(GetThis<Player>());
MovePlayer();
}
ここでは、コントローラの状態を検証し、もしAボタンが押されたら
Player::OnPushA関数が呼ばれる処理を書きます。
続いて
MovePlayer();呼び出して、コントローラの状態に合わせ、プレイヤーを動かします。
void Player::MovePlayer() {
float elapsedTime = (float)Scene::GetElapsedTime();
auto angle = GetMoveVector();
if (angle.length() > 0.0f) {
auto pos = GetComponent<Transform>()->GetPosition();
pos += angle * elapsedTime * m_Speed;
GetComponent<Transform>()->SetPosition(pos);
}
//回転の計算
if (angle.length() > 0.0f) {
auto utilPtr = GetBehavior<UtilBehavior>();
utilPtr->RotToHead(angle, 1.0f);
}
}
ここで呼び出されている
GetMoveVector関数は以下です。
Vec3 Player::GetMoveVector() const {
Vec3 angle(0, 0, 0);
//入力の取得
auto inPut = GetInputState();
float moveX = inPut.x;
float moveZ = inPut.y;
if (moveX != 0 || moveZ != 0) {
float moveLength = 0; //動いた時のスピード
auto ptrTransform = GetComponent<Transform>();
auto ptrCamera = GetStage()->GetCamera();
//進行方向の向きを計算
auto front = ptrTransform->GetPosition() - ptrCamera->GetEye();
front.y = 0;
front.normalize();
//進行方向向きからの角度を算出
float frontAngle = atan2(front.z, front.x);
//コントローラの向き計算
Vec2 moveVec(moveX, moveZ);
float moveSize = moveVec.length();
//コントローラの向きから角度を計算
float cntlAngle = atan2(-moveX, moveZ);
//トータルの角度を算出
float totalAngle = frontAngle + cntlAngle;
//角度からベクトルを作成
angle = Vec3(cos(totalAngle), 0, sin(totalAngle));
//正規化する
angle.normalize();
//移動サイズを設定。
angle *= moveSize;
//Y軸は変化させない
angle.y = 0;
}
return angle;
}
ここで呼び出されている
GetInputState関数は以下です。
Vec2 Player::GetInputState() const {
Vec2 ret;
//コントローラの取得
auto cntlVec = App::GetInputDevice().GetControlerVec();
ret.x = 0.0f;
ret.y = 0.0f;
WORD wButtons = 0;
if (cntlVec[0].bConnected) {
ret.x = cntlVec[0].fThumbLX;
ret.y = cntlVec[0].fThumbLY;
}
return ret;
}
このように
- コントローラの状態を取得(Player::GetInputState())
- 進行方向を決定(Player::GetMoveVector())
- プレイヤーを移動(Player::MovePlayer)
- プレイヤーの更新(Player::OnUpdate)
と構造的(関数に小分け)にメンバ関数が呼ばれているのが分かります。