【UE,C++ メモ -6-】開くDoorの作り方

## 事前説明
- 以下の内容でプロジェクト作成を行っています。
    - 3人称視点プロジェクト
    - C++
    - スターターコンテンツ有
- C++コーディングを行う為のプラグインや設定を事前に行っています。
## 最終的にできあがるもの
![Complete.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/f0dc89df-cfee-edd9-6b9a-8df8a02ca7a1.gif)
## プレイヤーのアクション設定
### InputActionの追加
自動作成分はMoveとLookしか用意されていないので、Door開閉用のInputActionを用意する。
今回は**IA_Interact**という名前で**Digital**入力にしました。
それをC++上で関数にBindします。（方法は割愛します。）
3人称視点プロジェクトで作成すると、**最初からBuild.csのモジュール一覧にEnhancedInputが追加されている**ので、使用予定がある場合は3人称視点プロジェクト(恐らく1人称視点でも可)で作成すると良いと思います。
### LineTraceを飛ばす
#### 事前設定
[プロジェクト設定]->[コリジョン]から新しいTraceChannelを追加する。
![Collision.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/0b93b73b-6a40-84bc-5e54-4593c234b1bc.png)
Presetを開き、プロファイルを設定し、TraceがSM_Doorにしか当たらないように設定する。
今回はOverlapに当たらず、Blockのみ当たるようにしました。
プロジェクトフォルダをエクスプローラーで開き[**Config**]->[**DefaultEngine.ini**]を表示する。
新しく作成したTraceChannelに紐づいたChannelを探す。私の場合は「**ECC_GameTraceChannel1**」でした。
![DefaultEngine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/539bff84-e83a-b2eb-9e92-0782145aec6b.png)
### コードを書く
- GetPlayerViewPoint(FVector& Location, FRotator& Rotation)
 ざっくり画面の中心のLocationと回転を受け取れる。
- LineTraceSingleByChannel()
    [UE5ドキュメント](https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/Engine/UWorld/LineTraceSingleByChannel/)から引用
    >Syntax
    struct FHitResult & OutHit,
    const FVector & Start,
    const FVector & End,
    ECollisionChannel TraceChannel,
    const FCollisionQueryParams & Params,
    const FCollisionResponseParams & ResponseParam
- DrawDebugPoint()（引数割愛）
 ざっくり当たったところに点を表示
 
![PlayerInteract.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/8b106797-1f68-9590-7055-752b0b839e64.png)
RangeがTraceの長さを表します。後に変更できるよう変数にしています。

上手くできていれば画面中央前方でHitしたActorを検出できるようになっているはずです！
![PlayerInteract.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/ca7a4ec7-70a7-3f60-beb8-c6fb9b82ad75.gif)
## BP_Doorを作成
### CPP_Interactを継承する。
今回作成したCPP_A_InteractのヘッダとCPPファイルコードは以下になります。
#### Interact()関数の記述が特殊なものになっています。これは**CPPで作成した関数をBPでオーバーライドする為に必要な記述になっています。**
<details><summary>ヘッダファイル</summary>

```C++:CPP_A_Interact.h
UCLASS()
class GRIDWORLD_API ACPP_A_Interact : public AActor
{
	GENERATED_BODY()

protected:

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Main, meta = (AllowPrivateAccess = "true"))
	UStaticMeshComponent* Mesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Main, meta = (AllowPrivateAccess = "true"))
	class USphereComponent* InteractionArea;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Main, meta = (AllowPrivateAccess = "true"))
	float InteractAreaRadius = 600.f;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	bool bCanInteract = true;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Main, meta = (AllowPrivateAccess = "true"))
	class UWidgetComponent* IconWidgetComp;
	
public:	
	// Sets default values for this actor's properties
	ACPP_A_Interact();

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

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

public:
	
	// インタラクト時のイベントを記述する。
	// 継承先で中身を決定する。(canInteractは親クラスでfalseになるよう設定されている。)
	UFUNCTION(BlueprintNativeEvent,BlueprintCallable)
	void Interaction(); // オーバーライドさせたい関数
	virtual void Interaction_Implementation(); // BPでオーバーライドする為の記述法_Implementationを末尾につける
	
	bool GetAreaInPawn();
};

```
</details>
<details><summary>CPPファイル</summary>

