0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

APlayerControllerにEnhancedInputを組み込む

Posted at

目的

APlayerControllerを継承したクラスからPlayerの移動、攻撃などを操作できるようにする。
Playerの動きは自身に実装し、それを動かすのはController側でやりたい方向け。
序盤で説明しているInputTriggerの実装はしませんので記事中の参考リンクをご確認ください。

環境

UE 5.4.4
VisualStudio 2022

EnhancedInputについて

UE5.1から標準Inputとして採用されているもの。
従来のInputMappingと大きく違うのは

  • Action/Axis Mappingsで設定していたものをInputActionとしてアセット管理できる
    (ProjectSettingsに大量のInputが並ぶことなく、見やすいので管理がしやすい)
  • InputActionに割り当てるキーや発火条件をInputMappingContextでまとめて管理できる
    (IMCを入れ替えることで手軽に入力を変更できる)
  • Trigger/Modifierを細かく設定することで離したときにのみ入力イベントを発火させたり、入力のデッドゾーンを設定できたりする
  • また、Trigger/Modifierはクラスを継承して自身独自のものも作成可能

と挙げられると思います。
また最後の独自のInputTriggerの作成についてはヒストリア様の記事が一番わかりやすいと思います。

こちらではたくさんあるUIを長押しすることで高速で見られるようにするといったゲームではよくあるような動作を独自実装されています。よくある表現ではあるもののUEに標準では搭載されていないですし、InputActionで実装を完結できるのはキレイでいいですね。

実装コード

あまり説明することもないので先にコードを貼っておきます。(今回のものに必要な物だけ抜粋し、ほかは省略)
Player側のコード

PlayerPawn.h
struct FInputActionValue;
UCLASS()
class TOONTANKS_API APlayerPawn : public APawn
{
	GENERATED_BODY()
public:
	void Move();
	void Turn(const FInputActionValue&);//Inputの入力値を受け取りたい場合はこのような形にする
    void Fire();
};


PlayerPawn.cpp
#include "PlayerPawn.h"
#include "Kismet/GameplayStatics.h"
#include "InputActionValue.h"

//実装内容に関してはありきたりなものなので、Turn以外は無視で構わないと思います。
void APlayerPawn::Move()
{
	FVector DeltaLocation(0.f);
	DeltaLocation.X = Speed * UGameplayStatics::GetWorldDeltaSeconds(this);
	AddActorLocalOffset(DeltaLocation, true);
}

void APlayerPawn::Turn(const FInputActionValue& Val)
{
	FRotator DeltaRotation = FRotator::ZeroRotator;
    //FInputActionValueから入力の値をとる
	FVector2D InputVector = Val.Get<FVector2D>();
	DeltaRotation.Yaw = InputVector.X * RotateRate * UGameplayStatics::GetWorldDeltaSeconds(this);
	AddActorLocalRotation(DeltaRotation, true);
}

void APlayerPawn::Fire()
{
    UE_LOG(LogTemp, Display, TEXT("Fire"));
}

PlayerController側のコード

MyController.h

class UInputMappingContext;
class UInputAction;
class APlayerPawn;
UCLASS()
class TOONTANKS_API AMyController : public APlayerController
{
	GENERATED_BODY()

protected:
	virtual void OnPossess(APawn* InPawn)override;
private:
	void SetupInput();
	APlayerPawn* OwnedPlayer;
    //Input Mapping Context
	UPROPERTY(EditDefaultsOnly, Category = "Input")
	const UInputMappingContext* InputMapping;
    //Input Action
	UPROPERTY(EditDefaultsOnly, Category = "Input")
	const UInputAction* InputAction_Move;//Digital
	UPROPERTY(EditDefaultsOnly, Category = "Input")
	const UInputAction* InputAction_Fire;//Digital
	UPROPERTY(EditDefaultsOnly, Category = "Input")
	const UInputAction* InputAction_Rotate;//Axis1D
};
MyController.cpp
#include "MyController.h"
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "PlayerPawn.h"

//OnPossessの時にPlayerのクラスを参照しておくのが一番安全だと思っている....
void AMyController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);
	OwnedPlayer = Cast<APlayerPawn>(GetPawn());
	SetupInput();
}

void AMyController::SetupInput()
{
    //LocalPlayerのサブシステムを利用したいためあらかじめ参照を取得しておく
	ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(Player);
	if (IsValid(LocalPlayer) && IsValid(OwnedPlayer))
	{
		if (IsValid(InputComponent))
		{
            //キャスト
			UEnhancedInputComponent* Input = Cast<UEnhancedInputComponent>(InputComponent);
            //それぞれのInputActionへ関数をバインドする
            //ETriggerEventで発火タイミングを設定できる。
			Input->BindAction(InputAction_Fire, ETriggerEvent::Started, OwnedPlayer, &APlayerPawn::Fire);
			Input->BindAction(InputAction_Move, ETriggerEvent::Triggered, OwnedPlayer, &APlayerPawn::Move);
			Input->BindAction(InputAction_Rotate, ETriggerEvent::Triggered, OwnedPlayer, &APlayerPawn::Turn);
		}
        //LocalPlayerSubsystemからEnhancedInputのサブシステムを参照
		if (UEnhancedInputLocalPlayerSubsystem* InputSystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>())
		{
			if (IsValid(InputMapping))
			{
                //MappingContextを登録
				InputSystem->AddMappingContext(InputMapping, 0);
			}
		}
	}

}

まとめ

こんな感じで従来のInputMappingでInputを実装した方ならわかる通り、実はあまり使い方は変わりません。さらに言えばInputMappingContextを使用しないのであればサブシステムへの参照などもしなくていいのでさらに従来通りの書き方に近くなるかと思います。

実装内容については入力とそれに対する動作を分けることで柔軟に変更に対応できるようになりつつ、InputMappingContextの力でキーバインドについてもInputMappingContextの入れ替えなどで対応できるようになっています。

参照

勉強の参考になったもの

公式ドキュメント
InputComponent

UEnhancedInputComponent

ETriggerEvent

FInputActionValue

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?