PaperSloth’s diary

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

UE4 Moduleについて

環境

Unreal Engine 4.17.2
Visual Studio Community 2015


概要

今回はPluginではなく、Moduleの分割でビルドを早くしたりコードの結合度を下げたいとかそういう人向けです。
Plugin開発についての知見も得られると思います。


Moduleの追加

先ずは新規にModuleを追加する方法を説明します。
基本的にはTemplateからのPluginの作成とかで勝手に作られるのですが
BlankでC++プロジェクトを作成した状態から開始します。

今回はModuleSandboxというプロジェクトに
BattleSystemという新規Moduleを追加します。

手順①
追加したいModule名のフォルダをProject/Source以下に作成する。
f:id:PaperSloth:20180305213201p:plain

手順②
[ProjectName].Build.cs
[ProjectName].cpp
[ProjectName].h
を手順①で作成したModuleNameフォルダにコピー
f:id:PaperSloth:20180305213402p:plain

手順③
コピーした
[ProjectName].xxxを
[ModuleName].xxxに変更する
f:id:PaperSloth:20180305213657p:plain

手順④
[ModuleName].Build.csのProjectNameの箇所をModuleNameに変更する

using UnrealBuildTool;

// 変更前:public class ModuleSandbox : ModuleRules
public class BattleSystem : ModuleRules
{
	// 変更前:public ModuleSandbox(ReadOnlyTargetRules Target) : base(Target)
	public BattleSystem(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

		PrivateDependencyModuleNames.AddRange(new string[] {  });
	}
}

手順⑤
[ModuleName].cppのProjectNameの箇所をModuleNameに変更
Moduleのクラスを追加し、Moduleの種類を指定する。

// 変更前:#include "ModuleSandbox.h"
#include "BattleSystem.h"
#include "Modules/ModuleManager.h"

// 追加
class FBattleSystemModule : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
// 追加終わり

// 変更前:IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ModuleSandbox, "ModuleSandbox" );

// 今回はGameModuleとして追加する。
// GameModule, Moduleの違いは次の章で解説します。
IMPLEMENT_GAME_MODULE(FBattleSystemModule, "BattleSystem");
// IMPLEMENT_MODULE(FBattleSystemModule, "BattleSystem");

IMPLEMENT_PRIMARY_GAME_MODULEはプロジェクト中に1つしか存在しません。
追加するModuleは全てPRIMARYを外す必要があります。

手順⑥
ExtraModuleNamesに追加Moduleを加える。
[ProjectName].Target.cs
[ProjectName].Editor.Target.cs
上記2つに変更を加えます。変更方法は同じです。

using UnrealBuildTool;
using System.Collections.Generic;

public class ModuleSandboxTarget : TargetRules
{
	public ModuleSandboxTarget(TargetInfo Target) : base(Target)
	{
		Type = TargetType.Game;

		// 変更前:ExtraModuleNames.AddRange( new string[] { "ModuleSandbox" } );
		ExtraModuleNames.AddRange( new string[] { "ModuleSandbox", "BattleSystem" } );
	}
}


手順⑦
uprojectにModuleを登録する。
[ProjectName].uprojectをテキストエディタで開き、追加Moduleの設定を加える。

{
    "FileVersion": 3,
    "EngineAssociation": "4.17",
    "Category": "",
    "Description": "",
    "Modules": [
        {
            "Name": "ModuleSandbox",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        },
        // 追加分
        {
            "Name": "BattleSystem",
            "Type": "Runtime",
            "LoadingPhase": "Default"
        }
        // 追加終了
    ]
}

手順⑧
Solutionの更新
.uprojectを右クリックし、Generate Visual Studio project filesでslnの更新を行う。
f:id:PaperSloth:20180305215306p:plain


エラーが出なければこれでModuleの追加が出来ています。
ソリューションを開いてSource以下に追加されていれば成功です。
f:id:PaperSloth:20180305215549p:plain


GameModuleとModuleの違い

この辺りは少し面倒な話でUE4 C++本でも
プラグインに対してのホットリロードには難ありというのが現時点での正直な感想です"と書かれています。

私はあまり不便だと感じていませんが、それはさて置き
先に結論だけ述べます。
GameModuleの場合はHotReloadの際にビルドに含まれます。
Moduleの場合はCompileボタンを押してもHotReloadが行われません。