```C++:CPP_A_Interact.cpp
ACPP_A_Interact::ACPP_A_Interact()
{
 	// 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;

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
	RootComponent = Mesh;

	InteractionArea = CreateDefaultSubobject<USphereComponent>(TEXT("InteractionArea"));
	InteractionArea->SetupAttachment(RootComponent);
	InteractionArea->SetSphereRadius(InteractAreaRadius);

	IconWidgetComp = CreateDefaultSubobject<UWidgetComponent>(TEXT("IconWidgetComp"));
	IconWidgetComp->SetupAttachment(RootComponent);
	IconWidgetComp->SetVisibility(false); // デフォルトは非表示
}

// Called when the game starts or when spawned
void ACPP_A_Interact::BeginPlay()
{
	Super::BeginPlay();
	
	// init
	InteractAreaRadius = 600.f;
	bCanInteract = true; // 初期からインタラクトを行えるようにしておく(状況次第で修正)
}

// Called every frame
void ACPP_A_Interact::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// Playerを検出かつ、インタラクトが可の時
	if (GetAreaInPawn() && bCanInteract)
	{
		// 表示
		IconWidgetComp->SetVisibility(true);
	}
	else
	{
		// 非表示
		IconWidgetComp->SetVisibility(false);
	}
}

void ACPP_A_Interact::Interaction_Implementation()
{
	// インタラクトが不可の場合は終了
	if (!bCanInteract) { return; }

	UE_LOG(LogTemp, Warning, TEXT("Super class Log:%s"), *this->GetActorNameOrLabel());

	// インタラクトが実行されたため、インタラクトを不可にする
	bCanInteract = false;
}

bool ACPP_A_Interact::GetAreaInPawn()
{
	TArray<AActor*> Actors;
	APawn* Player = nullptr;
	// OverlapされているActorを返す
	InteractionArea->GetOverlappingActors(Actors);

	for (AActor* actor : Actors)
	{
		Player = Cast<APawn>(actor);
		// もしプレイヤーなら
		if (Player != nullptr)
		{
			UE_LOG(LogTemp, Warning, TEXT("PlayerName:%s"), *Player->GetActorNameOrLabel());
		}
	}
	return Player != nullptr;
}
```
</details>

### BP_Doorを作成する。
#### 先ほど作成したCPP_A_Interactを継承したBP_A_Doorを作成し、画像のようにStaticMeshを登録する。
ポイントは、Doorのピポッドが表示されている箇所にあるという点です。
![DoorViewPort.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/fd488706-9e9f-b4db-15ec-50cfdb634043.png)
#### イベントグラフを組む。
私の場合、親関数を呼び出すことで、Flagが下りるようになっている。
他は特に難しい点はない。
![DoorEventGraph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/2cc70d1b-a69c-4ddc-4b19-696a81626505.png)
#### ドアをタイムラインで開くようにする。
カーブはお好みで設定する。
![DoorTimeLine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/920d0f65-12c7-37a5-9cb6-321466f1ade8.png)

上手くできていればDoorが開くようになっている。
![DoorOpen.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/f857b428-db80-46d4-5db6-1be9c36c04d8.gif)
## UIを表示して開けることを伝えよう
上記にあるCPP_A_Interactのコードにすでに記述済みなので、繰り返しの記述は割愛します。
- GetAreaPawn()(私が作成した関数です。)
　InteractionAreaにPawnがいる場合true、いない場合falseを返す。
 
WidgetComponentのVisibility設定を切り替えることで、表示と非表示を行っています。
この際、Build.csに登録されているモジュールに[**UMG**]がないと、UwidgetComponentを扱えませんので、注意が必要です。
![Build.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3277190/517c1506-0a7b-11cc-5768-2ac7c6f6be42.png)

DoorにOverlapを検出するCollisionを作成していた理由は、UIを表示したかったからです。
このCollisionにプレイヤーが飛ばすLineTraceが当たらないよう注意して、Collision設定を行ってください。

#### お疲れさまでした！