【UE5】BlueprintクラスをC++に置き換える手順について

プログラマーの尾関です。

あるプロジェクトで Blueprintで実装されたクラスをC++に置き換える機会があったので、そのときに学んだことを書き残しておきます。

とりあえずBlueprintで実装するというパターン

Unreal EngineでのC++実装に慣れていない開発者の場合、とりあえずBlueprintで実装する…ということはよくあると思います。そのこと自体は特に問題ではなく、Blueprintでゲームを完成まで持っていき、バグもそれほどないのであれば、Blueprintのみの実装でも全然良いと思います。

ただ問題となるのは、「保守できないほどに Blueprintが複雑に入り組んでしまっている」「不具合が大量に発生しているがBlueprintが複雑で修正作業が進まない」「他の人が何か処理を挟み込みたいときに読み解くのが難しくなっている」といったことが現実に発生している場合、または今後発生しそうな状況です。

そして、今回の件に関しては、プレイヤーである APawn が Blueprintのみで書かれてしまっており、読み解くのが難しくなっていたためC++への置き換えを行った…という経緯となります。

クラス定義の移行

C++に置き換える場合は、まずはクラスを定義します。Blueprintクラスが例えば "APawn" を継承して実装しているのであれば、C++側も "APawn" を継承してそのまま置き換えられます。

クラス継承構造の再現

BPでAPawnを継承している場合、C++クラスでもAPawnを継承するように設定します。エディターの歯車アイコンから「クラス設定」→「親クラス」で変更可能です。

変数定義の移植

親クラスを置き換えられたので、まずはメンバ変数を BP から C++ に移植します。

ここでの注意点としては2つあります。

  • 置き換えの対応関係をあらかじめ書き出しておく(データ型と変数名)
  • 「デフォルト値」を書き残しておく

特に重要なのが「デフォルト値」です。というのも C++ での置き換え時「同名変数」で上書きすると、UEの仕様上、その変数のデフォルト値は失われます

もし複雑な変数である場合は、別名変数で定義するのも1つの方法です。例えば元の変数名が「TagMap」である場合には「TamMap_Temp」という変数名を作って置き、デフォルト値をコピーした後に元の名前に置き換えます。

関数移植のベストプラクティス

変数は置き換え処理としては機械的な作業となるので、あまり問題は出ないかもしれません。問題となりやすいのが「関数」の置き換えです。

置き換える Blueprint クラスの規模によりますが、個人的には「関数を段階的に置き換える」方法をおすすめします。

具体的には単純作業で問題が起きにくい部分から置き換えて、徐々に複雑なロジックの置き換えを行う、という手順です。

  • 単純なGet/Set関数から実装
  • オブジェクト生成/破棄処理を優先
  • 複雑なフロー (分岐) を含まないシンプルな処理から着手

またGetter/Setterに置き換える副産物として、クラス内でのメンバ変数への直接参照をGetter/Setterを避けることができ、より堅牢なクラス設計になります。そして、単純な処理から置き換えることで、そのクラスの理解度も少しずつ深まっていくことになると思います。

Blueprint グラフ移植の手法

分岐や関数呼び出しを含んだ Blueprintグラフの移植方法として、分割統治を行います。

具体的には Tick() 内で A, B, C という処理が行われている場合、それらをまとめて C++ に移植するのではなく、Aをまず移植して動作確認、Bを移植して動作確認、「AとB」の呼び出しを X関数に置き換えて Tick() からは X関数を呼び出すようにする…といったように徐々にシンプルな構造に統治するようにしていきます。

テストとデバッグ

動作確認としては、最低限編集した部分にブレークポイントをおいて、起こり得る分岐を通します。

このあたりは普通のデバッグと同じですが、自分で書いた Blueprintクラスでない場合には、慎重な確認が求められると思います。

忠実にBlueprint グラフのフローを再現する

これはグラフの置き換えを行うときに注意すべき考え方の1つです。

グラフのフローを見て理解したつもりになって、最適化されたC++コードに置き換えたくなることがよくあります。場合によりけりですが、個人的には最初は冗長であったとしても Blueprint のフローをそのまま移植した方が無難です。理由としては自分の理解できていない何らかの要因によって予期せぬ挙動が発生し、置き換えに失敗することがあるからです。

ひとまず愚直にC++に置き換えた後、重複部分を1つの関数にまとめる、といったリファクタリングを行います。

ただ、置き換え元のBlueprintクラスのコードの理解が深まっていくに従って、大胆に書き換えることが可能となるので、一気に書き換えるべきかはその時の状況にもよります。

おしまい

以上、BlueprintをC++に置き換える方法についてでした。

繰り返しとなりますが、重要なのは、小規模なコンポーネント単位で移植→テスト→統合を繰り返すことです。1日あたり約2~3機能の移行を目標にし、バージョン管理システムを活用して置き換え前と置き換え後の挙動が同じになるように慎重に置き換え作業を行うこととなります。

\ 最新情報をチェック /