N煎ログブログ

n番煎じと言っても過言ではない今更な、でも個人的に躓いたUnityやUE4等での開発についての云々を書いていきます

【UE4】C++でExecuteConsoleCommand実行関数ライブラリBP作ってみた

目次

C++で書こうと思ったきっかけ

 つまるところ↑ツイートの通りなのですが、おおまかな処理等を構築する時にはBPは非常に有効です。しかし計算やループ(forではなく値の)処理を構築する場合はBPだと時間がかかる上に、見た目ブロック状のものが画面いっぱいになりそれらを繋ぐ線で埋まるので管理が難しくなってしまいます。

そして今回の場合はExecuteConsoleCommandをC++で実行させたいと思いました。

というのも、ExecuteConsoleCommandでゲームのクオリティ設定を行いたかったのですが、設定分ExecuteConsoleCommandノードがどんどん伸びていき、線も増えていき、コマンドの管理も時間がかかり始めていた、という背景があります。

というわけで書いてみました。ちなみにUE4でのC++は初です。

C++(UE4)は友達! 怖くないよ!

ガッ

f:id:isemito:20171215225635j:plain

というわけでいきなり噛みつかれました。

まず、当たり前といえば当たり前ですが初見でUE4での書き方が全くわからない。

とりあえず関数ライブラリを作り、Blueprint Function Libraryを親としてC++ファイルを作成

f:id:isemito:20171215230104p:plain

開いてみると...

[SystemFunctionLibrary.h] 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "SystemFunctionLibrary.generated.h"

/**
 * 
 */
UCLASS()
class TIMEROBBER_API USystemFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()




};

 

[CalcFunctionLibrary.cpp]

// Fill out your copyright notice in the Description page of Project Settings.

#include "SystemFunctionLibrary.h"

 

ギリギリギリ...

f:id:isemito:20171215225635j:plain

f:id:isemito:20171215233555j:plain

色々調べてみてこちらの記事が見つかりましたので参考に進めていきます。

unrealengine.hatenablog.com

staticでselfピンを無くす

 静的になるので当然ですが、staticにするとselfピンがなくなり、オブジェクトを渡す必要がなくなります。

例えば、

UFUNCTION(BlueprintCallable, Category = "System")
void TestFunction(int _test);

こう記述すれば...

f:id:isemito:20171215234043p:plain

◯- ターゲット[self]」というピンが表示されますが、

UFUNCTION(BlueprintCallable, Category = "System")
static void TestFunction(int _test);

こう記述すれば、

f:id:isemito:20171215234354p:plain

 selfピンが消えます。

おっじゃあstatic付けてExecuteConsoleCommand実行するC++コードを書けば良いんだなと思い検索かけてみた所、 

forums.unrealengine.com

 このポストにて

#include "Engine/World.h"
GetWorld()->Exec(GetWorld(), TEXT("MyAwesomeConsoleCommand X Y Z"));

 こう記述すれば実行できるよ! という情報がありました。

これでC++で ExecuteConsoleCommandを実行しましょう。

 

怖くない...怖くない...

f:id:isemito:20171215235043j:plain

ガッ

f:id:isemito:20171215225635j:plain

まだ駄目です。 

なぜなら、

f:id:isemito:20171215235428p:plain

 「GetWorld()」はconst修飾であり、sttaic関数内で使用できません。

「 じゃあconst付ければ使える..?」

と思いきやそうするともちろんstaticは外さなければならず故にselfピンが表示されてしまいます。

ExecuteConsoleCommandはどこにある?

ここで、そもそもExecuteConsoleCommandはどのソースコードにあるのか?

それを参考に書けば良いのではないか?

自力で探してみたものの見つからなかったのでAnswerHubにて質問した所、

ExecuteConsoleCommandと同等の機能をC++で実装したい - UE4 AnswerHub

#include "Kismet/KismetSystemLibrary.h"
void ExecuteConsoleCommand(UObject* WorldContextObject, const FString& Command, APlayerController* Player)