Moduleの場合はWindow -> Developer Tools -> Modulesウィンドウから行う必要があります。
f:id:PaperSloth:20180305221125p:plain
Recompileボタンを押下することでHotReloadが行われます。

他にも幾つか違いがあるとは思いますが、私が触ってみて分かった範囲について記述させていただきました。


PluginでもProjectの別Moduleの場合でも開発中にModuleとGameModuleを切り替えることが可能です。
ビルド時間をとりあえず短縮したい場合はModuleとして作成すれば良いと思われます。
Pluginの場合はModuleとして作成されます。

GameModuleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return true;
    }
};
IMPLEMENT_GAME_MODULE(F[ModuleName]Module, "[ModuleName]");


Moduleの場合

#include "[ModuleName].h"
#include "Modules/ModuleManager.h"

class F[ModuleName]Module : public IModuleInterface
{
public:
    virtual void StartupModule() override
    {
    }
    virtual void ShutdownModule() override
    {
    }
    virtual bool IsGameModule() const override
    {
        return false;
    }
};
IMPLEMENT_MODULE(F[ModuleName]Module, "[ModuleName]");

UE4 マウスカーソルを自作アイコンにする

環境

Unreal Engine 4.18.3


マウス座標の取得

Widgetでマウス座標を取得するには下記の2つのノードがあります。
・Get Mouse Position
・Get Mouse Position Scaled by DPI
f:id:PaperSloth:20180206195202p:plain

基本的にはGet Mouse Position Scaled by DPIで良いです。

画面解像度が固定でDPIスケールが1.0の場合や
DPIスケールの値が変わらないようにしている場合など
特殊な場合はGet Mouse Positionを使う場合もあると思います。

DPIスケールは画面解像度の変更に合わせてUIのサイズを変更してくれる機能です。
例えば、DPI Scaleが1.0の場合はGet Mouse Positionで問題ありませんが
初期設定では1280x720でWidgetを編集しており、DPI Scaleは約0.66です。
この値はProjectSettings -> Engine -> User Interfaceからカーブエディタで自由に編集できますが
基本的には初期設定のまま開発を行うでしょう。
初期値は1920x1080では1.0で3840x2160の場合は2.0が設定されています。
f:id:PaperSloth:20180206195021p:plain

この辺りの解像度の変化の違いをユーザー側が気にせず取得できるのが
Get Mouse Position Scaled by DPIとなっています。


マウスカーソルの変更

今回はマウスカーソルのみのWidget(WBP_MouseCursor)と
それを使用するWidget(WBP_MouseMove)の2つのWidgetを用意します。
f:id:PaperSloth:20180206195421p:plain

マウスカーソルのみのWidgetは下記のようにImageを1つ置いただけのWidget
Blueprint Graphには特に変更を加えません。
今回は特に素材を用意していないので、ポイントライトのテクスチャを使用しています。
f:id:PaperSloth:20180206195342p:plain

因みにマウスカーソルのみのWidget(WBP_MouseCursor)を作成してパーツとして分けていますが
今回の実装内容的にはWBP_MouseMoveにImageを配置する実装でも問題ありません。

もう一つのWidget(WBP_MouseMove)には先ほど作成したWidgetを配置します。
Palette内にUser Created -> WBP_MouseCursor(自作Widget名)があることを確認します。
f:id:PaperSloth:20180206195740p:plain

今回はマウスカーソルのアイコンがマウスの中心にきてほしいため
Allignmentには0.5. 0.5を代入してマウスカーソルの中心にアイコンがくるように補正しています。
f:id:PaperSloth:20180206195918p:plain
座標は画面外でもどこでもお好きなように。

続いてBlueprintの実装に移ります。
一旦は作成したWidgetを描画します。
ここでは確認のためにLevel Blueprintで行っています。
f:id:PaperSloth:20180206200024p:plain

続いて先ほど作成したWidgetのBlueprint Graphに移ります。
先ずは動作確認用に一旦マウスカーソルを表示することにします。
f:id:PaperSloth:20180206200141p:plain

ここまでで実行してアイコン画像とマウスカーソルが表示されていることを確認します。
f:id:PaperSloth:20180206200508p:plain


マウスカーソルの追従

マウスカーソルの座標を更新するには
アイコンの座標にMouse座標を代入するだけですね。

