C++メインでUE4ぷちコンに参加してみた

# 概要
ぷちコン参加は今回で5回目。これまで最優秀1回、受賞2回。
BPメイン(C++ 1～3割)で作ってました。

今回はほぼC++で作ってます。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ぷちコン応募しました。<br>円を渡って赤から逃げる1キーゲームです。<a href="https://t.co/6yBEfDGrWM">https://t.co/6yBEfDGrWM</a></p>&mdash; altalt (@altalt_jp) <a href="https://twitter.com/altalt_jp/status/1112405199165874176?ref_src=twsrc%5Etfw">2019年3月31日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# C++で開発して良かったこと
- BPスパゲッティをみなくてすむ
- 開発後半は、デバッグしやすくコードの差分が見やすいので助かった
- AnswerHubなどで見つけた解決策がC++でも大丈夫
- ソースを読めば解決できることも多い

# 今回やったこと
## Visual Studioのプラグインを使ってみた
特にVisual Assist X はオススメです。
ファイル検索だけでよければ、無料のVSFileNavもけっこう使いやすかったです。

- Visual Assist X
- Entrian Source Search
- VsVim

## モジュールを分けてみた
ヒストリアさんの記事などを参考にモジュール分割をしてみました。
http://historia.co.jp/archives/3097/

少し手こずりましたがモジュールの仕組みを知ることは、UE4 C++を触るうえで必須だと感じました。
リンクエラーの原因とかもわかるようになります。

ただ、整理のためにモジュールの移動とかしてるとトラブルも起きやすいので、ぷちコンの期間外で試してみたほうがいいです。
そもそもぷちコンの規模の作品では、モジュール分割してもデメリットが多く、あまり必要ないと思います。

## Luaを使ってみた
Luaはスクリプト言語なのでC++のビルドを待たずにすぐ結果が反映されます。
ジャンプ力や回転速度、チェインの挙動など細かく調整したいパラメータをLuaで設定できるようにしました。
最初の設定が手間ですが一度仕組みを作ってしまえば、UPROPERTYを追加する+アルファくらいの感覚でパラメータを追加できます。
BPと比べて、

- テキストなので一覧性が高い
- 変更履歴が管理できる
- 計算させた値を返したりできる

など導入するメリットは大きいです。

下のサイトを見て、実装しました。
モジュール分けとか試したあとだと理解しやすいような気がします。
https://wiki.unrealengine.com/Integrating_Lua

後は、下のような関数を用意して呼び出しました。

```cpp
/** Luaの関数名を指定してfloatを受け取る */
float ULuaImporter::GetFloat(FString Path, FString FuncName)
{
    lua_State* lua = luaL_newstate();
    luaL_openlibs(lua);
    FString root = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
    root += FString::Printf(TEXT("Script/%s.lua"), *Path);
    luaL_loadfile(lua, TCHAR_TO_UTF8(*root));
    lua_pcall(lua, 0, 0, 0);
    lua_getglobal(lua, TCHAR_TO_UTF8(*FuncName)); // 呼び出す関数
    lua_pcall(lua, 0, 1, 0);
    float ret=0;
    if (lua_isnumber(lua, -1))
    {
        ret = lua_tonumber(lua, -1);
    }
    lua_pop(lua, 1);
    return ret;
}
```

luaで簡単な値を返す例

```lua
function spin_time() return 1.0 end
function spin_time_in_air() return 5.0 end
function jump_force() return 1800 end

function combo_factors(arr)
   for i = 1, #arr do
       arr[i] = (i - 1) * 500 > 2000 and 2000 or (i - 1) * 500
   end
   return arr
end
```

## サブレベルのストリーミング
ひとつひとつのステージはサブレベルで配置しています。
ステージ呼び出し後に読み込んだアクターに対して処理をしたかったので、下のようにして通知を受け取るようにしました。
UUIDを変えないと、複数のレベルを同時に読み込むことができませんでした。

```cpp
 FName LevelToLoad = FName(*name);
 FLatentActionInfo LatentInfo;
 LatentInfo.CallbackTarget = this;
 LatentInfo.ExecutionFunction = FName("NotifyLevelLoaded");
 LatentInfo.Linkage = 1;
 LatentInfo.UUID = ++UUID;
 UGameplayStatics::LoadStreamLevel(this, LevelToLoad, true, true, LatentInfo);
```

LoadLevelInstanceで呼んだほうが、読み込んだレベルのアクターを直接取得できて便利だったのですが、読み込みが重かったのでやめました。

```cpp
 bool b;
 auto streaming = ULevelStreamingDynamic::LoadLevelInstance(GetWorld(), LevelToLoad.ToString(), FVector(0, 0, 0), FRotator(0, 0, 0), b);
 CurrentStreaming = streaming;
 GetWorld()->AddStreamingLevel(CurrentStreaming);
 CurrentStreaming->OnLevelLoaded.AddDynamic(this, &AStageLoader::NotifyLevelLoaded);
```

## デバッグラインのカスタム
円弧のデバッグラインが欲しかったので、ULineBatchComponentを真似して円弧のラインを描けるようにしました。
こういうことが簡単にできるのはC++の利点だと思います。

# ぷちコンでC++を使うべきか
C++を使うと開発スピードが落ちるので、短期間の開発でC++メインにするのはリスクが高いです。
簡単なゲームでも完成させたことがない人や、デザイナー・プランナー志望ならBPメインで作ったほうがいいと思います。

C++を使う場合でも、使ったことのないライブラリの検証とかは、ぷちコンの期間内ではやらないほうがいいです。
肝心のゲーム開発が進まなくなります。

BPとC++を使う上で、参考になる資料も上がってますので、最後に紹介しておきます。

BPからC++に移行した話
https://www.slideshare.net/EpicGamesJapan/ue4-bp18

BPメインの事例
https://www.slideshare.net/EpicGamesJapan/ue4-137253034