UE4のVisualStudioソリューションファイルの作り方について

UE4プロジェクトのVisualStudioソリューションファイルの作り方について

どこがどう繋がって、どのエンジンバージョンのソリューションが作成されるのか理解していなかったので、まとめました。 あくまでUE4でのVSソリューションファイルの作り方についてなので、UE4プロジェクトのバージョンコントロール方法ではありません。そのような情報がほしい場合には、ソース コントロールとして SVN を使用する | Unreal Engineなどを参考にしましょう。

ちなみにVer4.19ぐらいのときにメモしたやつなので、最新だと変わっていたりするかもしれません。

YOUR_PROJECT という部分は、自身の作成したプロジェクト名に置き換えてください。 方法はパターンA、パターンBみたいに列挙していきますので、お好きな手段を選ぶとよいです。

【エンジンソースの取得方法】

  • A. GithubからCloneして取得(お好きな作業フォルダへどうぞ)
  • B. EpicGamesLauncherからInstallOption->エンジンソースにチェック (デフォルトパス設定は、 [C:\Program Files\Epic Games\UE_4.xx\Engine])

【ソリューションファイルの作成方法】

  • A. Editor起動して、メニューの[File]->[Generate Visual Studio Project]、もしくは[File]->[RefreshProjects]
  • B. Setup.batと同階層に存在する、[GenerateProjectFiles.bat]を実行
  • C. YOUR_PROJECT.uprojectを右クリック->[Generate Visual Studio project files]
  • D. VisualStudioExtensionを入れておき、VisualStudioのツールバーから[Refresh Projects]ボタンを押す

【非改造・改造別 作成例】

リリースされているエンジンを使用したC++開発(非改造エンジン)

  • A. EpicGamesLauncherからEditorを起動、YOUR_PROJECT.uprojectをEditorで開き、メニューの[File]->[RefreshProjects]
  • B. YOUR_PROJECT.uprojectを右クリック->[Open]でEditorを開く->メニューの[File]->[Refresh Projects]
  • C. YOUR_PROJECT.uprojectを右クリック->[Generate Visual Studio project files]
  • D. YOUR_PROJECT.slnをVisualStudioで開き、VisualStudioのツールバーから[Refresh Projects]ボタンを押す(要:VisualStudioExtension)

改造エンジンを使用したC++開発

  • A. 改造エンジンと同階層にそのエンジンで開発するプロジェクトのフォルダを入れ、さらに同階層にGenerateProjectFiles.batがある形にする
例:
  MyUE4Engine/Engine/ (エンジンソース)
  MyUE4Engine/YOUR_PROJECT/ 
  MyUE4Engine/GenerateProjectFiles.bat

 MyUE4Engine/GenerateProjectFiles.batでソリューションファイルを作成する。 また、.uprojectのEngineAssociation項目は空にしておく。 "EngineAssociation": "", こうすることで、なぜかYOUR_PROJECTの上の階層にあるEngineフォルダ内のUE4をエンジンとして使ってくれるらしい。

