언리얼 엔진/Drag Down

[Drag Down] Data-Dirven 설계

라구넹 2025. 4. 30. 17:35
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "DDCharacterPlayerData.generated.h"

USTRUCT(BlueprintType)
struct FDDCharacterPlayerDataEntry
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Movement")
	FRotator RotationRate;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float JumpZVelocity;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float AirControl;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float MaxWalkSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float MinAnalogWalkSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Movement")
	float BrakingDecelerationWalking;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Camera")
	float TargetArmLength;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Anim")
	TSubclassOf<class UAnimInstance> AnimInstance;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Mesh")
	TObjectPtr<class USkeletalMesh> Mesh;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Mesh")
	FVector MeshLocation;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Mesh")
	FRotator MeshRotation;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capsule")
	float CapsuleRadius;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Capsule")
	float CapsuleHeight;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<class UInputMappingContext> MappingContext;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<class UInputAction> JumpAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<class UInputAction> ShoulderMoveAction;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
	TObjectPtr<class UInputAction> ShoulderLookAction;
};

/**
 * 
 */
UCLASS()
class DRAGDOWN_API UDDCharacterPlayerData : public UPrimaryDataAsset
{
	GENERATED_BODY()
	
public:
	UDDCharacterPlayerData();

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FDDCharacterPlayerDataEntry CharacterPlayerData;
};

유지보수에 편하고, 협업에도 좋으니 데이터 기반으로 리팩토링했다

 

void ADDCharacterPlayer::SetInputData()
{
	if (Data == nullptr) return;

	MappingContext = Data->CharacterPlayerData.MappingContext;
	JumpAction = Data->CharacterPlayerData.JumpAction;
	ShoulderMoveAction = Data->CharacterPlayerData.ShoulderMoveAction;
	ShoulderLookAction = Data->CharacterPlayerData.ShoulderLookAction;
}

void ADDCharacterPlayer::SetData()
{
	if (Data == nullptr) return;

	GetCharacterMovement()->RotationRate = Data->CharacterPlayerData.RotationRate;
	GetCharacterMovement()->JumpZVelocity = Data->CharacterPlayerData.JumpZVelocity;
	GetCharacterMovement()->AirControl = Data->CharacterPlayerData.AirControl;
	GetCharacterMovement()->MaxWalkSpeed = Data->CharacterPlayerData.MaxWalkSpeed;
	GetCharacterMovement()->MinAnalogWalkSpeed = Data->CharacterPlayerData.MinAnalogWalkSpeed;
	GetCharacterMovement()->BrakingDecelerationWalking = Data->CharacterPlayerData.BrakingDecelerationWalking;
	GetCapsuleComponent()->SetCapsuleSize(Data->CharacterPlayerData.CapsuleRadius, Data->CharacterPlayerData.CapsuleHeight);

	GetMesh()->SetSkeletalMesh(Data->CharacterPlayerData.Mesh);
	GetMesh()->SetAnimInstanceClass(Data->CharacterPlayerData.AnimInstance);
	GetMesh()->SetRelativeLocation(Data->CharacterPlayerData.MeshLocation);
	GetMesh()->SetRelativeRotation(Data->CharacterPlayerData.MeshRotation);

	UE_LOG(LogDD, Log, TEXT("[NetMode: %d] SetData - Mesh Z Pos : %f"), GetWorld()->GetNetMode(), GetMesh()->GetRelativeLocation().Z);
}

데이터 세팅은 인풋은 따로 해줘야 한다

 

void ADDCharacterPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	UE_LOG(LogDD, Log, TEXT("SetupPlayerInputComponent"));
	
	SetInputData();

	APlayerController* PlayerController = CastChecked<APlayerController>(GetController());

	if (UEnhancedInputLocalPlayerSubsystem* InputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
	{
		InputSubsystem->AddMappingContext(MappingContext, 0);
	}

	UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent);

	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ADDCharacterPlayer::Jump);  
	EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping); 
	EnhancedInputComponent->BindAction(ShoulderMoveAction, ETriggerEvent::Triggered, this, &ADDCharacterPlayer::ShoulderMove);
	EnhancedInputComponent->BindAction(ShoulderLookAction, ETriggerEvent::Triggered, this, &ADDCharacterPlayer::ShoulderLook); 

	GASManagerComponent->SetupGASInputComponent();
}

인풋 입력을 받기 전에 초기화해줘야 하는 값들이 있고

 

void ADDCharacterPlayer::PreInitializeComponents()
{
	Super::PreInitializeComponents();

	if (Data) 
	{
		// ENetowkSmoothingMode 때문에 Mesh Location, Rotation이 덮어써지는 문제가 있음
		// PreInitializeComponents에서 설정하면 Default Location, Rotation이 해당 값으로 설정됨
		SetData(); 
	}
}

런타임에 Mesh Position 설정하기 위해 PreInitializeComponents에서 작업해야 하는 것도 있다

 

그밖에, 어빌리티도 데이터 기반으로 분리했다