2020/9/14エンジニア

    [Unreal Engine] プラグインとActorの連携について[Part.1]

    執筆者 / K.T

    はじめに

    UE4で、レベルに依存しない汎用的な処理を、プラグインとして切り離しておくと便利です。

    プラグインに、アクタとの連携機能やティック機能を持たせれば、より使い勝手が良くなります。

    そのようなプラグインのサンプルを作成してみます。

    この記事の内容

    長いので、2回に分けてお送りします。

    今回(1/2)は、サンプルのプロジェクトを作成した上で、以下のような機能を持つプラグインを追加します。

     1.デリゲートを持ち、アクタのイベント関数を結合できる

     2.ティック機能を持ち、ティック関数が呼び出される

     3.ティック関数でデリゲートを実行し、パラメータ(角度)をアクタのイベント関数へ送る

     4.パラメータ(角度)は等速で増え続ける

    次回(2/2)は、アクタがイベント関数でパラメータ(角度)を受け取り、自身のZ軸回転に反映させるようにします。

    つまりアクタがコマのようにくるくる回るようにします。

    また、カメラをアクタの背後に置いて、アクタと一緒に回転するようにします。

    これによって生じる問題点についても解説し、修正を行います。

    用意するもの

    ・Unreal Engine 4.23.1

    ・Visual Studio 2017

    プロジェクトの作成

    プロジェクトブラウザを起動し、新規プロジェクトを作成します。

    C++タブの基本コードを選択し、スターターコンテンツは有りにしましょう。

    プロジェクトを作成したら、いったん閉じます。

    そして、できあがったプロジェクトのContentフォルダ内に、Flyingというフォルダを作成してください。

    作成したFlyingフォルダの中に、以下のフォルダから4つのアセットファイルをコピーします。

    UE_4.23\Templates\TemplateResources\Standard\Flying\Content\Meshes

    ここまでできたら、プロジェクトを起動してください。

    メッシュの設定

    コンテンツ/Flyingフォルダ内のUFOMaterialを開きます。

    詳細の[一般]-[Parent]にBaseMaterialを設定すると、パラメータグループが現れるので、保存後に閉じてください。

    続いて、同じくFlyingフォルダ内のUFOを開きます。

    詳細の[Material Slots]-[エレメント0]にUFOMaterialを設定すると、メッシュが水色に変わるので、保存後に閉じてください。

    アクタの設定

    コンテンツフォルダ内にブループリントクラスを新規追加します。

    親クラスはActorにしてください。

    作成したActorFlyingを開きます。

    DefaultSceneRootの下に、StaticMeshComponentを追加してください。

    詳細の[Static Mesh]-[Static Mesh]にUFOを設定します。

    また、[Materials]-[エレメント0]にUFOMaterialを設定します。

    コンパイルして保存後に閉じてください。

    ActorFlyingをビューポートにドラッグ&ドロップして、マップへ配置します。

    詳細の[トランスフォーム]で、アクタの位置や回転を図のように設定してください。

    プラグインの作成

    メニューの[編集]-[プラグイン]を選択します。

    [New Plugin]ボタンを押してください。

    Blueprint Libraryを選択し、[Name]にControlActorと入力してください。

    [Create Plugin]ボタンを押すと、プラグインのひな形が作成されます。

    プラグインの編集

    Visual Studioでソリューションが開かれるので、プラグインのソースコードを編集していきましょう。

    図でフォーカスされている4つのファイルが対象です。

    編集が終わったら、ソリューションをビルドして、保存後に閉じてください。

    ControlActorBPLibrary.h

    #pragma once

    #include "Kismet/BlueprintFunctionLibrary.h"
    #include "ControlActorBPLibrary.generated.h"

    // 動的デリゲートの宣言(引数としてfloatを1つ渡します)
    DECLARE_DYNAMIC_DELEGATE_OneParam(FControlActorEvent,
    float, RotYaw);

    UCLASS()
    class UControlActorBPLibrary : public UBlueprintFunctionLibrary
    {
         GENERATED_UCLASS_BODY()

        
    // イベント関数を結合(ブループリントから呼び出せる関数です)
         UFUNCTION(BlueprintCallable, Category=
    "ControlActor")
        
    static void    BindEvent(UObject* object, FName func_name);
    };


    ControlActorBPLibrary.cpp

    #include "ControlActor.h"

    // コンストラクタ
    UControlActorBPLibrary::UControlActorBPLibrary(
    const FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
    {
    }

    // イベント関数を結合
    void UControlActorBPLibrary::BindEvent(UObject* object, FName func_name)
    {
         FControlActorModule::BindEvent(object, func_name);
    }


    ControlActor.h

    #pragma once

    #include "Modules/ModuleManager.h"
    #include "Tickable.h"
    #include "ControlActorBPLibrary.h"

    class FControlActorModule : public IModuleInterface, public FTickableGameObject
    {
    private:
         FControlActorEvent      m_Event;      
    // イベント用のデリゲート

    public:
        
    // モジュール関連の関数
        
    virtual void        StartupModule() override {}
        
    virtual void        ShutdownModule() override {}

        
    static inline FControlActorModule&     Get(void){ return FModuleManager::LoadModuleChecked< FControlActorModule >( "ControlActor" ); }
        
    static inline bool      IsAvailable(void){ return FModuleManager::Get().IsModuleLoaded( "ControlActor" ); }
        
    static FControlActorModule*       GetInstance(void){ return ( FControlActorModule::IsAvailable() ? &Get() : nullptr ); }

        
    // ティック関連の関数
        
    virtual TStatId     GetStatId(void) const { RETURN_QUICK_DECLARE_CYCLE_STAT(FControlActorModule, STATGROUP_Tickables); }
        
    virtual bool        IsTickable(void) const { return true; }
        
    virtual void        Tick(float delta_time);

        
    // イベント関数を結合
        
    static void         BindEvent(UObject* object, const FName& func_name);
    };


    ControlActor.cpp

    #include "ControlActor.h"

    #define LOCTEXT_NAMESPACE "FControlActorModule"

    // ティック
    void FControlActorModule::Tick(float delta_time)
    {
        
    static float   rot_yaw = 0.0f;

         rot_yaw +=
    5.0f;        // 角度を更新
        
    if(rot_yaw > 360){ rot_yaw -= 360; }

         m_Event.ExecuteIfBound(rot_yaw);      
    // デリゲートを実行
    }

    // イベント関数を結合
    void FControlActorModule::BindEvent(UObject* object, const FName& func_name)
    {
         FControlActorModule*   
    module = GetInstance();

        
    if(module == nullptr){ return; }       // インスタンスの取得失敗

        
    if((object == nullptr) || func_name.IsNone()){
             
    module->m_Event.Clear();     // 引数が不正
         }
    else{
             
    // デリゲートにイベント関数を結合
             
    module->m_Event.BindUFunction(object, func_name);
         }
    }

    #undef LOCTEXT_NAMESPACE
        
    IMPLEMENT_MODULE(FControlActorModule, ControlActor)

    プラグインの解説

    コメントでだいたい理解できると思いますが、ポイントとなる部分について簡単に解説します。

    ・ControlActorBPLibrary.h(7行目)

    FControlActorEventという動的デリゲートを宣言しています。

    プラグインからアクタへ、イベントという形で情報を送るためのものです。

    ・ControlActorBPLibrary.h(16行目)

    BindEventという関数を宣言しています。

    この関数は、ブループリントから呼び出すことが可能です。

    第1引数のobjectには、イベント関数を保有するアクタオブジェクトを渡します。

    第2引数のfunc_nameには、イベント関数名を渡します。

    ・ControlActor.h(7行目)

    FTickableGameObjectというクラスを継承しています。

    モジュールクラスにティック機能を付加するためのものです。

    まとめ

    今回(1/2)は、プロジェクトを作成し、アクタやプラグインなど必要なものを追加しました。

    また、プラグインのソースコードを編集し、ティック機能やデリゲートを実装しました。

    次回(2/2)はこのプラグインとアクタを連携させて、アクタが動くようにします。

    参考リンク

    デリゲート
    FTickableGameObject

    ブログ一覧へ戻る