PaperSloth’s diary

主にゲーム開発関連についての記事を書きます。

UE4 Blueprintで2次元配列を作成する方法

環境

UE4.22.2

Blueprintでの2次元配列の作成方法

先に結論だけを書くと

  • 配列を持った構造体を用意
  • その構造体を配列で定義

上記の2ステップでできます。


コードで示すと下記になります。

USTRUCT(BlueprintType)
struct FIntArrayElement
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game")
	TArray<int32> IntArray;
};

UCLASS()
class PROJECT_API AExampleActor : public AActor
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Game")
	TArray<FIntArrayElement> TwoDimensionIntArray;
};

f:id:PaperSloth:20190531013637p:plain



これぐらいであれば、Blueprintでも表現可能です。

下図のようにArray(配列のメンバー)をもった構造体を定義します。
構造体の作成方法はContent Browserから
Blueprints -> Structure を選択です

次の手順でおそらく警告が出ると思うので、1つ以上の要素を追加しておきます。
f:id:PaperSloth:20190531014219p:plain


次に2次元配列をもたせたいBlueprintに変数を追加します。
その際にVariable Typeを先程作成した構造体に変更し
Arrayに変更します。

すると下図のように配列の中に配列を持った2次元配列を作ることができます。
f:id:PaperSloth:20190531014328p:plain


通常のままでは構造体になっており、使用できないため
構造体の出力ピンから構造体を分割するか
Break (構造体名)で構造体を分割して使用します。
f:id:PaperSloth:20190531014803p:plain


個人的には極力多次元配列を避けて開発したいのですが
どうしても必要という方もいると思ったので書きました。

以上です。

UE4 Unreal C++でのEnumとStringの変換について

環境

UE4.20.3
Visual Studio 2017 Community

Unreal C++でのenumについてはこちら
papersloth.hatenablog.com

Enumから文字列への変換

先ずは列挙型の宣言と定義から。

UENUM(BlueprintType)
enum class EConnectionType : uint8
{
    Multicast,
    Unicast,
    NUM    UMETA(Hidden)
};

class PROJECT_API AExampleActor: public AGameModeBase
{
	GENERATED_BODY()

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Enum")
	EConnectionType ConnectionType;
}

続いてBlueprint上でのノードの変換がこちら
f:id:PaperSloth:20190514231350p:plain


Blueprintでは上図のようにEnumからStringにCastしてくれます。
しかし、C++側ではこの機能は標準で実装されていないようです。
私が知らないだけという可能性が非常に高いですが。

今回の例は結構強引な型変換を行って実装しているため
既存機能で補えたり、より良い書き方があれば教えてください。


EnumからStringへの変換についてです。
今回は説明のためにエラー処理を省略しているため
実際に使う際はエラーチェックを行うことを推奨します。

FString AExampleActor::EnumToString(const EConnectionType Type, const FString& EnumName)
{
    UEnum* const Enum = FindObject<UEnum>(ANY_PACKAGE, *EnumName);
    return Enum->GetNameStringByIndex(static_cast<int32>(Type));
}

この書き方で動作はしますがEnumNameを都度渡したり
列挙型自体を引数に渡したりと汎用性は低いです。

文字列からEnumへの変換

特に使う場面も浮かびませんが、ついでなので書いておきます。

EConnectionType AExampleActor::StringToEnum(const FString& TypeName, const FString& EnumName)
{
    UEnum* const Enum = FindObject<UEnum>(ANY_PACKAGE, *EnumName);
    int64 Value = Enum->GetValueByName(FName(*TypeName));
    return static_cast<EConnectionType>(Value);
}


上記2つの関数はUFUNCTIONマクロを付けてBlueprint上でも使用できますが
Blueprintでは
Enum -> FString は既に用意されています。
FString -> Enum は最初に書きましたが、特に使う場面が浮かびません。


以上、こういう書き方もありますよという紹介でした。

UE4 PluginのHotReloadについて

環境

UE4.22.1
Visual Studio 2017 Community

概要

通常のProject同様にEditorからCompileボタンを押してHotReload!
というわけにはいかず悩んだ人もいるかもしれないと思ったのでメモ書きを残しておきます。