「KismetSystemLibrary.h」にありました。

 

...あれ?

 

f:id:isemito:20171216000220p:plain

書いてあったああああああああああ!!!!

なるほど。

ともあれ、これでExecuteConsoleCommandを直接呼び出すことが可能になりました。

WorldContextObjectとは

ExecuteConsoleCommandを直接呼び出す方法はわかりましたが、今度は見覚えのない言葉が出てきました。

f:id:isemito:20171216000606p:plain

WorldContextObjectとは一体?

まず、WorldContextObjectの型はUObjectであり、これはUE4内全てのオブジェクトの親クラスになるものです。そして、

[C++] WorldContextObject Parameter - UE4 AnswerHub

にて回答者様がおっしゃっているとおり、WorldContectObjectはつまり「GetWorld()」を持つオブジェクトを指す。つまりWorldContectObject(UObject)経由で「GetWorld()」を呼び出しているということになるようです。

そしてExecuteConsoleCommandは渡されたWorldContextObjectからPlayerControllerを取得し、ConsoleCommandを実行しています。

[ExecuteConsoleCommand関数 中身]

void UKismetSystemLibrary::ExecuteConsoleCommand(UObject* WorldContextObject, const FString& Command, APlayerController* Player)
{
    // First, try routing through the primary player
    APlayerController* TargetPC = Player ? Player : UGameplayStatics::GetPlayerController(WorldContextObject, 0);
    if( TargetPC )
    {
        TargetPC->ConsoleCommand(Command, true);
    }
}

メタデータ指定子

では、肝心のWorldContextObjectにはどうやって値を渡せば良いのか?

それは、「メタデータ指定子(meta)」と呼ばれるものを使用します。

docs.unrealengine.com

 上サイト内の「関数メタデータ指定子」に

WorldContext="Parameter"

BlueprintCallable 関数によって使用され、オペレーションが起こるワールドをどのパラメータが決めるかを示します。

 という説明があります。

つまりこの場合「どのパラメータが決めるか」というのは「WorldContextObject」であり、故に「"Parameter"」の箇所には「WorldContextObject」を割り当てます。そうすることで、オペレーションが起こるワールドはWorldContextObjectが持つことを示すことになるのかもしれません(勉強不足でまだ完全には理解できていません)。

出来た!

ともあれ、これでstaticしつつExecuteConsoleCommandを呼び出すことが出来るようになりました。

最終的なコードは以下になります。

最初に述べたとおりゲームのクオリティ設定が元々の目的だったのでクオリティ設定のコードになっています。

[SystemFunctionLibrary.h]

UCLASS()
class TIMEROBBER_API USystemFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
    //クオリティ設定反映
    UFUNCTION(BlueprintCallable, Category = "System", meta = (WorldContext = "_worldContextObject"))
    static void ApplyGameQuality(
        UObject* _worldContextObject,
        int _resolutionScale,
        float _viewDistance,
        int _antiAlias,
        int _postProcessQuality,
        int _shadowQuality,
        int _textureQuality,
        int _effectQuality
    );
};

[SystemFunctionLibrary.cpp]

void USystemFunctionLibrary::ApplyGameQuality(
    UObject* _worldContextObject,
    int _resolutionScale,
    float _viewDistance,
    int _antiAlias,
    int _postProcessQuality,
    int _shadowQuality,
    int _textureQuality,
    int _effectQuality) {
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ResolutionQuality " + FString::FromInt(_resolutionScale));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ViewDistanceQuality " + FString::SanitizeFloat(_viewDistance));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.AntialiasingQuality " + FString::FromInt(_antiAlias));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.PostProcessQuality " + FString::FromInt(_postProcessQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ShadowQuality " + FString::FromInt(_shadowQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.TextureQuality " + FString::FromInt(_textureQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.EffectsQuality " + FString::FromInt(_effectQuality));
}