UnrealC++のTips

この記事は、[Unreal Engine 4 \(UE4\) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/ue4) 23日目の記事です。

UnrealC++のTipsということで、情報メモをお届けします。
お役に立てれば嬉しいです。
確信のないものは疑問形になってます。


# 基本

使うことが多いUE4内のクラス

## 配列

大体以下の2つを知っていればOKです。

- TArray
- TMap


## 基本型
- int32
- float

後ろの数値がバイト数になる。


## 文字列
- FString
- FName
- FText


## クラス
- UObject
- AActor
- APawn
- UActorComponent
- UWorld
- UGameInstance
- UGameState
- UGameMode
- APlayerController

## 公式記事
公式にも記事がありますので読みましょう。
https://www.unrealengine.com/ja/blog/ue4-libraries-you-should-know-about


# UCLASS Tips

## UObject継承したとき
- クラス宣言前に`UCLASS`必須。
- クラス名先頭に`U`必須。
さらにクラス内宣言で`GENERATED_BODY()`が必須。（`GENERATED_UCLASS_BODY()`は古い宣言でレガシーらしい）
- `const FObjectInitializer& ObjectInitializer` を引数としたコンストラクタは自分で書ける。
引数を増やすことは無理っぽい？カスタムコンストラクタという機能があってそれで出来る？

例：
MyGameModule内にクラスを作るとする。

```cpp

UCLASS()
class MYGAMEMODULE_API UMyClass : public UObject
{
    GENERATED_BODY()

public:
    UMyClass(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) {}
};
```


## ヘッダーに～.generated.hを書くのが必要なとき
UCLASSやUSTRUCTが存在するヘッダーに追加する必要があり。
includeは最初ではなく、他のincludeするファイルがあったらの最後にしないといけない。
（多分 `GENERATED_BODY()` が必要なタイプがあるときに必要。 `UENUM` のときは必要なかった。Blueprintに露出する場合は必要かも？）

存在しないヘッダーに追加した場合は逆にエラーになる。

## CPPファイルに必ずIncludeしなくてはいけないファイルが存在する
Moduleの名前になっているヘッダファイル。
`HogeHoge.Build.cs` だったら、 `#include "HogeHoge.h"` を一番最初のインクルードすることが必須。

## PrefixHeaderはどれ？
　もしModule名がHogeHogeだったら（ビルドソースコードが `HogeHoge.Build.cs` だったら）、 `HogeHoge.h` にIncludeしておくとそのHogeHogeModule内でPrefixHeader代わりになる？


## Delegate
ExecuteIfBound関数が使えるのは返り値がvoidのDelegateのみ。

以下はDelegateの書き方の例なので参考にしてください。（よく忘れる）

```hpp
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FFoobarDelegate, int32, MyId, const FString&, message);
DECLARE_MULTICAST_DELEGATE_OneParam(FHogeHogeResultDelegate, const FMyResult&);
```

```cpp
FFoobarDelegate FoobarDelegate;
FoobarDelegate->AddDynamic(this, &HogeHogeClass::OnFoobar);
FoobarDelegate->Broadcast(id, msg);

FHogeHogeResultDelegate HogeHogeResultDelegate;
HogeHogeResultDelegate->AddUObject(this, &HogeHogeClass::OnHogeHogeResult);
HogeHogeResultDelegate->Broadcast(result);
```

```hpp
DECLARE_DELEGATE_OneParam(FMyToolSpinBoxClicked, float)
```

```cpp
FMyToolSpinBoxClicked OnClicked;
FReply SMyToolMainMenu::OnResetClicked()
{
    return FReply::Handled();
}
```

## グローバル変数として持たせられるクラス
ゲームを作っているときに、レベルを跨ぐような変数が欲しい時がある。
その時には `UGameInstance` のメンバ変数として持たせておくことで、グローバル変数のように使うことが出来る。
（これ以外レベルを跨ぐ変数を保持する方法は、多分ファイルに書き出すとかDBに持たせるとかしないと無理っぽい）

## C++に定義されている関数をBlueprintでオーバーライドして実装する方法
細かい方法は、 `3. Extend and Override C++ with Blueprints` （https://docs.unrealengine.com/latest/INT/Programming/Tutorials/VariablesTimersEvents/3/index.html
）の項目参照。

以下ざっくり説明。

C++側では、

```cpp
UFUNCTION(BlueprintNativeEvent)
void CountdownHasFinished();
virtual void CountdownHasFinished_Implementation();
```

を宣言して、C++側は `void CountdownHasFinished()` を実装せずに、 `virtual void CountdownHasFinished_Implementation()` の `_Implementation` が付いている方だけ実装。