下記のようなノードになります。
f:id:PaperSloth:20180206200828p:plain

実行すると下記のようにアイコンがマウスの位置に合わせて動くことが確認できます。
※キャプチャーツールの都合上、マウスカーソルは表示されていません。


続いてマウスカーソルの位置に徐々に近づくような実装も紹介しておきます。
Interp Toノードで徐々に値を更新することで実現可能です。
f:id:PaperSloth:20180206201540p:plain

動画が分かりにくいのですが、実行してみるとこんな感じです。

今回は適当に5という値を入れましたが
変数に昇格させて場面によって速度を変えたりオプション画面から変更できると便利そうですね。

最後に動作確認ができたので、不要であればShow Mouse Cursorを削除しておきます。
以上で完成です。

参考資料

DPIスケールについて
https://docs.unrealengine.com/latest/JPN/Engine/UMG/UserGuide/DPIScaling/index.html

Mouse座標の取得について
[UE4] Input入門|株式会社ヒストリア


マウスカーソルの表示方法についてはPlayer Controllerを継承したBlueprintから変更する方法とBlueprintからアクセスする方法の2つがあります。

Blueprintから変更する方法について
http://kagring.blog.fc2.com/blog-entry-122.html

PlayerControllerを継承して変更する方法について
https://docs.unrealengine.com/ja/Resources/ContentExamples/MouseInterface/MouseControlSetup/index.html

おまけ

おかずさんがもっと便利な作成な方法について教えてくださいました。
https://twitter.com/pafuhana1213/status/960869841598013440?s=20

UE4 リフレクションに指定できる型について

環境

Unreal Engine 4.17.2

概要

Blueprintに公開可能な型と公開できない型を書いていきます。
他にも追記した方がいいことがあれば、コメントやTwitterで指摘いただけると助かります。
UPROPERTYで説明していますが、UFUNCTIONでも基本的に同様です。

enumUENUMを付ければ公開可能です。
structはUSTRUCTを付けてBlueprintTypeが指定されていれば公開可能です。

コードは下記クラスに追加していきます。
因みにUPROPERTYでCategoryを指定しない場合はクラス名がカテゴリ名になります。

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ReflectionActor.generated.h"
 
UCLASS()
class PROJECTNAME_API AReflectionActor : public AActor
{
    GENERATED_BODY()
public:
    AReflectionActor();
private:
    // ここに変数を追加
};



Blueprintに公開可能な型

公開可能なプリミティブ型

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
bool _bool;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint8 _uint;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int32 _int;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
float _float;

f:id:PaperSloth:20180131234057p:plain


公開可能なUE4の型

ここではよく使う型だけを紹介します。
他にも挙げきれないくらい種類があります。
一部の構造体とUObjectを継承しているクラスポインタが公開可能です。

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FName name;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FString string;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FText text;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FVector vector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FRotator rotator;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FTransform transform;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
UObject* _object;

f:id:PaperSloth:20180131234302p:plain

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FMatrix matrix;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FQuat quat;

Matrix, QuaternionはBlueprintへの公開は可能です。
f:id:PaperSloth:20180201005332p:plain
ですが、これらを操作するための関数はBlueprintにはほとんど公開されていません。

公開可能な構造体、列挙型

USTRUCT(BlueprintType)
struct FFoo
{
    GENERATED_USTRUCT_BODY()
 
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 _int;
};
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FFoo foo;


この場合はUPROPERTYを公開設定で付与した_intのみがBlueprint上に公開されます。
f:id:PaperSloth:20180201005640p:plain

UENUM()
enum class EBar : uint8
{
    State1,
    State2,
    State3,
    Num        UMETA(Hidden)
};
 
UENUM()
namespace EBaz
{
    enum Type
    {
        State1,
        State2,
        State3,
        Num        UMETA(Hidden)
    };
}
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
EBar bar;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TEnumAsByte<EBaz::Type> baz;


enumの書き方を2種類紹介しましたが、Blueprintからの見え方は同じです。
f:id:PaperSloth:20180201005828p:plain
EBazのような古いenumの書き方を示したのには理由があります。
UE4のエンジンコードにはまだ古い書き方が残っているため
それらを利用する際にTEnumAsByteを使用します。
自身で列挙型を定義する際は上のEBarのような書き方をオススメします。
詳しくはこちら
papersloth.hatenablog.com

