UE4 でアクターにプログラムからコンポーネントを追加する方法

# 環境
UE 4.10.0
Visual Studio 2015

# 概要
Unreal Engine 4 を触ってみて動作を確認する
今回は、エディタ上ではなくプログラム上でコンポーネントを追加する方法

# 本文
## アクターにプログラム上でコンポーネントを追加したい
初めて UE4 を触った時に、アクターを定義してエディタからスタティックメッシュ
コンポーネントを追加し、メッシュを表示してみた。しかし、エディタ上でコンポー
ネントを手作業で指定するのは手間な上、条件によって表示するメッシュを変更したい
場合などの対応ができないので、プログラム上でアクターにメッシュコンポーネントを
追加する方法が無いかを調べてみた。

結論から書くと、以下のコードで対応できるようだ。
※アクターに球体のオブジェクトを追加して、表示するサンプルです

[ヘッダー]
※ TEST20160104 はプロジェクト名です
UCLASS()
class TEST20160104_API AMyActor : public AActor
{
    GENERATED_BODY()

public:	
    AMyActor(const class FObjectInitializer& ObjectInitializer);

    virtual void BeginPlay() override;
	
    virtual void Tick( float DeltaSeconds ) override;

private:
    UStaticMeshComponent* mStaticMeshComponent;
    UStaticMesh* mStaticMesh;
};

[コード]
AMyActor::AMyActor(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, mStaticMeshComponent(nullptr)
, mStaticMesh(nullptr)
{
    PrimaryActorTick.bCanEverTick = true;
	
    static ConstructorHelpers::FObjectFinder<UStaticMesh> staticMeshAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
    if (staticMeshAsset.Succeeded())
    {
        mStaticMesh = staticMeshAsset.Object;
        mStaticMeshComponent = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("StaticMeshName"));

        if (mStaticMeshComponent)
        {
            RootComponent = mStaticMeshComponent;
            mStaticMeshComponent->SetStaticMesh(mStaticMesh);
        }
    }
}

## コードの解説
まずは、ヘッダファイル。
スタティックメッシュと、スタティックメッシュコンポーネントをメンバ変数に追加
している。その他は、コンストラクタを引数ありのものに変更している。

次は、コード。
スタティックメッシュのファイルパスを指定する。アセットにアクセスできたらメッシュ
に参照をコピーし、スタティックメッシュコンポーネントを作成してメッシュをセット
している。

## アセットのパスに関する疑問
処理の流れに関しては、ゲームプログラムに馴染みのある方ならば問題ないはず。
しかし、アセットのパスの指定方法については疑問がある。
スタティックメッシュのアセットのパスを以下のように指定してある。

"/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"

StarterContent フォルダ以下のフォルダは、プロジェクトを新規で作成するとデフォ
ルトで付いてくるのでそのまま使用している。問題はそのフォルダパスだが、プロジェ
クトフォルダでは、以下のようになっているはず。

プロジェクト名\Content\StarterContent\Shapes\Shape_Sphere.uasset

要は Content フォルダが Game フォルダに置き換わっている。
なぜそうなるのかは調べてもよく分からなかったが、パスの先頭を抽象化しておく事で
様々なプラットフォームに対応できるようになっているのだろう。この抽象化はエディタ
上でもそのようになっている。エディタ上の [コンテンツブラウザ] タブをクリックして
StarterContent にマウスカーソルを合わせると "/Game/StarterContent" というパス
が表示されている。

## Game フォルダを無視してみる
ConstructorHelpers::FObjectFinder クラスでアセットを指定する場合 Game フォルダ
をパスの先頭に指定したが、これを無視するとどうなるか？を試してみた。
コードを以下のように変更してみた。

[コード]
static ConstructorHelpers::FObjectFinder<UStaticMesh> staticMeshAsset(TEXT("/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));

結論を書くと、コンパイルは普通に通る。
しかし、エディタのアウトプットログにはしっかりエラーが出ていた。

[エラーメッセージ]
LogPackageName:Error: DoesPackageExist: DoesPackageExist FAILED: '/StarterContent/Shapes/Shape_Sphere' is not a standard unreal filename or a long path name. Reason: パスの始まりが無効なルートです。パスは次のルートで始めてください: '/Engine/', '/Game/', '/Paper2D/', '/LeapMotionController/', '/Config/', '/Script/', or '/Temp/'
LogLinker:Warning: ファイル '/StarterContent/Shapes/Shape_Sphere' が見つかりません

やはり Game フォルダを指定する必要があるようだ。
他にも、指定できるフォルダ名があるようだが、そのあたりは追々調査しよう。

## 拡張子の疑問
アセットのパスを指定した際の拡張子にも疑問はある。

"/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"

対応するフォルダにあるのは *.uasset という拡張子のアセットだ。
ドキュメントのコピペミスか？と思い、試しに拡張子を uaset に変更して実行
してみた。

"/Game/StarterContent/Shapes/Shape_Sphere.uasset"

[エラーメッセージ]
Error: CDO Constructor (MyActor): Failed to find /Game/StarterContent/Shapes/Shape_Sphere.uasset

どうやら FObjectFinder に指定するのはアセットのファイルパスとは少し異なるようだ。
*.uasset をバイナリエディタで開いてみると、FBX のデータの他に様々なメタデータが
付加されているようだ。"Shape_Sphere.Shape_Sphere" という指定方法については何か
しらの意味があるようだ。この辺りも追々調査する事にしよう。

# まとめ
プログラム上で、コンポーネントの追加を行うにはメンバ変数を追加し、アセットのパス
を指定してコンポーネントを探し、セットアップする。パスの指定については [コンテンツ
ブラウザ] にて確認するのが確実。なお CreateDefaultSubobject は、コンストラクタで
呼ばないとエラーになるようだ。