113.Effekseerの使用

 このサンプルはFullTutorial013というディレクトリに含まれます。

Effekseerについて

 Effekseerはエフェクト作成のためのツールです。高機能なうえに軽いツールで、フリーで配布されているので、学習のためや、小規模なソフトハウスでも気軽に使うことができます。

 BaseCrossでもパーティクル描画という方法でエフェクトの描画は可能ですが、細かな演出には向いていません。というのは、エフェクトの動的な部分を計算で指定してあげなければいけないからです。
 簡単なエフェクトや、連番アニメーションであればこれでもいいのですが、手の込んだエフェクトはやはりツールが必要です。そこで、Effekseerで作成したデータをBaseCrossで再生するサンプルを作成しました。それがFullTutorial013です。

ライブラリの準備

 Effekseer再生のためのライブラリは、GitHub情報サイト

https://effekseer.github.io/jp/

 からダウンロードできます。ここからダウンロードリンクをクリックしEffekseer for Runtimeをダウンロードしてください。
 ダウンロードするのは、zipファイルです。そのままではセキュリティによりビルドに含めるとエラーが出ますので、zipの状態で、右ボタンをクリックして、プロパティで、セキュリティ警告を解除してください。
 解凍しますとCompiledというディレクトリが出てきます。この中のincludeおよびlibの内容を以下のように、BaseCrossのLibsディレクトリ内にコピーしてください。
【includeファイル】
Compiled/include/Effekseer.h
Compiled/include/EffekseerRendererDX11.h
Compiled/include/EffekseerSoundXAudio2.h
を、BaseCrossの
Libs/include/Effekseer.h
Libs/include/EffekseerRendererDX11.h
Libs/include/EffekseerSoundXAudio2.h
としてコピー。

【libファイル(デバッグ用)】
Compiled/lib/VS2015/Debug/Effekseer.lib
Compiled/lib/VS2015/Debug/EffekseerRendererDX11.lib
Compiled/lib/VS2015/Debug/EffekseerSoundXAudio2.lib
を
Libs/lib/Dx11/Debug/Effekseer.lib
Libs/lib/Dx11/Debug/EffekseerRendererDX11.lib
Libs/lib/Dx11/Debug/EffekseerSoundXAudio2.lib
としてコピー。

【libファイル(リリース用)】
Compiled/lib/VS2015/Release/Effekseer.lib
Compiled/lib/VS2015/Release/EffekseerRendererDX11.lib
Compiled/lib/VS2015/Release/EffekseerSoundXAudio2.lib
を
Libs/lib/Dx11/Release/Effekseer.lib
Libs/lib/Dx11/Release/EffekseerRendererDX11.lib
Libs/lib/Dx11/Release/EffekseerSoundXAudio2.lib
としてコピー。
 以上で準備は終わりです。もし、BaseCross内にLibs/lib/Dx11/などのディレクトリがない(存在しない)場合は、手作業で作成してもいいですが、ほかのサンプルをデバッグモードとリリースモード両方でリビルドしてみてください。DitectXTXのライブラリがビルドされたときにディレクトリも作成されます。

サンプルの起動

 FullTutorial013BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。
 リビルドして実行すると以下の画面が出てきます。

 

図0113a

 


 ここでAボタンを押すと、派手なエフェクトがでます。このエフェクトはEffekseerのランタイムのダウンロードに含まれるテスト用のサンプルです。このようなエフェクトをBaseCrossでも再生できるようになります。

制限事項

 実は、このサンプルにはサウンドの情報も入ってます。エフェクト発射と同時に再生されるはずですが、BaseCrossで使用する場合はサウンド機能はこのサンプルでは使用できません。
 理由は、EffekseerX3DAudioという機能を使用してますが、これが入ってない環境もあるため、その場合、別にセットアップをする必要があります。できるだけ、Windows10デフォルトで動作するようにしたいので、Effekseerサウンド機能は実装してません。ただ、EffekseerSoundXAudio2のインクルード及びlibファイルの設置はしていても問題はありません。

コード解説

 EffekseerのランタイムはBaseCrossのライブラリには含まれません。ですので、ゲームコンテンツ側の実装となります。関係するのはGameSourcesの中のEfkInterface.h/cppです。これは、フルバージョンだけではなく、シンプルバージョンでもこの2つのファイルをプロジェクトに実装すればEffekseerのデータを再生できるようになります。
 また、Effekseerのランタイムの使用は、このサンプルのようにEfkInterface.h/cpp内のクラスを使う必要があるわけではありません。Effekseerのランタイムは、非常に使いやすくインターフェイスが整っているので、べたに記述しても簡単に利用可能でしょう。

 このファイルの中にはEfkEffectクラス、EfkPlayクラス、EfkInterfaceクラスの3つのクラスがあります。それぞれ役割があります。

EfkEffectクラス

 このクラスはエフェクトファイル(.efk拡張子)のオブジェクトを管理します。エフェクトを使うGameObjectなどで実装します。

