【Unreal C++】⑨モジュール 【UE4】
C++でコードを書く際、UE4の公式ドキュメントで調べていると思いますが、UnrealC++ではHeaderファイルをincludeするだけではだめみたいです。
以下によるとUE4のC++コードは、「モジュール」と呼ばれる単位で、「UnrealBuildTool」を通してビルドされるそうです。なので必要に応じて追加してあげる必要があります。
[UE4] モジュールについて|株式会社ヒストリア
モジュールに関してUE4.15以降で仕様変更があったので以下を参照してください。
ちなみに自分の環境はver4.18.3です。
miyahuji111.hatenablog.com
追加方法
①[ProjectName].Build.cs
([ProjectFolder]/source/[ProjectName]/)にある[ProjectName].Build.csを開くと、以下のようになっていると思います。
using UnrealBuildTool; public class hogeProject : ModuleRules { public hogeProject(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); PrivateDependencyModuleNames.AddRange(new string[] { }); } }
②Moduleの追加
公式ドキュメントのAPIのリファレンスを見るとModuleの項目があり、
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "UMG","UMGEditor" });
のnew string[] 以下の部分になければ "" で囲んで追加することでその関数を使用することが出来ます。
【Unreal C++】 ⑧Level Streaming 【UE4】
C++でLevel Streaming 使うときにちょっと躓いたのでメモ。
BPでのLevel Streamingは以下を参考に。
unrealengine.hatenablog.com
詳しくは以下に書いていますがロード後にすぐ表示しているので、今回はロード後すぐに表示しないパターンを書いておきます。
docs.unrealengine.com
ロード
ロード後すぐに表示しないパターンにするため
第3引数・・・読み込み後に表示を行うかどうか
第4引数・・・同期読み込みを行うかどうか
を両者ともfalseにしました。
docs.unrealengine.com
[UE4] レベルストリーミングについて|株式会社ヒストリア
マップ(サブレベル)表示
BPと大体同じで
第1引数・・・表示非表示をいじりたいマップ
第2引数・・・表示非表示のフラグ
UFunctionLibrary::beVisibleMap(MapName, true);
アンロード
関数については以下を参考に。
docs.unrealengine.com
マップ(サブレベル)非表示
UFunctionLibrary::beVisibleMap(MapName, false);
Tips
マップの遷移で分岐があったので2つのマップを同時にロードする必要があり、FLatentActionInfo型の変数を2つ用意するだけでいいのかなと思ったのですが、1つだけしかロードされませんでした。
調べてみると、UUIDで識別しているみたいで2つの変数に別々のUUIDを与えてあげることで2つロードすることができました。
ちなみにUUIDのデフォルトの値は-1みたいです。
【Unreal C++】⑦イベントディスパッチャー【UE4】
今回はイベントディスパッチャーについてです。
BPでの実装は以下を参考に。
unrealengine.hatenablog.com
動的マルチキャストデリケートでBP側のイベントディスパッチャーと同等の処理が出来るみたいです。
呼び出し側
DECLARE_DYNAMIC_MULTICAST_DELEGATEマクロを用いてクラスを作成します。
命名規則からクラス名には「F」の接頭辞が必要です。
また宣言の際にプロパティをBlueprintAssignableにする必要があります。
Call.h
#include "CoreMinimal.h" #include "Call.generated.h" DECLARE_DYNAMIC_MULTICAST_DELEGATE(FhogeDispather); UCLASS() class HogeProject_API ACall : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties ACall(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; //Event Dispather(Multicast Delegate) UPROPERTY(BlueprintAssignable) FhogeDispather hogeDispather; }
先ほど作成したクラスの変数のBroadcastメソッドを呼んであげます。
Call.cpp
#include "Call.h" ACall::ACall() { } ACall::HogeFunc() { hogeDispather.Broadcast(); }
受信側
Funcは呼び出したい処理です。
UFUNCTION()がついてないとクラッシュしました。
Receive.h
#include "CoreMinimal.h" #include "Call.h" #include "Engine.h" //GEngine #include "Receive.generated.h" UCLASS() class HogeProject_API AReceive : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AReceive(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; //Event Dispather(Multicast Delegate) UFUNCTION() void Func(); }
BeginPlayでAddDynamicを用いて作成したクラスの変数と呼びたい処理をバインディングします。
Receive.cpp
#include "Receive.h" // Sets default values AReceive::AReceive() { } // Called when the game starts or when spawned void AReceive::BeginPlay() { Super::BeginPlay(); //Event Dispather for (TActorIterator<ACall>ActItr(GEngine->GameViewport->GetWorld()); ActItr; ++ActItr) { ACall* Call = *ActItr; Call->hogeDispather.AddDynamic(this, &AReceive::Func); } } void AReceive::Func() { //呼び出したい処理 }
以上で呼び出し(call)側のHogefunc関数が呼ばれるタイミングで受信(Receive)側のFunc関数が同時に呼ばれます。
【Unreal C++】⑥Interface【UE4】
今回はC++とブループリントでインターフェースを呼ぶ機会があったので備忘録として。
他にもいい方法があると思うのであくまで参考程度でお願いします。
エンジンはVer4.18.3を使用しています。
ちなみにブループリントインターフェースなどインターフェースについては以下のリンクを参照してください。
docs.unrealengine.com
unrealengine.hatenablog.com
①クラスの作成&宣言
おそらくBP側で作成したインターフェースはC++側に呼ぶことが出来ないのでC++側でインターフェースを作成します。C++でインターフェースを継承したクラスを作成します。
関数指定子をBlueprintImplementableEventでもいけるという情報もありましたが、参考したサイトの多くがBlueprintNativeEventを使用していたのでこちらを使用します。
unreallife.hatenablog.com
hogeInterface.h
#include "CoreMinimal.h" #include "hogeInterface.generated.h" UINTERFACE(Blueprintable) class hogeProject_API UhogeInterface : public UInterface { GENERATED_UINTERFACE_BODY() }; class hogeProject_API IhogeInterface { GENERATED_IINTERFACE_BODY() public: UFUNCTION(BlueprintNativeEvent, BlueprintCallable) void hogeFunc(); }
hogeInterface.cpp
#include "hogeInterface.h" UhogeInterface::UhogeInterface(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer) { } void IhogeInterface::hogeFunc_Implementation() { }
ブループリントでの実装
イベント側
「Event hogeFunc」を呼んであげます。
イベントを呼び出すためには「Class Settings→interface→Implemented Interface」に先ほど作成したhogeInterfaceを追加してコンパイルする必要があります。
呼び出し側
「hogeFunc(Message)」を呼んであげます。
Targetノードにはイテレータ、Get All Actors with Interface、Get All Widgets with Interfaceなどを用いて検索したイベント側のクラスを繋いであげます。
自分はレベルストリーミング使用しているのでGet All Widgets of Classの第2引数のTop Level Onlyはfalseにしました。
C++での実装
イベント側(hogeActor)
hogeInterface を継承したクラスを作成し、関数をOverrideしてあげます。
hogeActor.h
#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "hogeInterface.h” #include "hogeActor.generated.h" UCLASS() class hogeProject_API AApple : public AActor, public IhogeInterface { GENERATED_BODY() public: //Interface virtual void hogeFunc_Implementation() override; };
hogeActor.cpp
void hogeActor::hogeFunc_Implementation() { //処理 }
呼び出し側
検索して配列に格納してRanged-forを用いて配列に対して処理をしてあげます。
TArray<AhogeActor*>hogeActAry;
UWorld* World=GEngine->GameViewport->GetWorld();
UGameplayStatics::GetAllActorsWithInterface(World,UhogeInterface::StaticClass(),hogeActAry);
for(AhogeActor* hogeAct : hogeActAry)
{
IhogeInterface::Execute_hogeFunc(hogeAct);
}
Get All Actors of Class、Get All Widgets of Classなどは遅い処理なのでTickなどで呼ばないでください。
docs.unrealengine.com
【Unreal C++】⑤Timer(Delay)【UE4】
BPのDelayに相当する処理をするにはTimerを使うようです。
他に方法があればコメントで教えて頂けると幸いです。
Timerの開始
Timerを開始するにはFTimerManagerのSetTimer 関数を用います。
World->GetTimerManager().SetTimer(_TimerHandle,Callback,Rate,bLoop,FirstDelay);
引数 | 型 | 説明 |
_TimerHandle | FTimerHandle | Timerを管理する変数 |
Callback | TFunction | 呼びたい関数 |
Rate | float | 関数を何秒ごとに呼ぶか |
bLoop | bool | ループするかどうか |
FirstDelay | float | 最初に何秒待つか |
例(カウントタイマー)
hogeActor.h
UCLASS() class HOGEPROJECT_API AhogeActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AhogeActor(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; UFUNCTION() void Timer(); public: // Called every frame virtual void Tick(float DeltaTime) override; protected: UPROPERTY() UWorld* World; public: UPROPERTY() int PlayTime; };
hogeActor.cpp
void AhogeActor::BeginPlay() { World=GEngine->GameViewport->GetWorld(); FTimerHandle _TimerHandle; World->GetTimerManager().SetTimer(_TimerHandle, this, &AhogeActor::Timer,1.0f, true); } void AhogeActor::Timer() { PlayTime+=1; }
Timerの停止
Timerを停止するにはFTimerManagerのClearTimer 関数を用います。
また、もう一度同じTimerHadleでSetTimerを呼ぶと既存のものが破棄され、新しいものに置き換えられます。
ClearしたTimerHandleは新しいTimerを管理するために再利用することが出来ます。
World->GetTimerManager().ClearTimer(_TimerHandle);
引数 | 型 | 説明 |
_TimerHandle | FTimerHandle | Timerを管理する変数 |
Timerの一時停止
Timerによる関数呼び出しを一時的に実行しないようにするにはFTimerManagerのPauseTimer 関数を用います。
World->GetTimerManager().PauseTimer(_TimerHandle);
Timerの再開
一時停止したTimerを再開させるにはFTimerManagerのUnPauseTimer 関数を用います。
World->GetTimerManager().UnPauseTimer(SampleTimerHandle);
Timerが一時停止状態の確認
Timerが一時停止しているかどうかを確認するためにはFTimerManagerのIsTimerPaused 関数を用います。bool値がreturnされます。
if( World->GetTimerManager().IsTimerPaused(_TimerHandle) == true ) { }
反対にTimerがアクティブで一時停止していないことを確認するためにはFTimerManagerのIsTimerActive 関数を用います。bool値がreturnされます。
if( World->GetTimerManager().IsTimerActive(_TimerHandle) == true ) { }
追記
(2018.01.10)
↓のドキュメントではループありのタイマーをClearTimer関数でタイマーのクリアを行っています。
これは実質、タイマーの停止も担っています。
ここで1つ疑問に思ったのが、ループなしのタイマーでは自動的に停止するのでタイマーの停止を目的としたClearTimer関数の使用は必要無くなりますが、クリアを目的としたClearTimerが必要なのかということです。
docs.unrealengine.com
APIにはIsTimerActive関数やTimerExists関数がありますので、こちらを用いてループなしのタイマーが自動停止した後にまだタイマーが存在しているかを調べてみたいと思います。
docs.unrealengine.com
調査
いろいろ調べ方はあると思いますが、簡単な方法で実装します。
bLoopをfalseとしたSetTimer関数をBeginPlayで呼びます。
IsTimerActive関数やTimerExists関数をTickで呼びます。
TimerAct.h
#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Engine.h" #include "TimerAct.generated.h" UCLASS() class HOGEPROJECT_API ATimerAct : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties ATimerAct(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: // Called every frame virtual void Tick(float DeltaTime) override; protected: UWorld* World; FTimerHandle _TimerHandle; protected: UFUNCTION() void Process(); };
TimerAct.cpp
void ATimerAct::BeginPlay() { World=GEngine->GameViewport->GetWorld(); FTimerHandle _TimerHandle; World->GetTimerManager().SetTimer(_TimerHandle, this, &ATimerAct::HogeProcess,1.0f, false); //bLoop=false } void ATimerAct::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (World->GetTimerManager().IsTimerActive(_TimerHandle)) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, "Active"); } if (World->GetTimerManager().TimerExists(_TimerHandle)) { GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Red, "Exist"); } } void ATimerAct::HogeProcess() { }
結果
プレイ開始からHogeProcessが呼ばれるまでタイマーが存在し、その後は自動的にクリアされ、存在しなくなっているようです。
※何か間違えがあればコメントよろしくお願い致します。