大雨のち大晴れ

C++とかUE4とかの記事を書くかもしれないブログ

【UE4】C++でキー入力を配列にまとめてみる

今回の説明に出てくるソースが入ったプロジェクトです。
こちらを見ながら今回の記事を読んでいただけると分かりやすいと思います。
github.com

前置き

こんにちは、最近インフルエンザが流行っているらしいので気を付けようと思う今日この頃です。

今回はUE4の入力をC++の配列にまとめてグローバルに呼び出せるようにしてみたいと思います。
間違い等ありましたらご指摘いただけると幸いです。

APlayerControllerのサブクラスを作成


まずは入力を取りたいのでAPlayerControllerのサブクラスを作成します。

そして、入力を保持する為の配列をメンバ変数に追加します。
入力は毎フレーム更新を行うので、Tickをオーバーライドします。

配列のアクセスにマジックナンバーを使用したくないのでenumも定義しましょう。
あとはキャストを書くのが面倒なので、便利そうな関数をちょこっと書いておきます。。。

// MyPlayerController.h
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"

template<typename T>
inline int32 ToInt32(T _val)
{
	return static_cast<int32>(_val);
}

UENUM(BlueprintType)
enum class EMY_INPUT_TYPE : uint8
{
	KEY_UP,
	KEY_DOWN,
	KEY_RIGHT,
	KEY_LEFT,

	MAX,
};

UCLASS()
class INPUTTESTPROJECT_API AMyPlayerController : public APlayerController
{
	GENERATED_BODY()
	
private:
	TArray<bool> PastPressed; // 1フレーム前の入力情報
	TArray<bool> CurrentPressed; // 現フレームの入力情報

protected:
public:
	AMyPlayerController();

	virtual void Tick(float _deltaTime) override;
};



次はcppの方で関数を実装していきます。

入力はIsInputKeyDownを使用して取っていきます。
指定したキーが押されてる場合はtrue、押されていない場合はfalseをくれます。
今回は十字キーの入力を取っていきます。

// MyPlayerController.cpp
#include "MyPlayerController.h"

AMyPlayerController::AMyPlayerController()
{
	PastPressed.Init(false, ToInt32(EMY_INPUT_TYPE::MAX));
	CurrentPressed.Init(false, ToInt32(EMY_INPUT_TYPE::MAX));
}

void AMyPlayerController::Tick(float _deltaTime)
{
	PastPressed = CurrentPressed;

	// 入力情報の更新
	CurrentPressed[ToInt32(EMY_INPUT_TYPE::KEY_UP)]	  = IsInputKeyDown(EKeys::Up);
	CurrentPressed[ToInt32(EMY_INPUT_TYPE::KEY_DOWN)]  = IsInputKeyDown(EKeys::Down);
	CurrentPressed[ToInt32(EMY_INPUT_TYPE::KEY_RIGHT)] = IsInputKeyDown(EKeys::Right);
	CurrentPressed[ToInt32(EMY_INPUT_TYPE::KEY_LEFT)]  = IsInputKeyDown(EKeys::Left);
}

入力判定の実装

今回はキーを押した瞬間と、押されている間と、離した瞬間の判定を実装したいと思います。

まずは先ほどのAMyPlayerControllerに以下の関数を追加します。

// MyPlayerController.h
public:
	UFUNCTION(BlueprintCallable)
	bool IsPressed(EMY_INPUT_TYPE _inputType);
	
	UFUNCTION(BlueprintCallable)
	bool IsPressHold(EMY_INPUT_TYPE _inputType);
	
	UFUNCTION(BlueprintCallable)
	bool IsReleased(EMY_INPUT_TYPE _inputType);


実装はこんな感じになります。

// MyPlayerController.cpp
bool AMyPlayerController::IsPressed(EMY_INPUT_TYPE _inputType)
{
	bool pp = PastPressed[ToInt32(_inputType)];
	bool cp = CurrentPressed[ToInt32(_inputType)];

	return ((pp ^ cp) & (!pp));
}

bool AMyPlayerController::IsPressHold(EMY_INPUT_TYPE _inputType)
{
	return CurrentPressed[ToInt32(_inputType)];
}

bool AMyPlayerController::IsReleased(EMY_INPUT_TYPE _inputType)
{
	bool pp = PastPressed[ToInt32(_inputType)];
	bool cp = CurrentPressed[ToInt32(_inputType)];

	return ((pp ^ cp) & (pp));
}

実際に使ってみる

今回作成したプレイヤーコントローラーを機能させるにはレベルに設定しないといけないのですが、(めんどうなので…)説明は省略させて頂きます。ご了承ください。。

そして実際に使ってみたコードが以下になります。

AMyPlayerController* MyController = Cast<AMyPlayerController>(UGameplayStatics::GetPlayerController(this, 0));

if (MyController)
{
	if (MyController->IsPressHold(EMY_INPUT_TYPE::KEY_UP))
	{
		// ↑キーが押されているッ!
	}
}

これで無事C++で楽にキー入力を取る事が出来ました!
ただ、入力周りはこれで行こうとすると、エディタで設定できるアクションマッピングは使わない方がいいかもしれません。単純にどっちで入力してるのか管理し辛くなるので…。


それでもBPで入力処理を実装したい時がどうしても出てくると思います。その度にTickからノード伸ばして…なんてやってられません!
なので、UFUNCTION(BlueprintImplementableEvent)で関数を作ってC++側でキー入力判定をした時に呼んであげればいい感じになると思います。

今回のプロジェクトのソースで言うとMyActorでやってますので、見て頂ければと思います。


それではお疲れ様でした。良いUnrealライフを…。