PluginのHotReloadについて

まず、EditorにあるCompileボタンでHotReloadが行われるのはGame Projectのコードのみです。

Pluginは別の手順でRecompileを行う必要があります。
Window -> DeveloperTools -> Modules を選択してModules Windowを開きます。
f:id:PaperSloth:20190507224617p:plain

あとはそのWindowからHotReloadを行いたいPluginを探してRecompileボタンを押すだけです。
f:id:PaperSloth:20190507224737p:plain


Compile Complete!
f:id:PaperSloth:20190507225143p:plain

UE4 JsonFileの読み込みについて

環境

UE4.20.3
Visual Studio 2017 Community
Windows10

概要

とりあえずでこのリポジトリに突っ込んでるJsonLoad関数を文字に書き起こしただけの備忘録です。
github.com

UE4のゲーム実行時のオプションやプロパティ等を外部ファイルで編集したいとか
用途は何でもいいんですが、JsonFileを使いたい時があるのでLoaderのメモ書きを残しておきます。

JsonFileの読み込みについて

まずJsonLoaderを作成するにあたって、UE4で既存のModuleを利用するため参照の追加が必要です。
ProjectのBuild.csにJsonを追加します。

PublicDependencyModuleNames.AddRange(
    new string[]
    {
        "Core",
        "Json", // 追加
    }
);


続いて実際のLoad処理を書いていきますが、UE4側で既にJsonObject等の便利なクラスが用意されているので、そちらを使っていきます。
今回は雑把にBlueprintFunctionLibraryに書いていきます。



次に簡単なJsonを用意しました。
とりあえず一通り使うであろうbool, int, array, string, vector(float)が入っています。

// Example.json
{
    "isFullScreen": false,
    "windowWidth": 1280,
    "exampleArray": [
        {
            "exampleString": "Hello Json",
            "offset": {
                "x": 100.0,
                "y": 0.100,
                "z": 3.14
            }
        }
    ]
}


ここまでで一旦JsonObjectとjsonの作成ができました。
しかし、このままでは利用できないためJsonObjectから実際に値を取り出すコードを簡単に書いていきます。

#include "JsonFunctionLibrary.h"

// pathは (ProjectDir)/Json/Example など
bool AExampleActor::LoadJson(const FString& Path)
{
    const auto JsonObject = UPSJsonFunctionLibrary::LoadJsonObject(JsonFile);
    if (JsonObject.IsValid() == false)
    {
        return false;
    }

    MaxFps = static_cast<float>(JsonObject->GetNumberField("MaxFps"));
    IsFullScreen = JsonObject->GetBoolField("isFullScreen");
    WindowWidth = JsonObject->GetIntegerField("windowWidth");

    const auto ArrayField = JsonObject->GetArrayField("exampleArray");
    for (const auto Field : ArrayField)
    {
        const auto ObjectField = Field->AsObject();
        ExampleString = ObjectField->GetStringField("exampleString");

        auto const OffsetField = ObjectField->GetObjectField("offset");
        Offset.X = static_cast<float>(OffsetField->GetNumberField("x"));
        Offset.Y = static_cast<float>(OffsetField->GetNumberField("y"));
        Offset.Z = static_cast<float>(OffsetField->GetNumberField("z"));
    }
    return true;
}

UE4 Editorでの動画像の撮影について

環境

UE4.20.3
Windows10

概要

SNSやHP等で制作中のスクリーンショットをアップすることはよくありますね。
その中でEditor上でスクリーンショットを撮影される方も多いでしょう。
ですが、せっかくの良いゲームなのに
デバッグログが出ていたりEditorOnlyのGUIが表示されているとすごく完成度が低く見えてしまい残念に感じます。
少しでも良いゲームがもっと良く見えるようになるといいなと思います。

ゲームのスクリーンショットの撮影について

まず、UE4ではF9キーを押下することでスクリーンショットを撮影することができますね。
撮影した画像は
(ProjectDir)\Saved\ScreentShots\Windows\ScreentShot(連番).png
に保存されます。
f:id:PaperSloth:20190129200652p:plain
ここまでは特に問題ありませんね。

