johnnyGameStudio’s blog

無能なゲームプログラマのぼやき ぎーくになりたい Twitter: https://twitter.com/JGS_Developer

【UE4】Unityで慣れたゲームPGがUE4で覚えるべきTips〜その2〜

はじめに

前回の記事(Unityで慣れたゲームPGがUE4で覚えるべきTips)を書いてからだいぶ時間がたったので個人的にまとめておきたいとおもったことをつらつらと書いていく
また、UnityからUE4に移住してきた人やUE4が初めてのゲームエンジンという人も参考になると思います(多分)
間違っていたらご指摘お願いします

本記事の対象になる人

  • C++なんもわからんという人
  • Unityなら一通り触っていたという人
  • C++より高級な言語なら触っていたという人

headerファイルで無邪気にIncludeするな

前回の記事で「Includeとはなにかということ」という項目を書きました
開発をしていると実感できますが、むやみにheaderファイルでIncludeしまくってるheaderがあったときにそのheaderをイジってしまうと
「1/325...」「2/325...」「3/325...」
といった具合にVisualStudioのビルドを無心で眺めるという虚無時間が頻発します

前方宣言を使おう

前回の記事で書いたようにIncludeとはコンパイル時にそのheaderをその場所にコピペされることで記述したファイル内で参照できるようにするものです
しかし、header内では基本的には関数や変数の宣言のみで関数の中身はcppファイル内に記述されます
ということはheaderでは外部のクラス型は参照する必要がありますが関数や変数は参照できなくてお構わないということになりますので、その場合は前方宣言を使うべきです
具体的には下記のような感じ

#include "CoreMinimal.h"
//#include "Hoge.h" ←Hoge.hをincludeする必要はないのでコメント化
class Hoge;//クラス宣言だけ必要な場合はこれだけで良い

class Foo
{
public:
    Foo();
    ~Foo();

    Hoge *hoge;//クラス型として認識される
};

どういう仕組かというとC++コンパイラコンパイル時にファイル内にひとまず「これはクラスである」という宣言さえあれば実際にリンクさせるのは最後に同名のクラスを探してリンクさせてくれるので前方宣言さえやっていればコンパイルが通り、さらにheaderをincludeしていないのでそのheaderファイルで差分が発生しても変更差分として対象にならないのでコンパイルの虚無時間をへらすことに繋がります
これはとても基本的なことである一方でチームで開発しているとincludeしておけば楽なのでついつい知らずしらずのうちに無駄なincludeが増えてしまうことが多いので注意が必要です

Includeの""と<>

includeするときに下記のようにかかれているのを見ることがよくあると思うのですが、この違いを意識していますでしょうか

#include <stdio.h>//これと
#include "Hoge.h"//これの違い

これらの違いはincludeするファイルのディレクトリの検索するスコープの違いです
<>で囲まれたファイルはコンパイラが認識しているディレクトリ(システムで設定されているディレクトリや環境変数で指定されているディレクトリなど)のみから探し出してincludeしますが、""で囲まれたファイルは記述したファイルのカレントディレクトリを検索した後に<>と同じdディレクトリを検索します
知らなくても絶対マズイというものでもない気がしますが、C++ではheaderのinclude順によってエラーがでることがあるので知識として知っておきましょう
検証してないので知りませんが<>で足りるものを""で書いているとビルド速度に影響でるかも(ほんまか?)

Engine.hをIncludeするのをやめろ

色々な記事でEngine.hをincludeしているところが多いのですが、これってビルド時間が長くなる原因になるのでやめましょう
Engine.hをincludeするとEngineが持つ全てのheaderをincludeすることになるのでたしかに書いておけばEngineのクラスや関数やマクロを呼び出すことができてしまうので楽なのですが、これを色々なファイル内で書いているとコンパイル時に毎回巨大なheaderを読み込むことになるので虚無時間が増えます
「とはいってもこのAPIはなにをincludeすればいいのかわからない!」 という人はUE4C++の公式リファレンスの該当するクラスのページを見てもらえばどうincludeすればいいか書いてあります
公式リファレンス
個人的な肌感ですが、たいていの場合はEngine/Engine.hをincludeしていれば事足りるケースが多いです

Includeの順番(IWYUの記述ルール)

C++ではincludeする順番が大事です
むちゃくちゃにincludeしているとふとした時に不可解なエラーが発生します
可読性という観点から見てもめちゃくちゃにincludeしているよりも一定のルールによってincludeされていることが望ましいです
UE4の場合はIWYUの記述ルールに沿うのが最も良いと考えられます

IWYUリファレンス

UnityBuildの場合の無名名前空間の罠

UE4にはオプションとしてUnityBuildというものがある
断じて競合ゲームエンジンとは関係ない

UnityBuildについてはこちら-株式会社ヒストリアさまより-

かんたんに言ってしまえばUnityBuildとはビルド時に全てのファイルを一つのファイルにまとめてしまってからビルドしてしまえば早いでしょ?という仕組みです
その場合に問題になってしまうのはグローバルスコープにある変数や関数が同名な場合エラーが起きることです

無名名前空間は用法用量を守って正しく扱いましょう

前回の記事で「無名名前空間」を紹介したがこれを多用しているとこの問題にぶつかる
無名名前空間はそのファイル内のグローバルスコープに宣言されてしまうため、UnityBuildを使うと全てのファイルと同一スコープになってしまうためうっかり同名の関数や変数があるとガンガンエラーが出てしまうので無名名前空間は外部から完全に隠蔽したい変数や関数を扱いたい場合には便利ですが、むやみに使うと問題を引き起こしてしまいます
使う場合は本当に無名名前空間で扱うべきものなのか?を考えてから使いましょう

C++にはリフレクションがない

題名の通りです。ありません。終了。

Unityでエディタ拡張をしようとした場合、エディタ拡張で様々なことがかんたんにできます
しかしそれはC#にリフレクションがあるから機能することです
UE4でもそこらへんはカバーされてはいるのですが、それはUCLASSやUFUNCTIONなどのマクロで宣言されているもののみがリフレクションのように取得できるだけであり、例えば独自に作ったローカルのマスターデータクラスを参照して拡張したエディタから操作したいといったケースではリフレクションがないのでかなり大変です
UE4.22以前はエディタ拡張をしようとするとプラグインで作る必要がありました
確認していないのでわかりませんが、今はEditor Utility Widgetがあるので多少作りやすくなっているかもしれません

あとがき?

三連休で暇だったので書きました
C++で書いているとC#ではあんなにかんたんにできたことがなぜできないんだ意味わかんねえやはり人類にC++は早すぎた」という気持ちに支配されます全てを支配できた喜びに打ちひしがれるので大変楽しいです
とはいっても純粋なC++よりもUE4C++ではかなり書きやすくなっているようで今後はより扱いやすくなっていくのではないでしょうか
そこらへんよろしくお願いしますepic games