執筆者 / 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

ブログ一覧へ戻る