LoginSignup
1
0

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

Last updated at Posted at 2024-01-12

事前説明

  • 以下の内容でプロジェクト作成を行っています。
    • 3人称視点プロジェクト
    • C++
    • スターターコンテンツ有
  • C++コーディングを行う為のプラグインや設定を事前に行っています。

最終的にできあがるもの

Complete.gif

プレイヤーのアクション設定

InputActionの追加

自動作成分はMoveとLookしか用意されていないので、Door開閉用のInputActionを用意する。
今回はIA_Interactという名前でDigital入力にしました。
それをC++上で関数にBindします。(方法は割愛します。)
3人称視点プロジェクトで作成すると、最初からBuild.csのモジュール一覧にEnhancedInputが追加されているので、使用予定がある場合は3人称視点プロジェクト(恐らく1人称視点でも可)で作成すると良いと思います。

LineTraceを飛ばす

事前設定

[プロジェクト設定]->[コリジョン]から新しいTraceChannelを追加する。
Collision.png
Presetを開き、プロファイルを設定し、TraceがSM_Doorにしか当たらないように設定する。
今回はOverlapに当たらず、Blockのみ当たるようにしました。
プロジェクトフォルダをエクスプローラーで開き[Config]->[DefaultEngine.ini]を表示する。
新しく作成したTraceChannelに紐づいたChannelを探す。私の場合は「ECC_GameTraceChannel1」でした。
DefaultEngine.png

コードを書く

  • GetPlayerViewPoint(FVector& Location, FRotator& Rotation)
    ざっくり画面の中心のLocationと回転を受け取れる。
  • LineTraceSingleByChannel()
    UE5ドキュメントから引用

    Syntax
    struct FHitResult & OutHit,
    const FVector & Start,
    const FVector & End,
    ECollisionChannel TraceChannel,
    const FCollisionQueryParams & Params,
    const FCollisionResponseParams & ResponseParam

  • DrawDebugPoint()(引数割愛)
    ざっくり当たったところに点を表示

PlayerInteract.png
RangeがTraceの長さを表します。後に変更できるよう変数にしています。

上手くできていれば画面中央前方でHitしたActorを検出できるようになっているはずです!
PlayerInteract.gif

BP_Doorを作成

CPP_Interactを継承する。

今回作成したCPP_A_InteractのヘッダとCPPファイルコードは以下になります。

Interact()関数の記述が特殊なものになっています。これはCPPで作成した関数をBPでオーバーライドする為に必要な記述になっています。

ヘッダファイル
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();
};

CPPファイル
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;
}

BP_Doorを作成する。

先ほど作成したCPP_A_Interactを継承したBP_A_Doorを作成し、画像のようにStaticMeshを登録する。

ポイントは、Doorのピポッドが表示されている箇所にあるという点です。
DoorViewPort.png

イベントグラフを組む。

私の場合、親関数を呼び出すことで、Flagが下りるようになっている。
他は特に難しい点はない。
DoorEventGraph.png

ドアをタイムラインで開くようにする。

カーブはお好みで設定する。
DoorTimeLine.png

上手くできていればDoorが開くようになっている。
DoorOpen.gif

UIを表示して開けることを伝えよう

上記にあるCPP_A_Interactのコードにすでに記述済みなので、繰り返しの記述は割愛します。

  • GetAreaPawn()(私が作成した関数です。)
     InteractionAreaにPawnがいる場合true、いない場合falseを返す。

WidgetComponentのVisibility設定を切り替えることで、表示と非表示を行っています。
この際、Build.csに登録されているモジュールに[UMG]がないと、UwidgetComponentを扱えませんので、注意が必要です。
Build.png

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

お疲れさまでした!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0