```cpp
void ACountdown::CountdownHasFinished_Implementation()
{
    //Change to a special readout
    CountdownText->SetText(TEXT("GO!"));
}
```

Blueprint側は `CountdownHasFinished` イベントを作成する。
Blueprint側からC++の関数を呼び出したい場合は、 `right-clicking the Countdown Has Finished node and selecting Add call to parent function from the context menu.` で実行線をつなげる。

また、C++側でオーバーライドしたBlueprintイベントを呼び出したい場合は、 `CountdownHasFinished_Implementation` ではなく、
`CountdownHasFinished` を呼ぶ必要があることに注意。

```cpp
void ACountdown::Test()
{
//    CountdownHasFinished_Implementation(); // こっちだと基底のC++の方しか呼び出されない
    CountdownHasFinished(); // こっちだとオーバーライドされたBlueprintイベントが呼び出される
}
```

## C++に定義されている関数をBlueprintでオーバーライドし、その関数をC++から呼び出す場合
`UFUNCTION` 宣言時に `BlueprintImplementableEvent` を使う。

```cpp
UFUNCTION(Category = "Player Health", BlueprintImplementableEvent, BlueprintCallable)
void PlayerIsHealthyTick(float CurrentHealth);
```

したら、UE4Editorで `function override` から `PlayerIsHealthyTick` を選択してイベントを作成する。
これで `PlayerIsHealthyTick` 関数が呼ばれると、オーバーライドしたBlueprintの `PlayerIsHealthyTick` イベントが呼ばれるようになる。

あとC++から呼ぶときには `this->` 必須らしい？
`this->PlayerIsHealthyTick(1.0f);`

参考：https://wiki.unrealengine.com/Blueprints,_Empower_Your_Entire_Team_With_BlueprintImplementableEvent


# オブジェクトの作成

## NewObject\<T\>
通常のnewと同じ感じ。
クラス名からしか指定できないので、Blueprintクラスは生成不可？
Actor生成で使ってはいけない。（はず）

```cpp
UBillboardComponent* BillboardComponent = NewObject<UBillboardComponent>(this);
```

##  CreateDefaultSubobject\<T\>
コンストラクタでしか使えない。
Defaultのコンストラクタが呼ばれる。
クラス名からしか指定できないので、Blueprintクラスは生成不可？

BlueprintでComponentを追加するときの処理とかはコンストラクタでこれを呼べば可能。

```cpp
SpriteComponent = CreateDefaultSubobject<UBillboardComponent>(TEXT("Sprite"));
```

## ConstructorHelpers::FObjectFinder\<T\>
assetファイルから取得する。
.Objectでそのオブジェクトのポインタを取得可能。

```cpp
static ConstructorHelpers::FObjectFinder<UStaticMesh> CraneArmMesh(TEXT("/Engine/EditorMeshes/Camera/SM_CraneRig_Arm.SM_CraneRig_Arm"));
PreviewMesh_CraneArm = CreateOptionalDefaultSubobject<UStaticMeshComponent>(TEXT("PreviewMesh_CraneArm"));
if (PreviewMesh_CraneArm)
{
	PreviewMesh_CraneArm->SetStaticMesh(CraneArmMesh.Object);
}
```

## StaticLoadObject
assetファイルから読み込んで取得する。


##  UWorld::SpawnActor
UActorを継承したクラス専用。
Worldに生成する。（レベルに置くと同義？）
Actorを新しく作るときには `NewObject` ではなくこっち。
通常のComponentとかに使ってはいけない。（というかこっちはコンパイルエラー出るはず）

Blueprintクラスを生成するには、 `SpawnActor` の引数に `.ClassObject` を渡す。

# FString Tips
## 文字列変換
### convert Fstring to normal c++ char[] 

```cpp
// FString -> TCHAR
TCHAR* chars = *MyString;
```
```cpp
// FString -> TCHAR -> ANSI
ANSICHAR* ansiChars = TCHAR_TO_ANSI(*MyString);
```
参考：https://forums.unrealengine.com/showthread.php?3207-convert-Fstring-to-normal-c-char


### char array to an FString
```cpp
FString Fs = FString(ANSI_TO_TCHAR(arr));    // ANSI -> TCHAR -> FString
FString Fs = FString(UTF8_TO_TCHAR(arr));    // UTF-8 -> TCHAR -> FString
```
参考：https://answers.unrealengine.com/questions/115456/best-way-to-convert-from-a-char-array-to-an-fstrin.html


### FName -> FString
`FNameValue.ToString()`
https://answers.unrealengine.com/questions/2616/manipulating-fname-and-fstring.html