(設定参考: https://github.com/EpicGames/UnrealTournament

  • B. 改造エンジンのEditor起動、YOUR_PROJECT.uprojectをEditorで開き、メニューの[File]->[Refresh Projects]
  • C. YOUR_PROJECT.slnをVisualStudioで開き、VisualStudioのツールバーから[Refresh Projects]ボタンを押す(要:VisualStudioExtension)

補足

Aの方法だと、MyUE4Engine/UE4.slnが作られるので、それを開きます。 BとCの方法だと、開くのはUE4.slnではなく、YOUR_PROJECT.slnになって、パスはMyUE4Engine/YOUR_PROJECT/YOUR_PROJECT.slnになると思います。

おすすめの方法は?

非改造 -> どれでもやりやすい方法でいいと思います。

改造 -> AのGenerateProjectFiles.batを使用したもの。同じフォルダにまとまっていることで管理しやすくなり、uprojectのEngineAssociationを空白で管理できるので、環境によってEngineAssociationが異なるハッシュタグになってuprojectファイルに差分が出てしまう問題を防げる。

例:改造エンジンでゲームを作ってみたい(Windows環境)

Engineソースをダウンロードする。develop/MyUE4GameパスにてGitからCloneした。

develop/MyUE4Game/Engine/
develop/MyUE4Game/Samples/
develop/MyUE4Game/Templates/
develop/MyUE4Game/Setup.bat
develop/MyUE4Game/GenerateProjectFiles.bat
develop/MyUE4Game/README.md

Setup.batで必要なものをインストール。 GenerateProjectFiles.batでエンジン用ソリューションUE4.slnを作成。

develop/MyUE4Game/Engine/
develop/MyUE4Game/Samples/
develop/MyUE4Game/Templates/
develop/MyUE4Game/Setup.bat
develop/MyUE4Game/GenerateProjectFiles.bat
develop/MyUE4Game/README.md
develop/MyUE4Game/UE4.sln <- New!

エンジンをビルドしてEditor起動。 テンプレートからC++プロジェクトを作成する。作成パスはdevelop/MyUE4Gameにして、プロジェクト名はFPSGameとする。

develop/MyUE4Game/Engine/
develop/MyUE4Game/Samples/
develop/MyUE4Game/Templates/
develop/MyUE4Game/FPSGame/ <- New!
  develop/MyUE4Game/FPSGame/FPSGame.uproject <- New!
develop/MyUE4Game/Setup.bat
develop/MyUE4Game/GenerateProjectFiles.bat
develop/MyUE4Game/README.md
develop/MyUE4Game/UE4.sln

develop/MyUE4Game/UE4.slnを開くと、FPSGameソリューションが含まれているので、それをUE4.slnのスタートアッププロジェクトにすれば完成。 (出来ていなかったらGenerateProjectFiles.batで再作成する)

エンジンいじらない状態でゲーム部分だけ開発するときには、develop/MyUE4Game/FPSGame/FPSGame.slnを開いてビルドして実行でもよいとかそんな感じだったと思います。(多分)

トラブルシューティング

UE4のVisualStudioExtensionってどこにあるの?

エンジンコードフォルダの、Engine\Extras\UnrealVSにあります。 (詳細: https://docs.unrealengine.com/latest/JPN/Programming/Development/VisualStudioSetup/UnrealVS/index.html

.uprojectを右クリックでUnrealのメニューが出てこない

EpicGamesLauncherと拡張子の関係付けができていない。 EpicGamesLauncherを起動して開くと、復旧するか聞かれるので復旧してください。

VisualStudioのツールバーの[Refresh Projects]がグレーアイコンで押せない

同上?使っていないので条件は詳しく調べていません……とりあえず次のを試してみてはいかがでしょうか?

EpicGamesLauncherと拡張子の関係付けができていないため?EpicGamesLauncherを起動して開くと、復旧するか聞かれるので復旧してみてください。 もしくは、GenerateProjectFiles.batがソリューションファイルと同じフォルダにないから? 改造エンジンだと非対応?

.uprojectで生成されるエンジンコードのバージョンが決まる基準は何なの?

 .uprojectの"EngineAssociation": の値で決まる。.uprojectをテキストファイルとして開くと書いてあります。

  • 単純な数値 -> エンジンのバージョン(Epic Games Launcherのエンジンを使用する)
  • ハッシュ値 -> PC固有のパスがレジストリに登録されていて、それが基準になる
  • 空欄 -> 親フォルダにエンジンフォルダがあればそれを使用する。無ければバージョン指定をGUIで設定する必要がある

 参考: http://historia.co.jp/archives/2316/

GenerateProjectFiles.batが見当たりません

非改造エンジン(Launcherからダウンロードしたエンジン)には存在しません。 代わりにuprojectファイルを右クリックして、[Generate Visual Studio project files]からslnなどのファイルを更新しましょう。

VisualStudio2017のソリューション作りたいのに2015のが作られてしまう

対応しているエンジンバージョンの場合、GenerateProjectFiles.batのコマンドライン引数に -2017 を加えると作ってくれます。 2019でも同じだと思います。

$ ./GenerateProjectFiles.bat -2017

技術書典4で頒布したUE4 C++ 同人誌の電子版を販売開始しました

Twitterのみでしか宣伝しておりませんでしたので、こちらでも宣伝します。 技術書典4にて物理本で頒布したUnreal C++本のPDF電子版の販売をBoothにて行っております。 本文132ページ、1200円です。

booth.pm

(紹介文より抜粋) UE4でゲーム開発をしようとするときに、どうやってUnreal C++メインで実装していくのかをステップバイステップで解説します。UE4のバージョンは4.19で動作確認しております。(物理本を初頒布時の最新版です)

コンポーネントの追加、アクター生成、当たり判定、デリゲート、入力、UI(UMG)などゲーム制作における基本を作る方法を、簡単なシューティングゲームを題材に詰め込みました。 ※必要な機能を可能な限りUnrealC++で実装してみるという内容であり、ゲームは完成しませんのでご注意ください。

ObjectInitializer.CreateDefaultSubobject、ConstructorHelpers::FObjectFinder、ConstructorHelpers::FClassFinder、TSubclassOf、CODなど気になる単語がありましたら読んでみると役に立つかもしれません。 タイトルが悪かったのか、『UnrealC++ 本』だったり、『UE4 C++ 本』とかで検索しても出てこなかったので、これで少しググラビリティ上がるといいなと思いつつ宣伝エントリを書きました。 もしご興味があればご購入していただけると増補改訂版や続編とか、UE4関連同人誌が今後出るかもしれません。

よろしくお願いしますー。

f:id:finap:20190223101801p:plain

UE4 Outerについて調査してみた

この記事はUnreal Engine 4 (UE4) Advent Calendar 2018の12日目の記事です。

qiita.com

Outer?

Unreal C++を書いていると、Outerというキーワードに遭遇します。これは NewObject の引数に存在したり、かなりクラス基底部分の UObjectBase::GetOuter 関数にも存在します。 今回、このOuterについて調べてみました。

まずはコードのコメントを読んでみる

Outerのコメントを見ると、『Object thier object resides in.(このオブジェクトが存在するオブジェクト)』と説明されております。 つまりOuterは、俗にいうOwner、そのオブジェクトの親を示している……?でもActorにはOwnerというのが別に存在しているけど……?

では実際はどうなのか、例から実際の動作を見ていきましょう。

ActorのOuter

テストコードは次のような単純なものです。

void AMyActor::BeginPlay()
{
  Super::BeginPlay();
    
  UObject* CurrentOuter = GetOuter();
  while (CurrentOuter != nullptr)
  {
    UE_LOG(LogTemp, Warning, TEXT("Outer:%s"), *CurrentOuter->GetName())
    CurrentOuter = CurrentOuter->GetOuter();
  }
}

ThirdPersonExampleに自分で作ったActorを継承したBlueprintをレベルに置き、実行してみます。 結果、そのActorのOuterを辿っていくと次のようになります。

MyActor AActor

PersistentLevel(MyActor->GetOuter) == ULevel

 ┗ThirdPersonExampleMap(PersistentLevel->GetOuter) == UWorld

  ┗/Game/ThirdPersonBP/Maps/ThirdPersonExampleMap(PIEだと名前変わります) == UPackage

MyActorはPersistentLevelに作られ、PersistentLevelはThirdPersonExampleMapに作られ……といった流れになっています。 しかし、ThirdPersonExampleMapが/Game/ThirdPersonBP/Maps/ThirdPersonExampleMapに作られたと考えるのは少し違和感を感じます。 /Game/ThirdPersonBP/Maps/ThirdPersonExampleMapはUPackageなのでアセット(ファイル)なのだから、ファイル自体から作られるのではなく、他の関数から読み込まれて作られそうなイメージがあります。

なので、Outerは誰に作られたかというよりも、何によって作られたかと考えるのがしっくりくるかもしれません。

また、UObjectBaseUtilityには GetOutermost という関数もありますが、これは存在する一番上の親を取得する関数です。 つまり、さきほどのMyActorで GetOutermost 関数を呼ぶと、/Game/ThirdPersonBP/Maps/ThirdPersonExampleMapのUPackageが取得できます。

LoadObjectのOuter

次に、 LoadObject<USkeletalMesh>(this, TEXT("/Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin")) したUObjectのOuterを辿った場合は以下のとおりです。

/Game/Mannequin/Character/Mesh/SK_Mannequin UPackage

というように、 LoadObject したオブジェクトのOuterはUPackageのみでした。さきほどとは違って、 LoadObject を呼んだときに引数に渡したUObjectのポインタ this がOuterに含まれておりませんでした。 ソースコードを読んでみると、どうやら LoadObject 関数の引数のOuterは、オブジェクトの読み込み・既に読み込まれているかどうかの検索先の絞り込みの判断に使われるようです。

他にも、ActorとActorComponentは乗っているLevelのアセット、 LoadObject したUObjectではそれの元となるアセットがOuterの終点になるようです。

NewObjectのOuter

では通常の NewObject したUObjectの場合にはどうなるでしょうか。

NewObject テンプレート関数は、最初の引数でOuterオブジェクトを指定します。 こちらにNULLを渡した場合、TransientPackageと呼ばれるものがOuterに設定されるようです。そして、オーバーロードされている実装には、デフォルト引数で GetTransientPackage 関数が指定されているものが存在します。 これによりグローバル変数で持っている静的なUPackageとして存在しているTransientPackageがOuterとしてデフォルト引数で渡されます。

実際に NewObject<UObject>() として関数を呼び出したときのOuterは、/Engine/Transient(UPackage)が表示されます。 Transientとは一時的という意味で、プログラム内で一時的なものとして作られたという意味で指定されているものだと思われます。UPROPERTYにもオプションでありますね。

ちなみに、 NewObject のOuter引数にNULLではなく、MyActorの this を渡した場合には次のようになりました。

Object_0

MyActor(BuleprintでWorldに置いた場合には、Blueprint名になる)

 ┗PersistentLevel(MyActor->GetOuter)

  ┗ThirdPersonExampleMap(PersistentLevel->GetOuter)

   ┗/Game/ThirdPersonBP/Maps/ThirdPersonExampleMap(PIEだと名前変わります)

Outerのまとめ

Outerは派生クラスによって取得できるものが変わることがわかりました。

では最後にまとめてみると、Outerの終点(Outermost)は次のようになるようです。

  • ActorとActorComponentはSpawnした先のLevelのアセット
  • LoadObjectしたUObjectではロード先のデータアセット
  • NewObject関数経由の場合は、引数Outerに渡したものにより変化します
  • 実行中にしか作成されない一時的なものはTransientPackageがOuterに指定されます

Outerを理解して、さらにUnreal C++を便利に使っていきましょう。


宣伝

最近はVTuberが熱い、ということでバ美肉しました。セルフ受肉です。

UE4の動画もやるかもしれませんので、よろしければよろしくお願いします。

というわけで、明日はVTuber繋がり(?)の水瀬ツバキちゃんおかずさんの『UE4 & iOS開発時のデバッグ・プロファイリング方法 まとめ 2018』です。

いつもお世話になっております。