EfkPlayクラス

 このクラスは実際に再生中のオブジェクトを管理します。EfkEffectランタイムではハンドルという概念で各エフェクトがオブジェクト化されます。EfkPlayクラスはこの各ハンドルをカプセル化したものです。

EfkInterfaceクラス

 このクラスはEffekseerマネージャとレンダラーをカプセル化しています。

 これらのクラスはBaseCrossとの相性が良いようにラッピングされたものですが、Effekseerは生ポインタを使用していて、自分でオブジェクトの解放をしなければなりません。スマートポインタを使うことで、これらの処理をクラス内に収めることができますのでこのような設計になってます。ですから、上記の3つのクラスを使用する分には、メモリの解放は意識しなくても大丈夫です。

コードの記述

 まずEffekseerを使うゲームではEfkInterface.h/cppをプロジェクトに加え、Project.hに以下を追加します。
#pragma once

#include "EfkInterface.h"
#include "ProjectShader.h"
#include "ProjectBehavior.h"
//以下略
 このようにして、GameStageクラスの宣言などに、以下のように記述します
class GameStage : public Stage {
    //エフェクトのインターフェイス
    shared_ptr<EfkInterface> m_EfkInterface;
//中略
public:
//中略
    //初期化
    virtual void OnCreate()override;
    //エフェクトのインターフェイスの取得
    shared_ptr<EfkInterface> GetEfkInterface() const {
        return m_EfkInterface;
    }
    //更新
    virtual void OnUpdate() override;
    //描画
    virtual void OnDraw() override;
};
 このようにしてGameStage::OnCreate()に以下のように記述します
    void GameStage::OnCreate() {
        try {
//中略
            //エフェクト作成
            m_EfkInterface = ObjectFactory::Create<EfkInterface>();

//中略
            //プレーヤーの作成
            CreatePlayer();
        }
        catch (...) {
            throw;
        }
    }
 エフェクトは今回はプレイヤーで使うので、プレイヤーが構築される前に実装しておきます。
 続いてOnUpdate()とOnDraw()です。多重定義して実装します。
//更新
void GameStage::OnUpdate() {
    m_EfkInterface->OnUpdate();
}
//描画
void GameStage::OnDraw() {
    auto& camera = GetView()->GetTargetCamera();
    m_EfkInterface->SetViewProj(camera->GetViewMatrix(), camera->GetProjMatrix());
    m_EfkInterface->OnDraw();
}
 続いて、プレイヤーです。以下はヘッダです。
class Player : public GameObject {
    //エフェクト
    shared_ptr<EfkEffect> m_EfkEffect;
    //エフェクト実行オブジェクト
    shared_ptr<EfkPlay> m_EfkPlay;
//中略
public:
//中略
};
 以下はPlayer::OnCreate()です。エフェクトファイルを読み込みます。
void Player::OnCreate() {
//中略

    //エフェクトの初期化
    wstring DataDir;
    App::GetApp()->GetDataDirectory(DataDir);
    wstring TestEffectStr = DataDir + L"Effects\\test.efk";
    auto ShEfkInterface = GetTypeStage<GameStage>()->GetEfkInterface();
    m_EfkEffect = ObjectFactory::Create<EfkEffect>(ShEfkInterface, TestEffectStr);

    //ステートマシンの構築
    m_StateMachine.reset(new StateMachine<Player>(GetThis<Player>()));
    //最初のステートをPlayerDefaultに設定
    m_StateMachine->ChangeState(PlayerDefaultState::Instance());
}
 このようにステートマシンの前に実装するとよいでしょう。
 ここでサウンドコンポーネントの初期化とありますが、前述したようにEffekseerのサウンド機能は利用できないので、BaseCrossに含まれるサウンドコンポーネントを実装しています。またこのWAVはシーン構築時にリソース化されたものです。
 最後に、実際にエフェクトの発射です。AボタンのハンドラであるPlayer::OnPushA()に記述します。
//Aボタンハンドラ
void  Player::OnPushA() {
    if (GetStateMachine()->GetCurrentState() == PlayerDefaultState::Instance()) {
        auto Ptr = GetComponent<Transform>();
        //エフェクトのプレイ
        auto ShEfkInterface = GetTypeStage<GameStage>()->GetEfkInterface();
        m_EfkPlay = ObjectFactory::Create<EfkPlay>(m_EfkEffect, Ptr->GetPosition());
        //サウンドのプレイ
        auto XAPtr = App::GetApp()->GetXAudio2Manager();
        XAPtr->Start(L"cursor", 0, 0.5f);
        //通常ステートならジャンプステートに移行
        GetStateMachine()->ChangeState(PlayerJumpState::Instance());
    }
}
 このように、ボタンが押されるたびにm_EfkPlayが上書きされるようになっています。
 m_EfkPlayshared_ptrですので、上書きされるたびに、まえのshared_ptrのデストラクタが動きます。そのデストラクタでは、実行中のエフェクトをストップしてから破棄されるよう記述があるので、ハンドルが中途半端に残ることはありません。
 もし、複数の、同じエフェクトを同時に表示させる場合はm_EfkPlayを1つではなく配列で管理します。そうするとハンドルの再利用ができるようになります。