EditorでHidden In Gameが有効なActor/Componentを非表示にする

ViewportでGキーを押下することでGameViewの切り替えができます。
Hidden In Gameのフラグが立っているActor/Componentを非表示にできます。

先ずありがちなのが、CollisionやSpline等の実行中に表示されないComponentがスクリーンショットに写っている例です。

「新ステージを実装しました!」と下記のような画像が出てくるよりは
f:id:PaperSloth:20190129201439p:plain

下記のような画像の方がまだいいですね。
f:id:PaperSloth:20190129201601p:plain

Editor画面がまるまる写っている場合

ViewportでF11キー押下でImmersive Modeにしてからスクリーンショットを撮ることで回避できます。

これはほとんど見かけませんが、Editor画面がまるまる写っている例です。
f:id:PaperSloth:20190129201946p:plain

見たいのはあくまでもゲーム画面なので開発における技術ブログでもなければ
Editor画面は不要です。

UE4.20.3の場合はF9キー押下で普通に撮影すればこうはなりませんが
Windows標準のスクリーンショット機能で撮影をした場合などでしょうか。

Lightmap Rebuildの警告やPrintStringやAddOnScreen系のログが出ている。

Console CommandからDisableAllScreenMessagesを打ちましょう。
再度表示したい場合はEnableAllScreenMessagesです。

これが一番多くて一番ダサいですね。
f:id:PaperSloth:20190129202844p:plain

実は警告の下にうっすらと
「DisableAllScreenMessages'to suppress」と書かれています。


以上です。
せっかくのステキなアート、かっこいいゲームを作ってもこれが出てるだけでげんなりします。
ゲーム本来のキレイな絵が見れる頻度が増えることを祈ります。

 

SDL SDLの導入について

環境

Windows 10
Visual Studio 2017 Community
SDL 2.0.9

SDLとは

SDL (Simple DirectMedia Layer) は、Graphic, Sound系のAPIを提供するC言語マルチプラットフォーム対応のライブラリ。

公式サイトは下記
Simple DirectMedia Layer - Homepage

Forumを見れば分かるのだが現在も使用しているユーザーはいる。
https://discourse.libsdl.org/


SDLを使用している書籍は下記等がある。
で、意外と導入で躓いたという人もいるかもしれないと感じたのでこの記事を書くに至った。

オンラインゲームを支える技術ではSDL1.2系が使用されている。

オンラインゲームを支える技術  ??壮大なプレイ空間の舞台裏 (WEB+DB PRESS plus)

オンラインゲームを支える技術  ??壮大なプレイ空間の舞台裏 (WEB+DB PRESS plus)

ゲームプログラミングC++ではSDL2.0.3が使用されている。

ゲームプログラミングC++

ゲームプログラミングC++


SDLの導入

まずは下記よりDownloadを行う。
もしリンクが切れていたらブログトップの公式サイトのリンクからDownloadページにいってください。
Simple DirectMedia Layer - SDL version 2.0.9 (stable)

今回はDevelopment LibrariesのVC++を使用する。


1. コンソールアプリケーションを新規に作成
 適当に空のプロジェクトで良い

2. ダウンロードしたファイルを適当な場所(Solution\External\SDL とか)に解凍

3. SDLのインクルードディレクトリの指定。
 構成プロパティ/C/C++/全般/追加のインクルードディレクト
f:id:PaperSloth:20190122220010p:plain

4. 追加のライブラリディレクトリの指定。
 リンカー/全般/追加のライブラリディレクト
f:id:PaperSloth:20190122220328p:plain

5. 追加の依存ファイルの指定。
 リンカー/入力/追加の依存ファイル
  SDL.libとSDLmain.libを追加
f:id:PaperSloth:20190122220551p:plain

6. DLLをビルド時にコピー
 ビルドイベント/ビルド後のイベント/コマンドライン
  xcopy "(SDLへのパス)\*.dll" "$(OutDir)" /i /s /y を入力
f:id:PaperSloth:20190122220848p:plain

ひとまずここまでで動かすための前準備が完了。

SDLを使用する際の注意点

まず、SDLを使用する際に下記の記述をするとエラーになってしまいます。