公開可能なcontainer

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TArray<int32> _array;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TMap<int32, int32> map;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TSet<int32> set;

ここではintを指定していますが
公開可能なプリミティブ型、構造体、クラスポインタは基本的に使用可能です。
f:id:PaperSloth:20180201010039p:plain

また、Map, SetのBlueprintでの扱いに関してですが
4.13以下では使用不可
4.14では実験的な機能
4.15から正式なサポートとなります。


Blueprintに公開不可能な型

これらはC++側では当然使用可能ですが、Blueprintへの公開はできません。
UPROPERTY, UFUNCTIONを付与するとエラーとなります。
Blueprintで使用したい場合はcastするなどの対応が必要です。
数が多いため、一部のみを抜粋しています。

プリミティブ型

UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint16 _uint16;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint32 _uint32;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
uint64 _uint64;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int8 _int8;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int16 _int16;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
int64 _int64;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
double _double;


UE4で定義された型、container

#include "Containers/StaticArray.h"
 
// 省略
 
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
FMatrix2x2 mat22;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
TStaticArray<int32, 10> staticArray;



コード全容

// Copyright(c) 2018 PaperSloth

#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Containers/StaticArray.h"
#include "ReflectionActor.generated.h"
 
USTRUCT(BlueprintType)
struct FFoo
{
    GENERATED_USTRUCT_BODY()
 
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    int32 _int;
};
 
UENUM()
enum class EBar : uint8
{
    State1,
    State2,
    State3,
    Num        UMETA(Hidden)
};
 
UENUM()
namespace EBaz
{
    enum Type
    {
        State1,
        State2,
        State3,
        Num        UMETA(Hidden)
    };
}
 
UCLASS()
class PROJECTNAME_API AReflectionActor : public AActor
{
    GENERATED_BODY()
public:
    AReflectionActor();
private:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FFoo foo;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    EBar bar;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TEnumAsByte<EBaz::Type> baz;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    bool _bool;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint8 _uint;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int32 _int;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    float _float;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FName name;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FString string;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FText text;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FVector vector;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FRotator rotator;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FTransform transform;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    UObject* _object;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FMatrix matrix;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FQuat quat;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TArray<int32> _array;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TMap<int32, int32> map;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TSet<int32> set;
 
#if 0
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint16 _uint16;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint32 _uint32;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    uint64 _uint64;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int8 _int8;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int16 _int16;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    int64 _int64;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    double _double;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    FMatrix2x2 mat22;
    UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true"))
    TStaticArray<int32, 10> staticArray;
#endif
};

f:id:PaperSloth:20180201010405p:plain

UE4 ヒットストップの実装について

※1点修正があったため追記しました。(2018/01/31 1:22)
詳しくはページ下部をご覧ください。

環境

Unreal Engine 4.17.2

Time Dilationについて

ヒットストップの実装に関してはGlobal Time Dilationの値を変更するだけで簡単に実装ができます。
Time Dilationの説明について特に知る必要がないという人は読み飛ばしてください。

ここではTime Dilationの種類等について解説します。
Time Dilationは初期値が1.0です。
デフォルトの設定では
最小値は0.0001
最大値は20.0
となっています。
1を超える値を設定した場合、例えば2.0の場合は2倍速になることを表しています。
逆に1未満の場合、例えば0.5の場合は通常の1/2の速度になることを表しています。

基本的には下図のようなCustom Time DilationとGlobal Time Dilationを使用します。
f:id:PaperSloth:20180128231743p:plain

Custom Time Dilationとは

Custom Time DilationはActorに設定されたActorごとの速度を表します。
定義は下記のActor内に記述があります。
Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
Engine/Source/Runtime/Engine/Private/Actor.cpp

Actorの更新呼び出しの中で
下図のように使用されています。

Target->TickActor(DeltaTime * Target->CustomTimeDilation, TickType, *this);	

Global Time Dilationとは

これは名前にGlobalとあるようにゲーム中の更新に関わります。
定義箇所は下記です。
Engine/Source/Runtime/Engine/Classes/GameFramework/WorldSettings.h
Engine/Source/Runtime/Engine/Private/WorldSetting.cpp

因みにC++側で使用する際はほとんどの場合では
代わりにGetEffectiveTimeDilationを使用してください。という警告があります。

