概要
ぷちコン参加は今回で5回目。これまで最優秀1回、受賞2回。
BPメイン(C++ 1~3割)で作ってました。
今回はほぼC++で作ってます。
ぷちコン応募しました。
— altalt (@altalt_jp) 2019年3月31日
円を渡って赤から逃げる1キーゲームです。https://t.co/6yBEfDGrWM
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