#include "SDL.h"

int main()
{
    SDL_Init(SDL_INIT_EVERYTHING);
    // メイン処理
    SDL_Quit();
    return 0;
}

エラーメッセージを見てみると
LNK2019 未解決の外部シンボル _SDL_main が関数 _main_getcmdline で参照されました。
と出てきます。

これはSDL_main.h内でmainでdefineが定義されているからです。

#if defined(SDL_MAIN_NEEDED) || defined(SDL_MAIN_AVAILABLE)
#define main    SDL_main
#endif

また、SDL_main.h内にはメイン関数は次のように定義してくださいと書かれています。

int main(int argc, char *argv[])


そのため、SDLを使用する際には引数付きのメイン関数を定義する必要があります。

void main() // NG!
int main() // NG!
int main(int argc, char* argv[]) // OK
int main(int argc, char** argv) // OK
#include "SDL.h"

int main(int argc, char** argv)
{
    (void)argc;
    (void)argv;
    SDL_Init(SDL_INIT_EVERYTHING);
    // メイン処理
    SDL_Quit();
    return 0;
}

ひとまずWindow, Rendererを作成してみるとこんな感じになります。
f:id:PaperSloth:20190122225202p:plain

DirectXOpenGLに比べるとすごく簡単なので、C++でとりあえずゲームを作ってみようかなという人は挑戦してみてください。

UE4 C++でfpsを取得する方法について

この記事は「Unreal Engine 4 (UE4) Advent Calendar 2018」16日目の記事です。
Unreal Engine 4 (UE4) Advent Calendar 2018 - Qiita

環境

UE4.20.3

FPSの取得、あるいは計算方法

UE4fpsの計算でググると真っ先に出てくるのが
Tick内で1.0f / DeltaTimeで計算する方法。
Blueprintからも計算できてお手軽。
あとはこれをログに吐くなり、Widgetで表示するなりして使う。

void AExampleActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
    float fps = 1.0f / DeltaTime;
    // ログ表示とかとか
}

fpsを単純に表示したい場合はConsole Commandから"stat fps"で表示ができる。
基本的にデバッグで使いたいというだけの場合はこちらを使うだろう。

しかし、stat fpsではできないことが2つある。
①Shippingでビルドした場合に確認できない(Shippingではコンソールコマンドを受け付けないため)
②Game内のUIでfpsの表示機能が作成できない
 PCゲームではfpsを表示するオプションがあったりする
 
fpsを計算したり、stat fpsで表示してみて気になるのは
Tickでの計算値とstat fpsで表示されるfpsの値に違いがある点。
これは計算方法が違うため結果が異なる。
f:id:PaperSloth:20181210145352p:plain

そこで、今回はこのstat fpsで表示される値の取得方法について紹介する。

そもそもエンジン側でfpsの値を保持しているで自分でわざわざ計算する必要もない。

stat fpsで表示される値

UnrealEngine.cpp内にstat fpsで表示されるfpsの定義がある。

ENGINE_API float GAverageFPS = 0.0f;

名前からも分かるようにこの値はグルーバル変数として定義されている。
この値はUnrealEngine.hのCalculateFPSTimings()内で計算されている。

C++側でのfpsの取得について

実際に使用する際はBlueprintFunctionLibraryにでもまとめてしまうと良いだろう。

#include "Engine.h"

float UExampleFunctionLibrary::GetAverageFps()
{
    extern ENGINE_API float GAverageFPS;
    return GAverageFPS;
}


また、stat fpsでは処理に何ms時間がかかったかも表示している。
こちらもグローバル変数で公開されているので取得することができる。

float UExampleFunctionLibrary::GetAverageMs()
{
    extern ENGINE_API float GAverageMS;
    return GAverageMS;
}

30fpsのゲームなら33.33ms
60fpsのゲームなら16.66ms
120fpsのゲームなら8.33ms

この時間内に処理を終える必要がある。


Widgetを作成し、fps, msを表示してみた結果が下記になる。
stat fpsで表示されている値と一致していることが分かる。
f:id:PaperSloth:20181210150145p:plain

この方法で取得した値であればShippingビルドでも使用することができる。