BLOGブログ

2018.02.02UE4UE/ Sequencer

[UE4][C++]シーケンサーで非対応のパラメータを制御可能にする方法

エンジニアの山中です。
本記事では、シーケンサーを使用する中で発生し得る問題と解決法をご紹介します。

【問題】

シーケンサーを使用していると、とあるパラメータの値をシーケンサー内で書き換えたいのに非対応となっているケースがあります。
今回は「CaptureComponent2D」のパラメータ「CaptureEveryFrame」を例にご説明します。



「CaptureEveryFrame」は、シーンのキャプチャを常に行い続けるかどうかのフラグです

制御可能なパラメータに関しては、LevelSequenceアセットを開くことでパラメータ横にアイコンが付き
シーケンサー内からもパラメータが選択可能になるのですが、「CaptureEveryFrame」には
アイコンも無ければ、項目にも挙がっていません。


シーケンサーで制御可能であることを示すアイコンとシーケンサー内のパラメータ追加欄
他のパラメータ「CaptureSource」「CaptureSortPriority」は制御可能のようです

それでは、シーケンサー内で「EveryCaptureFrame」を制御可能にするにはどうすれば良いかを探ってみます。

【調査】

そもそも制御可能、不可能の項目は何が違うのでしょうか?ソースの各定義を見てみます。

CaptureSource(制御可能パラメータ


CaptureSortPriority(制御可能パラメータ


CaptureEveryFrame(制御不可能パラメータ

まず「CaptureSource」に注目すると、UPROPERTYに他とは異なる「interp」というプロパティが設定されています。
公式ドキュメント」を見ると、どうやらこの設定を行うことでマチネからアクセス可能になるようです。
実際に「CaptureEveryFrame」に「interp」を設定してみた結果、正常に項目が追加されました。

ただ、「CaptureSortPriority」に関してはプロパティ「interp」が設定されていないにも関わらず
制御可能になっているので、制御方法は他にもあるはず!ということでもう少し調査を進めます。

次に、実際に項目が表示されるタイミングではリスト化された情報が存在していると予想して
そのリストがまとめられる条件を探ってみます。
まずは「SceneCaptureComponent2D」の[+Track]ボタンが押された際に通る処理を探します。


ここに注目!

ソース内で「”Properties”」と書かれてある場所を全検索します。
(この文字列が使用されているソースの周辺で項目の追加を行っているはず、と予測)


アヤシイ

ということで、「FSequencerObjectBindingNode::HandleAddTrackComboButtonGetMenuContent」を通ることが判明しました。
また、予想通り「KeyablePropertyPaths」というTArray変数に項目が追加されていることを確認しました。
次に、このTArray変数に追加を行う処理が記述されている場所を探します。

この関数内を辿っていくと、以下の条件式が見つかります。

条件:セッター関数が存在していて編集可能である、または「Interp」が設定されていること(関数として機能していること)

どうやら上記の条件を満たしたプロパティのみが項目として表示され、シーケンサーから制御可能になるようです。
ここで、先程疑問となっていた「CaptureSortPriority」に関して、定義されているヘッダーを詳しく調べてみると・・・

確かにセッター関数が存在していました。

【結果】

・UPROPERTYにてプロパティ「interp」を設定する
・セッター関数を用意する(Set「変数名」)※bool型の変数の場合は接頭辞「b」を除いた関数名にする必要があるようです。

シーケンサーで制御可能なパラメータは上記のいずれかの方法を行っていました。

ただ、今回のケースだと上記2通りの方法をそのまま行おうとするとエンジンソースの修正が発生してしまいます。
そこで、せっかくなのでエンジンソースを修正しない方法を取って解決とします。

※補足ですが、ブループリントに作成した変数をシーケンサーで制御したい場合は
変数のプロパティの「Expose to Cinematics」にチェックを入れます。

【解決方法】

「USceneCaptureComponent2D」を継承した「UTESTSceneCapture2DComponent」
「ASceneCapture2D」を継承した「ATESTSceneCapture2D」をそれぞれ作成して、
コンポーネント側の派生クラスにセッターを定義します。

#include “Engine/SceneCapture2D.h”
#include “TESTSceneCapture2D.generated.h”

UCLASS(hidecategories = (Collision, Material, Attachment, Actor), MinimalAPI)
class ATESTSceneCapture2D : public ASceneCapture2D
{
GENERATED_UCLASS_BODY()
};

#include “Components/SceneCaptureComponent2D.h”
#include “TESTSceneCaptureComponent2D.generated.h”

UCLASS(hidecategories = (abstract, Collision, Object, Physics, SceneComponent, Mobility))
class TEST_API UTESTSceneCaptureComponent2D : public USceneCaptureComponent2D
{
GENERATED_UCLASS_BODY()

public:
UFUNCTION()
void SetCaptureEveryFrame(bool bInCaptureEveryFrame) { bCaptureEveryFrame = bInCaptureEveryFrame; }
};

セッターには「UFUNCTION()」も忘れずに記述してください

次に、「ATESTSceneCapture2D」のコンストラクタにて、派生クラスの「UTESTSceneCapture2DComponent」を
使用するように設定します。
この辺りの詳しい説明は過去の記事「[UE4] ObjectInitializerでコンポーネント生成を制御する」をご覧ください。

UTESTSceneCaptureComponent2D::UTESTSceneCaptureComponent2D(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bCaptureEveryFrame = false;
}

「SceneCapture2D」がメンバとして所持している「SceneCaptureComponent2D」を
「TESTSceneCapture2D」の場合は「TESTSceneCaptureComponent2D」として扱うように設定

上のソースでは、ついでに「CaptureEveryFrame」をデフォルトでOFFにしています。
理由は、シーンキャプチャの処理は負荷が掛かってしまうので本来不要なタイミングではOFFにするのが良いのですが、
配置の度に設定するのは手間ですし、設定ミスも発生してしまう為です。
(ただ、使い方やプロジェクト規模次第になりますので、参考程度に)

これで設定は全て完了したので、テストレベルに「ATESTSceneCapture2D」を配置してシーケンサーを確認します。

【最後に】

実はこの問題、既に「こちらの質問」で解決方法が提示されています。
ただ、今回の場合エンジンソースの修正を行わない、というのと
問題に対して改めてソースを追って原因を調査してみる、といった視点からまとめてみました。
参考になれば幸いです。