先程最小値は0.0001で最大値は20.0と書きましたが、この設定は変更することが出来ます。
f:id:PaperSloth:20180128232843p:plain

World Setteingsタブの最下部に隠れたパラメーターとして
Min Global Time Dilation, Max Global Time Dilationがあります。
BlueprintからSet Global Time Dilationを呼び出した際にはこの値を参照してClampされています。
そのため、厳密には0を指定しても0.0001が設定されるため、完全に更新が停止するというわけではありません。

ヒットストップの実装

前置きが長くなりましたが、ヒットストップの実装は至って簡単です。
今回はMacroLibraryとして作成したものを例に紹介します。
親クラスにはActorを指定しています。

実装にはたった5つのノードを呼び出すだけの非常にシンプルな実装となっています。
f:id:PaperSloth:20180128233845p:plain

図に説明があるように
Global Time Dilationにどれくらいゆっくりにしたいかの値を設定します。
0以上1未満の値でゲーム全体をゆっくりにすることが出来ます。

次にDelayで元の早さに戻るまでの時間を設定します。

呼び出し例としては下記のような感じです。
ここではわかりやすくするためにDelayに渡す値をStop Timeという名前にしています。
通常の1/10の速度に変更し、0.15秒後に元の早さに戻るように設定しています。
※0.15秒ではなく、1.5秒後になります。
詳しくは追記したページ下部の説明をご参照ください。
f:id:PaperSloth:20180128234145p:plain

実際にヒットストップを使用した感じは下記のような感じになります。
ここでは敵の撃破時に使用しています。
容量の都合上、10FPSのGifで分かりにくいですが、こんな動きになります。
f:id:PaperSloth:20180130223753g:plain

※1点誤りがあったため、追記です。
ブログ公開後に指摘をいただきましたように
Delayノードも時間の進みが遅くなります。

これは最初、気付かずに実装すると少しハマってしまうので注意が必要です。
Global Time Dilationの影響を受けるノード
・Delay
Global Time Dilationの影響を受けないノード
・Set Timer by Event


以上のように5つのノードだけで簡単にヒットストップが実装できます。
先程の例のようにMacroLibraryにしておくと便利です。
その時はActor以上を継承したものを選択しましょう。
また、FunctionLibraryでは実装できません。
理由としてはDelayなどのLatentノードの呼び出しができないからです。
ゲームのコンボや撃破時の演出など活用できる場面は幾つかあると思います。
是非試してみてください。

UE4 文字列変換について

目次

環境

Unreal Engine 4.17.2
Visual Studio Community 2015

FStringとstd::stringの変換について

プラグイン開発を行っているとネイティブのC/C++で書かれたプラグインとのやり取りが発生することがあると思います。
その時にUE4側のstring(FString)とC/C++のstring(std::string)の変換が必要になることがあります。

よくド忘れするので、簡単に個人用メモとしてまとめます。

FString -> std::string

この変換にはTCHAR_TO_UTF8マクロを使います。

FString ustr = "UnrealString";
std::string cstr = TCHAR_TO_UTF8(*ustr);

std::string -> FString

この変換にはc_strを呼び出します。

std::string cstr = "CppString";
FString ustr = cstr.c_str();

TCHAR_TO_UTF8の他にも幾つかマクロが存在します。
それらの定義は下記に記述があります。
Engine/Source/Runtime/Core/Public/Containers/StringConv.h

今回紹介したものがUTF8_TO_TCHARでしたが、他にも

TCHAR_TO_ANSI(str);
ANSI_TO_TCHAR(str);
TCHAR_TO_UTF8(str);

などがあります。


Blueprintに値を公開したい場合はFStringでなければなりません。
そのため、下記のようなものはエラーとなります。

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "String")
std::string FooName;

UFUNCTION(BlueprintCallable, Category = "String")
void ConvertString(const std::string str);

UFUNCTION(BlueprintCallable, Category = "String")
std::string ConvertString();

UPROPERTYもUFUNCTIONもFStringに変換しなければ使用することはできません。
ですが、std::stringが引数、戻り値として使用できないというわけではないため
Unreal C++側での呼び出しに限定される場合は問題ありません。

UE4 Editorのショートカットキーのカスタマイズについて

目次

環境

Unreal Engine 4.18.3

エディタのショートカットキー一覧

