UnrealC++でActorを使う初歩、コンポーネントを追加する方法、＋α


この記事は「[Unreal Engine 4 (UE4) その2](https://qiita.com/advent-calendar/2017/ue4second)」の18日目の記事です。

# １、UnrealC++でActorへコンポーネントを追加する方法

C++側でコンポーネント追加しておくと
・各コンポーネントの初期化をC++側で行える。
・デリゲートへ割り当てる関数のC++化
等の処理をより簡潔に行えるようになります。


## １－１、C++側でコンポーネントを追加したActorをBPで継承した図
![追加.png](https://qiita-image-store.s3.amazonaws.com/0/32364/efaffa25-62ab-6806-5d59-49a98a3cc4cf.png)
Inherited(C++)がC++から継承されていることを示しています。



## １－２、ヘッダへの変更
UnrealEditor側でパラメータを参照する必要が出ると思いますので
コンポーネントのポインタは必ず作成するクラス内で保持します。

``` c++

UCLASS()
class QIITACOMPO_API ATestActor : public AActor
{
	GENERATED_BODY()
public:	
	// Sets default values for this actor's properties
	ATestActor(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

private :
	//追加するシーンコンポーネントを保持
	UPROPERTY(Category = "C_CODE", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
		class UBoxComponent* m_EnemyRootCollider;
	UPROPERTY(Category = "C_CODE", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
		class UStaticMeshComponent* m_EnemyMesh;

	//追加するアクターコンポーネントを保持
	UPROPERTY(Category = "C_CODE", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
		class UEnemySampleCtrl* m_EnemyCtrl;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
};
```



## １－３、CPPの変更
コンポーネントを追加する際、必ずRootを置き換えるようにします。
でないとUnrealEditorの表示上で変な事になります。
そのアクターで物理的には何もしたくない場合は何もしないコンポーネントとして
DummyComponent等を作っておくと便利です。
尚、AttachToという関数も有るようですがこちらは基本的には推奨されないそうです。

```c++
// Sets default values
ATestActor::ATestActor(const FObjectInitializer& ObjectInitializer ) : AActor(ObjectInitializer)
{
 	PrimaryActorTick.bCanEverTick = true;

	//ルートは必ず何かに付け替える。
	//でないと変なことになる。
	m_EnemyRootCollider = CreateDefaultSubobject<UBoxComponent>("RootCollider");
	RootComponent = Cast<USceneComponent>(m_EnemyRootCollider);

	//メッシュを追加する。
	m_EnemyMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	m_EnemyMesh->SetupAttachment(RootComponent);

	// アクターコンポーネントを追加する。
	m_EnemyCtrl = CreateDefaultSubobject<UEnemySampleCtrl>(TEXT("EnemyCtrl"));
	AddOwnedComponent(m_EnemyCtrl);
}
```
通常はここまで


## １－４、UnrealEditor上でパラメータが見れなくなってしまう時対策
時々ですが、C++側で追加したコンポーネントのパラメータがUnrealEditor上で
見れなくなるケースが発生することがあります。
この症状が出た場合下記のような問題が発生します。
・コライダ系のコンポーネントではUnrealEditorでは設定が全く変更できなくなります。
・UnrealEditorでのコンポーネントのパラメータ表示が通常と違った画面になります。
正直なところ発生条件などは分かっていませんが、対策方法はあるので紹介します。
下記のようにしておくと正常に表示されるようになります。

ヘッダのコンストラクタの宣言に引数を追加します。

```c++
// Sets default values for this actor's properties
ATestActor(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
```

C++コード側を下記のようにします。
コンストラクタ上でAActorコンストラクタを呼び出します。

```c++
ATestActor::ATestActor(const FObjectInitializer& ObjectInitializer) : AActor(ObjectInitializer)
{
}
```

# ２、デリゲートへ関数をバインドする。

アクターコンポーネント等を追加する際、
デリゲートへ関数をバインドする必要が出るかと思います。
その場合はコンストラクタで下記のようにすればバインドできます。
バージョンによってやり方が違うようなので注意が必要です。

``` C++
// Sets default values
AEnemySample::AEnemySample(const FObjectInitializer& ObjectInitializer ) : AActor(ObjectInitializer)
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	FScriptDelegate delegeteDefine;
	delegeteDefine.BindUFunction( this , "OnDeleteActor" );
	m_EnemyCtrl->m_DestroyTimeout.Add(delegeteDefine);
}

//バインドしたい関数
void AEnemySample::OnDeleteActor()
{
	this->Destroy();
	return;
}
```