事前説明
- 以下の内容でプロジェクト作成を行っています。
- 3人称視点プロジェクト
- C++
- スターターコンテンツ有
- C++コーディングを行う為のプラグインや設定を事前に行っています。
最終的にできあがるもの
プレイヤーのアクション設定
InputActionの追加
自動作成分はMoveとLookしか用意されていないので、Door開閉用のInputActionを用意する。
今回はIA_Interactという名前でDigital入力にしました。
それをC++上で関数にBindします。(方法は割愛します。)
3人称視点プロジェクトで作成すると、最初からBuild.csのモジュール一覧にEnhancedInputが追加されているので、使用予定がある場合は3人称視点プロジェクト(恐らく1人称視点でも可)で作成すると良いと思います。
LineTraceを飛ばす
事前設定
[プロジェクト設定]->[コリジョン]から新しいTraceChannelを追加する。
Presetを開き、プロファイルを設定し、TraceがSM_Doorにしか当たらないように設定する。
今回はOverlapに当たらず、Blockのみ当たるようにしました。
プロジェクトフォルダをエクスプローラーで開き[Config]->[DefaultEngine.ini]を表示する。
新しく作成したTraceChannelに紐づいたChannelを探す。私の場合は「ECC_GameTraceChannel1」でした。
コードを書く
- 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()(引数割愛)
ざっくり当たったところに点を表示
RangeがTraceの長さを表します。後に変更できるよう変数にしています。
上手くできていれば画面中央前方でHitしたActorを検出できるようになっているはずです!
BP_Doorを作成
CPP_Interactを継承する。
今回作成したCPP_A_InteractのヘッダとCPPファイルコードは以下になります。
Interact()関数の記述が特殊なものになっています。これはCPPで作成した関数をBPでオーバーライドする為に必要な記述になっています。
ヘッダファイル
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ファイル
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のピポッドが表示されている箇所にあるという点です。
イベントグラフを組む。
私の場合、親関数を呼び出すことで、Flagが下りるようになっている。
他は特に難しい点はない。
ドアをタイムラインで開くようにする。
UIを表示して開けることを伝えよう
上記にあるCPP_A_Interactのコードにすでに記述済みなので、繰り返しの記述は割愛します。
- GetAreaPawn()(私が作成した関数です。)
InteractionAreaにPawnがいる場合true、いない場合falseを返す。
WidgetComponentのVisibility設定を切り替えることで、表示と非表示を行っています。
この際、Build.csに登録されているモジュールに[UMG]がないと、UwidgetComponentを扱えませんので、注意が必要です。
DoorにOverlapを検出するCollisionを作成していた理由は、UIを表示したかったからです。
このCollisionにプレイヤーが飛ばすLineTraceが当たらないよう注意して、Collision設定を行ってください。