### FName、FText、FStringの相互変換について
危険な変換があるので、ドキュメントの【文字列の取り扱い】参照。
https://docs.unrealengine.com/latest/JPN/Programming/UnrealArchitecture/StringHandling/index.html

### C言語でよくある通常の文字列関数使いたい
`\Engine\Source\Runtime\Core\Public\Misc\CString.h`
に定義されている、 `FCString` 、 `FCStringAnsi` 、 `FCStringWide` の関数でいろいろできるようになっている。

例：
`auto length = FCStringAnsi::Strlen(charPtrArray);`

## FTextでFormat

```cpp
FString FileName;
(FText::Format(LOCTEXT("Test", "Save {FileName}.data."), FText::FromString(FileName)));
```

## FStringの文字列操作

### FString str = FString(TEXT("abcdefg"))のとき。

```cpp
str.Left(5); // "abcde"
str.Right(5); // "cdefg"


str.LeftChop(5); // "ab"
str.RightChop(5); // "fg"


str.LeftPad(10); // "   abcdefg"
str.RightPad(10); // "abcdefg   "
```

拡張子を外したファイル名が欲しいなら、以下の関数で良い。
` FPaths::GetBaseFilename(TEXT("hogehoge.txt"));`

拡張子だけ欲しければ、
`FPaths::GetExtension(TEXT("hogehoge.txt"));`

StringからIntegerへの変換

```cpp
FString str(TEXT("123"));
FCString::Atoi(*str);
```

IntegerからStringへの変換
`FString::FromInt(123);`

特定の文字で分割したい

```cpp
FString str(TEXT("hogehoge_txt_temp"));
TArray<FString> Values;
str.ParseIntoArray(Values, TEXT("_"));    // hogehoge, txt, tempに分割される
```

# etc Tips
## GCを強制的に呼ぶ
`GetWorld()->ForceGarbageCollection();` 

## Logカテゴリーの追加方法
ヘッダーで`DECLARE_LOG_CATEGORY_EXTERN`して、
CPPに`DEFINE_LOG_CATEGORY`する。
外部モジュールでも使いたい場合は、ヘッダー側の宣言にAPI宣言を前方に置いておく。

### 例：HogeHogeモジュール専用のログを追加したい場合

```h
HOGEHOGE_API DECLARE_LOG_CATEGORY_EXTERN(LogHogeHoge, Display, All);
```

```cpp
DEFINE_LOG_CATEGORY(LogHogeHoge);
```

そのCPPファイルだけで使いたい場合は`DEFINE_LOG_CATEGORY_STATIC`？

## WindowsのDWORDとか使いたい場合
`#include "AllowWindowsPlatformTypes.h"`
と
`#include "HideWindowsPlatformTypes.h"`
で囲んで、その囲んでいる中にDWORDなどの処理を書く。