Editor Preferences - General - Keyboard Shortcuts以下に設定項目があります。
f:id:PaperSloth:20180128161608p:plain

PauseとFrameSkipにショートカットキーを割り当てておくと
デバッグ作業がしやすくなります。
f:id:PaperSloth:20180128162146p:plain

確認のたびにEjectしてからPauseだと止めたいタイミングで止めるのが難しいですからね。

他にもショートカットキーを設定可能な項目がたくさんあります。
お好みで設定してみてください。

また、既存のショートカットキーがどんなものがあるのかを確認するためにも一通り確認しておくと作業が楽になります。

UE4 ローカルマルチプレイについて

目次

環境

Unreal Engine 4.18.3

概要

ローカルマルチプレイの基本についてはalweiさんのブログが参考になります。
UE4 簡単なローカルマルチプレイヤーゲームを作ってみる - Let's Enjoy Unreal Engine
今回はこれに少し追記したような内容になります。


画面の分割設定について

画面を分割しない設定や縦に分割、横に分割の設定が可能です。
Project Settings - Maps & Modes - Local Multiplayer以下に設定項目があります。
f:id:PaperSloth:20180125213049p:plain


・Use SplitScreen
画面分割を行うか否かです。
デフォルトではオンになっているため、通常はこのままで良いでしょう。

・Two Player Splitscreen Layout
2人プレイ時の画面分割設定です。
Horizontal - 上画面、下画面に分割されます。デフォルトの設定です。
f:id:PaperSloth:20180125213434p:plain
Vertical - 右画面、左画面に分割されます。
f:id:PaperSloth:20180125213351p:plain

ゲーム内容に合わせてお好みでお選びください。

・Three Player Splitscreen Layout
3人プレイ時の画面分割設定です。
Favor Top - 上画面が大きく表示されます。デフォルトの設定です。
f:id:PaperSloth:20180125213537p:plain
Favor Bottom - 下画面が大きく表示されます。
f:id:PaperSloth:20180125213605p:plain

プレイヤーを指定の位置に配置したい場合について

PlayerStartを削除し、Level上に直接Pawn(Character)を配置する方法が楽です。

しかし、配置しただけではどのキャラクターも操作できないため操作設定を行います。
配置したキャラクターのPawn - Auto Possess Playerに指定したいPlayer番号を設定します。
0からカウントされるため、Player 0が1P、Player 1が2Pとなります。
f:id:PaperSloth:20180125213708p:plain

4人配置したイメージ
f:id:PaperSloth:20180125213838p:plain


2人、4人の対戦や協力ゲームだと同じキャラクターの色違いを用意したりするため簡単ですね。
Level上に直接Pawn(Character)を置くため、Pawn(Character)の種類はバラバラでも構いません。
例えば3人プレイのゲームで1対2で1人が巨大なモンスターというのも面白そうですね。
f:id:PaperSloth:20180125214328p:plain

まとめ

さて、比較的簡単にローカルマルチプレイゲームが作れますね。
私自身もUE4ぷちコンゲームジャムで3人構成のチームでこんなゲームを作りました。
www.nicovideo.jp


ですが、幾つか課題もあるためそれらが解決できそうかどうかも考えつつ挑んでみてください。

・4人プレイの場合そもそもコントローラーが用意できるか、繋げるか
 どちらも物理的な問題ですが、繋げるかは結構重要ですね。
 ノートPCのUSBポートの空き数と相談してハブを用意するなどしましょう。

・個々のプレイヤーの入力を上手くさばけそうか
 そこそこ簡単な問題ですが、肥大化したプロジェクトを
 いきなりマルチ対応したりするとぐちゃぐちゃになります。
 どうやったら綺麗に入力を処理できるかのイメージを考えましょう。

・個々のプレイヤー情報をどうやって取得するか
 TickでGet All Actors Of Classで取得するみたいな方法は極力避けたいですね。
 個々のプレイヤーの情報にアクセスしたりチェックしたりは必ず必要になるので、
 最初の段階で考えておくと良さそうです。

・リスポーン処理をどうするか
 例えば対戦ゲーム等でリスポーンがある場合に
 他のプレイヤーと同じ地点にリスポーンしてしまうのは避けたいですね。

以上のようにいざ作ってみると詰まってしまうことがあると思いますが1人プレイより盛り上がります!
Blueprintにちょっと慣れてきた方は作ってみてください。