Edited at

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


概要

ぷちコン参加は今回で5回目。これまで最優秀1回、受賞2回。

BPメイン(C++ 1~3割)で作ってました。

今回はほぼC++で作ってます。



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

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

/** 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で簡単な値を返す例

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を変えないと、複数のレベルを同時に読み込むことができませんでした。

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

 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