## ActorじゃないけどTickで動かしたい
ヒストリアさんのブログ参照。
[[UE4] C++コードにゲームの初期化，終了，更新のタイミングで処理を追加する方法 | historia Inc - 株式会社ヒストリア](http://historia.co.jp/archives/775 "[UE4] C++コードにゲームの初期化，終了，更新のタイミングで処理を追加する方法 | historia Inc - 株式会社ヒストリア")

`\Engine\Source\Runtime\Engine\Public\Tickable.h`の`FTickableGameObject`を継承して使う。
レベルを跨いで実行したいTickにも効果あり。


## CriticalSection
`FCriticalSection`をメンバ変数などで変数を作る。

スコープ内だけ有効にしたい場合には、下記のような感じで使う。
`FScopeLock Lock(&Critical);`

### スレッドセーフなインクリメントをしたい
以下の関数を使用すると安全。
`int32 CurrentID; // インクリメントしたい整数（メンバ変数として持っていると仮定する）`
`const int32 NextId = FPlatformAtomics::InterlockedIncrement(&CurrentID);`

もしくは素直に`FThreadSafeCounter`型を使う。

```cpp
FThreadSafeCounter Counter;
const int32 nextId = Counter.Increment();
```


## UnrealC++でComponent登録

### コンストラクタ
`CreateDefaultSubobject` を使う。

### コンストラクタ以外
`NewObject` した後、`AttachToComponent` 関数を呼ぶだけではダメで、
 `RegisterComponent` 関数を呼ぶ必要がある。


## Level内にいるActorの検索
### ActorInLevel

```cpp
TArray<AActor*> Characters;
UGameplayStatics::GetAllActorsOfClass(this, AMyCharacter::StaticClass(), Characters);
```

## FilePathの検索
### LoadFilePath

```cpp
const FName AnimMontageAssetPath(TEXT("/Game/Animation/AnimMontageSample"));
auto AnimMontage = Cast<UAnimMontage>(StaticLoadObject(UObject::StaticClass(), nullptr, *AnimMontageAssetPath.ToString()));
```

### glob

```cpp
TArray<FString> FileNames;
IFileManager::Get().FindFiles(FileNames, *FString::Printf(TEXT("%s/Datas/*.*"), *FPaths::GameContentDir()), true, false);
for (auto FileName : FileNames)
{
	auto BaseFileName = FPaths::GetBaseFilename(FileName);

	// 読み込み
	...
}
```

## 外部ライブラリ追加方法

VisualStudioの設定ではなく、ライブラリを使うモジュールの～.Build.cs内で追加する。

**lib追加方法**
`PublicAdditionalLibraries`
のメンバ関数Addで.libを追加する。

**IncludePath追加方法**
`PublicIncludePaths` 、もしくは `PrivateIncludePaths` のメンバ関数 `Add` でIncludePathを追加する。
ビルドの構成は `TargetInfo` の変数から取得できる。

例：

```csharp
    private string ModulePath
    {
       get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }
    }
    
    // ライブラリを置いた基底パス取得
    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "../../../ThirdParty/")); }
    }


    if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
    {
      string LibrariesPath = Path.Combine(ThirdPartyPath, "hogehogelib", "Windows"); // libファイルが置いてあるパスを設定
      // ビルドターゲットによってリンクするライブラリを変更する
      switch (Target.Configuration)
      {
      case UnrealTargetConfiguration.Shipping:
      case UnrealTargetConfiguration.Test:
        // Release版
        PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "HogeHoge_x64.lib"));
      break;
      case UnrealTargetConfiguration.Debug:
      default:
        // Debug版
        PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "HogeHoge_x64.lib"));
      break;
      }
    }
```

## レベルを開く

`UGameplayStatics::OpenLevel(this, TEXT("/Game/Stage/Level001"));`


## URLを開く

`FPlatformProcess::LaunchURL(*FString::Printf(TEXT("http://www.google.com")), NULL, NULL);`


## 各ディレクトリ取得

`FPaths::GameDir()         // Gameフォルダのパス（Contentフォルダ？）`

あとは大体名前の通りのフォルダのパスを取得できる。
`FPaths::GetConfigDir()`
`FPath::GetSavedDir()`
など。


## C++側だけconstにしてBlueprintに出す
```cpp
    UFUNCTION(BlueprintCallable, Category = "Exprement")
    int32 Exprement()
    #if  CPP
    const
    #endif
    {
        //test = 111;　　// コメント外すとエラー
        return 201;
    }
    
    // このとき
    UCLASS()
    class UTest : public UClass
    {
        int32 test;
    };
```

# 今日はUnrealC++の日
[裏 Unreal Engine 4 \(UE4\) Advent Calendar 2016 \- Qiita](http://qiita.com/advent-calendar/2016/ue4_inside) でも @Kaaaai さんが[よく使われる処理をUnrealC\+\+で書いてみようの巻。 \- Kai's Tech](http://ikagamedev.hatenablog.com/entry/2016/12/22/235346) というのが公開されています！是非読みましょう！

# UnrealEngine4の技術書がセール中

現在、Packtのセールで電子書籍・ビデオが全部$5なので買いましょう。

Bundleとしてもまとまってます。
[Unreal Engine 5 for $25 Bundles \| PACKT Books](https://www.packtpub.com/5-dollar-2016/unreal-engine-bundles/?utm_source=twitter&utm_medium=social&utm_campaign=fivedollar2016)

自分はMastering Unreal Engine Bundleをポチりました。

日本でもUnrealC++の技術書が出ることを願っています。（誰か書いてください）

# 実装が合っているはずなのに思った動作にならない時のチェックリスト

- 本当に変数に値が入っているか
- デフォルト動作になっていないか
- 条件分岐、計算に使っている数値の単位が正しいか
- 初期化・終了処理し忘れていないか


- デバッガで動作を追ってみる
- 使用している変数をPrintデバッグして確認
- コードを読む、考えこむだけではなく、編集してみる
- API仕様が間違っている可能性（辛いやつ）


- リビルド
 - リビルドでも駄目な場合は、キャッシュを削除する。
  - ビルドキャッシュが入っているIntermediate のディレクトリは空にする
  - 各ゲーム固有処理のキャッシュは Engineじゃない方のSaved フォルダ内のデータを消しましょう
- 気分転換する
- 散歩
- 風呂に入る
- 仮眠する
- 一旦寝て明日やる

# 明日は
明日はtarotarokunさんの、某MashupAwardに出す作品で得た、UE4で作ったVRゲームの動画配信ノウハウを大公開です。