🏛️

私の技術ノート:【UnrealEngine】DataAssetを使って汎用性の良い入力システムを作る

に公開

今回のやること

DataAssetを使ってタグとインプットアクションを紐づけすることにより、汎用性を高くし、入力システムを扱いやすくなり、ヒューマンエラー等防止にもつながります。

必要なクラス

  • DataAssetクラス
  • EnhancedInputComponentクラス (バインドさせるため)
  • ゲームプレイタグクラス (紐づけするタグ作成)
  • Characterクラス (入力受け付けるキャラ)

DataAssetクラス

UDataAsset_InputConfig.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "DataAsset_InputConfig.generated.h"

class UInputAction;
class UInputMappingContext;

//** タグとインプットアクションを紐づけするための構造体 **//
USTRUCT(BlueprintType)
struct FMyActionInputData
{
	GENERATED_USTRUCT_BODY()

public:

    //タグの定義
	UPROPERTY(EditDefaultsOnly, meta = (Categories = "InputTag"))
	FGameplayTag InputTag;

    //インプットアクションの定義
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputAction> InputAction;
};

/**
 * 
 */
UCLASS()
class MYACTION_API UDataAsset_InputConfig : public UDataAsset
{
	GENERATED_BODY()

public:

    //マッピングコンテキストの定義
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
	TObjectPtr<UInputMappingContext> IMC_Default;

    //インプットデータ構造体を配列で定義 (複数個設定可能)
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = "InputTag"))
	TArray<FMyActionInputData> NativeInputDatas;

    //タグ名からインプットアクションを取得する関数
	UInputAction* GetInputActionByTag(const FGameplayTag& TagName) const;
	
};
UDataAsset_InputConfig.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "DataAssets/DataAsset_InputConfig.h"

/// <summary>
///  指定したタグのInputActionを返す
/// </summary>
/// <param name="TagName"></param>
/// <returns></returns>
UInputAction* UDataAsset_InputConfig::GetInputActionByTag(const FGameplayTag& TagName) const
{
	for (const FMyActionInputData& InputData : NativeInputDatas)
	{
		if (InputData.InputTag == TagName && InputData.InputAction)
		{
			return InputData.InputAction;
		}
	}
	return nullptr;
}

EnhancedInputComponentクラス

UMyActionInputComponent.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "EnhancedInputComponent.h"
#include "DataAssets/DataAsset_InputConfig.h"
#include "MyActionGameplayTags.h"
#include "MyActionInputComponent.generated.h"

/**
 * 
 */
UCLASS()
class MYACTION_API UMyActionInputComponent : public UEnhancedInputComponent
{
	GENERATED_BODY()

public:

    //バインドさせる関数を自作
    template<class UserObject, typename CallbackFunc>
    void BindNativeInputAction(const UDataAsset_InputConfig* DA_InputConfig, const FGameplayTag& TagName, ETriggerEvent TriggerEvent, UserObject* ContextObject, CallbackFunc Func);
};

template<class UserObject, typename CallbackFunc>
inline void UMyActionInputComponent::BindNativeInputAction(const UDataAsset_InputConfig* DA_InputConfig, const FGameplayTag& TagName, ETriggerEvent TriggerEvent, UserObject* ContextObject, CallbackFunc Func)
{
    checkf(DA_InputConfig, TEXT("Not found Data asset 'DA_InputConfig' "));

    BindAction(DA_InputConfig->GetInputActionByTag(TagName), TriggerEvent, ContextObject, Func);
}

ゲームプレイタグクラス

MyActionGameplayTags.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "NativeGameplayTags.h"

namespace MyActionGameplayTags
{
	//** Input tags **//
	MYACTION_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
	MYACTION_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
}
MyActionGameplayTags.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyActionGameplayTags.h"

namespace MyActionGameplayTags
{
	//** Input tags **//
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, "InputTag.Move");
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, "InputTag.Look");
}

Characterクラス

AMyActionHeroCharacter.h
//キャラクタークラスのヘッダーに以下を追加
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "DataAsset", meta = (AllowPrivateAccess = true))
    TObjectPtr<UDataAsset_InputConfig> DA_InputConfig;
AMyActionHeroCharacter.cpp
void AMyActionHeroCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//** LocalPlayerの取得 **//
	ULocalPlayer* LocalPlayer = GetController<APlayerController>()->GetLocalPlayer();
	check(LocalPlayer);

	//** EnhancedInputLocalSubSystemの取得 **//
	UEnhancedInputLocalPlayerSubsystem* SubSystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LocalPlayer);
	check(SubSystem);

	//** SubSystemからマッピングコンテキストを追加 **//
	SubSystem->AddMappingContext(DA_InputConfig->IMC_Default, 0);

	//** MyActionInputComponentの取得 **//
	UMyActionInputComponent* MyInputComponent = CastChecked<UMyActionInputComponent>(PlayerInputComponent);

	//** InputComponentからバインド **//
	MyInputComponent->BindNativeInputAction(DA_InputConfig, MyActionGameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move);
	MyInputComponent->BindNativeInputAction(DA_InputConfig, MyActionGameplayTags::InputTag_Look, ETriggerEvent::Triggered, this, &ThisClass::Input_Look);
}

Discussion