StatusComponent 구조 리팩터링

- 현재 캐릭터, 로봇의 인게임 HUD 구성이 크게 다르지 않음
- 캐릭터의 기본 UI 구성에서 로봇만 가지는 UI들이 추가되는 형태
- 기본 HUD 구성에서 로봇 관련 UI 숨김 처리하기로 결정
- 이후 로봇 탑승한 경우에만 로봇 관련 UI를 보이도록 설정하여 연동하는 형태
BaseStatusComponent
헤더
#include "CoreMinimal.h"
#include "Character/DataAsset/PlayerCharacterStatus.h"
#include "Components/ActorComponent.h"
#include "BaseStatusComponent.generated.h"
DECLARE_MULTICAST_DELEGATE_OneParam(FOnHPChangedDelegate, float /*InPercentage*/)
DECLARE_MULTICAST_DELEGATE_OneParam(FOnResourceChangedDelegate, float /*InPercentage*/)
DECLARE_MULTICAST_DELEGATE(FOnDeathDelegate);
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class CR4S_API UBaseStatusComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UBaseStatusComponent();
#pragma region Get
FORCEINLINE float GetMaxHP() const { return BaseStatus.MaxHealth; }
FORCEINLINE float GetCurrentHP() const { return BaseStatus.Health; }
FORCEINLINE float GetMaxResource() const { return BaseStatus.MaxResource; }
FORCEINLINE float GetCurrentResource() const { return BaseStatus.Resource; }
FORCEINLINE float GetResourceConsumptionRate() const { return BaseStatus.ResourceConsumptionRate; }
FORCEINLINE float GetAttackPower() const { return BaseStatus.AttackPower; }
FORCEINLINE float GetArmor() const { return BaseStatus.Armor; }
FORCEINLINE float GetColdThreshold() const { return BaseStatus.ColdThreshold; }
FORCEINLINE float GetHeatThreshold() const { return BaseStatus.HeatThreshold; }
FORCEINLINE float GetHumidityThreshold() const { return BaseStatus.HumidityThreshold; }
#pragma endregion
#pragma region Add
UFUNCTION(BlueprintCallable)
void AddMaxHP(const float InAmount);
UFUNCTION(BlueprintCallable)
void AddCurrentHP(const float InAmount);
void AddMaxResource(const float InAmount);
void AddCurrentResource(const float InAmount);
void AddResourceConsumptionRate(const float InAmount);
void AddAttackPower(const float InAmount);
void AddArmor(const float InAmount);
void AddColdThreshold(const float InAmount);
void AddHeatThreshold(const float InAmount);
void AddHumidityThreshold(const float InAmount);
#pragma endregion
#pragma region Override
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,FActorComponentTickFunction* ThisTickFunction) override;
protected:
// Called when the game starts
virtual void BeginPlay() override;
#pragma endregion
#pragma region Status
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
FBaseStats BaseStatus;
#pragma endregion
#pragma region Delegate
public:
FOnHPChangedDelegate OnHPChanged;
FOnResourceChangedDelegate OnResourceChanged;
FOnDeathDelegate OnDeathState;
#pragma endregion
};
소스
#include "BaseStatusComponent.h"
// Sets default values for this component's properties
UBaseStatusComponent::UBaseStatusComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UBaseStatusComponent::BeginPlay()
{
Super::BeginPlay();
}
void UBaseStatusComponent::AddMaxHP(const float InAmount)
{
BaseStatus.MaxHealth+=InAmount;
const float Percentage=FMath::Clamp((BaseStatus.Health)/BaseStatus.MaxHealth,0.f,1.f);
OnHPChanged.Broadcast(Percentage);
}
void UBaseStatusComponent::AddMaxResource(const float InAmount)
{
BaseStatus.MaxResource+=InAmount;
const float Percentage=FMath::Clamp((BaseStatus.Resource)/BaseStatus.MaxResource,0.f,1.f);
OnResourceChanged.Broadcast(Percentage);
}
void UBaseStatusComponent::AddCurrentHP(const float InAmount)
{
const float Temp=FMath::Clamp(BaseStatus.Health+InAmount,0,BaseStatus.MaxHealth);
if (Temp <= 0)
{
BaseStatus.Health = 0;
OnDeathState.Broadcast();
}
else
{
BaseStatus.Health=Temp;
}
const float Percentage=FMath::Clamp((BaseStatus.Health)/BaseStatus.MaxHealth,0.f,1.f);
OnHPChanged.Broadcast(Percentage);
}
void UBaseStatusComponent::AddCurrentResource(const float InAmount)
{
const float Temp=FMath::Clamp(BaseStatus.Resource+InAmount,0,BaseStatus.Resource);
BaseStatus.Resource=Temp;
const float Percentage=FMath::Clamp((BaseStatus.Resource)/BaseStatus.MaxResource,0.f,1.f);
OnResourceChanged.Broadcast(Percentage);
}
void UBaseStatusComponent::AddResourceConsumptionRate(const float InAmount)
{
BaseStatus.ResourceConsumptionRate+=InAmount;
}
void UBaseStatusComponent::AddAttackPower(const float InAmount)
{
BaseStatus.AttackPower+=InAmount;
}
void UBaseStatusComponent::AddArmor(const float InAmount)
{
BaseStatus.Armor+=InAmount;
}
void UBaseStatusComponent::AddColdThreshold(const float InAmount)
{
BaseStatus.ColdThreshold+=InAmount;
}
void UBaseStatusComponent::AddHeatThreshold(const float InAmount)
{
BaseStatus.HeatThreshold+=InAmount;
}
void UBaseStatusComponent::AddHumidityThreshold(const float InAmount)
{
BaseStatus.HumidityThreshold+=InAmount;
}
// Called every frame
void UBaseStatusComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
- PlayerCharacterStatusComponent에서 관리하던 변수들 중 로봇과 공통인 변수들을 해당 클래스에서 관리
- FBaseStats 구조체에 속한 변수들은 로봇, 캐릭터의 공통 변수
- 해당 클래스에서 관리되는 변수의 델리게이트 변수도 선언
- 델리게이트와 연관된 변수값이 조정되는 경우 Broadcast 실시
PlayerCharacterStatusComponent
헤더
#include "CoreMinimal.h"
#include "BaseStatusComponent.h"
#include "Character/DataAsset/PlayerCharacterStatus.h"
#include "PlayerCharacterStatusComponent.generated.h"
DECLARE_MULTICAST_DELEGATE_OneParam(FOnHungerChangedDelegate, float /*InPercentage*/)
class UPlayerCharacterStatusAssets;
class APlayerCharacter;
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class CR4S_API UPlayerCharacterStatusComponent : public UBaseStatusComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UPlayerCharacterStatusComponent();
#pragma region Get
FORCEINLINE float GetMaxHunger() const { return PlayerStatus.MaxHunger; }
FORCEINLINE float GetCurrentHunger() const { return PlayerStatus.Hunger; }
#pragma endregion
#pragma region Add
void AddMaxHunger(const float InAmount);
void AddCurrentHunger(const float InAmount);
#pragma endregion
#pragma region Override
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,FActorComponentTickFunction* ThisTickFunction) override;
protected:
// Called when the game starts
virtual void BeginPlay() override;
#pragma endregion
#pragma region DataAsset
protected:
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="StatusData")
TObjectPtr<UPlayerCharacterStatusAsset> StatusData;
#pragma endregion
#pragma region Owner
protected:
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Owner")
TObjectPtr<APlayerCharacter> OwningCharacter;
#pragma endregion
#pragma region Status
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess=true))
FPlayerCharacterStats PlayerStatus;
#pragma endregion
#pragma region Delegate
public:
FOnHungerChangedDelegate OnHungerChanged;
#pragma endregion
};
소스
#include "PlayerCharacterStatusComponent.h"
#include "Character/Characters/PlayerCharacter.h"
// Sets default values for this component's properties
UPlayerCharacterStatusComponent::UPlayerCharacterStatusComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false;
}
// Called when the game starts
void UPlayerCharacterStatusComponent::BeginPlay()
{
Super::BeginPlay();
OwningCharacter=Cast<APlayerCharacter>(GetOwner());
if (StatusData)
{
BaseStatus=StatusData->BaseStats;
PlayerStatus=StatusData->PlayerStats;
}
}
// Called every frame
void UPlayerCharacterStatusComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UPlayerCharacterStatusComponent::AddMaxHunger(const float InAmount)
{
PlayerStatus.MaxHunger+=InAmount;
const float Percentage=FMath::Clamp((PlayerStatus.Hunger)/PlayerStatus.MaxHunger,0.f,1.f);
OnHungerChanged.Broadcast(Percentage);
}
void UPlayerCharacterStatusComponent::AddCurrentHunger(const float InAmount)
{
const float Temp=FMath::Clamp(PlayerStatus.Hunger+InAmount,0,PlayerStatus.MaxHunger);
PlayerStatus.Hunger=Temp;
const float Percentage=FMath::Clamp((PlayerStatus.Hunger)/PlayerStatus.MaxHunger,0.f,1.f);
OnHungerChanged.Broadcast(Percentage);
}
- 플레이어에서만 관리하면 되는 변수들을 추가 관리하기 위한 클래스
- BaseStatusComponent를 상속
- 배고픔 관련 변수 추가 및 UI 업데이트를 위한 델리게이트 추가
- BeginPlay 에서 데이터 에셋 값으로 구조체 변수 초기화
ModularRobotStatusComponent
헤더
#include "CoreMinimal.h"
#include "BaseStatusComponent.h"
#include "Character/DataAsset/ModularRobotStatus.h"
#include "ModularRobotStatusComponent.generated.h"
DECLARE_MULTICAST_DELEGATE_OneParam(FOnEnergyChangedDelegate, float);
DECLARE_MULTICAST_DELEGATE_OneParam(FOnStunChangedDelegate, float);
DECLARE_MULTICAST_DELEGATE_OneParam(FOnWeightChangedDelegate, float);
class AModularRobot;
class UModularRobotStatusAsset;
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class CR4S_API UModularRobotStatusComponent : public UBaseStatusComponent
{
GENERATED_BODY()
public:
UModularRobotStatusComponent();
#pragma region Get
FORCEINLINE float GetMaxEnergy() const { return RobotStatus.MaxEnergy; }
FORCEINLINE float GetCurrentEnergy() const { return RobotStatus.Energy; }
FORCEINLINE float GetEnergyConsumptionRate() const { return RobotStatus.EnergyConsumptionRate; }
FORCEINLINE float GetMaxStun() const { return RobotStatus.MaxStun; }
FORCEINLINE float GetCurrentStun() const { return RobotStatus.Stun; }
FORCEINLINE float GetStunResistance() const { return RobotStatus.StunResistance; }
FORCEINLINE float GetArmorMultiplier() const { return RobotStatus.ArmorMultiplier; }
FORCEINLINE float GetAttackPowerMultiplier() const { return RobotStatus.AttackPowerMultiplier; }
FORCEINLINE float GetMaxWeight() const { return RobotStatus.MaxWeight; }
FORCEINLINE float GetCurrentWeight() const { return RobotStatus.Weight; }
#pragma endregion
#pragma region Add
void AddMaxEnergy(const float InAmount);
UFUNCTION(BlueprintCallable)
void AddEnergy(const float InAmount);
void AddMaxStun(const float InAmount);
UFUNCTION(BlueprintCallable)
void AddStun(const float InAmount);
void AddStunResistance(const float InAmount);
void AddArmorMultiplier(const float InAmount);
void AddAttackPowerMultiplier(const float InAmount);
void AddMaxWeight(const float InAmount);
void AddWeight(const float InAmount);
#pragma endregion
#pragma region Override
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,FActorComponentTickFunction* ThisTickFunction) override;
protected:
// Called when the game starts
virtual void BeginPlay() override;
#pragma endregion
#pragma region DataAsset
protected:
UPROPERTY(EditAnywhere,BlueprintReadOnly,Category="StatusData")
TObjectPtr<UModularRobotStatusAsset> StatusData;
#pragma endregion
#pragma region Owner
protected:
UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Owner")
TObjectPtr<AModularRobot> OwningCharacter;
#pragma endregion
#pragma region Status
private:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess=true))
FModularRobotStats RobotStatus;
#pragma endregion
#pragma region Delegate
public:
FOnEnergyChangedDelegate OnEnergyChanged;
FOnStunChangedDelegate OnStunChanged;
FOnWeightChangedDelegate OnWeightChanged;
#pragma endregion
};
소스
#include "ModularRobotStatusComponent.h"
#include "Character/Characters/ModularRobot.h"
// Sets default values for this component's properties
UModularRobotStatusComponent::UModularRobotStatusComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UModularRobotStatusComponent::BeginPlay()
{
Super::BeginPlay();
OwningCharacter=Cast<AModularRobot>(GetOwner());
if (StatusData)
{
BaseStatus=StatusData->BaseStats;
RobotStatus=StatusData->RobotStats;
}
}
// Called every frame
void UModularRobotStatusComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UModularRobotStatusComponent::AddMaxEnergy(const float InAmount)
{
RobotStatus.MaxEnergy+=InAmount;
const float Percentage=FMath::Clamp(RobotStatus.Energy/RobotStatus.MaxEnergy,0.f,1.f);
OnEnergyChanged.Broadcast(Percentage);
}
void UModularRobotStatusComponent::AddEnergy(const float InAmount)
{
const float Temp=FMath::Clamp(RobotStatus.Energy+InAmount,0.f,RobotStatus.MaxEnergy);
RobotStatus.Energy=Temp;
const float Percentage=FMath::Clamp(RobotStatus.Energy/RobotStatus.MaxEnergy,0.f,1.f);
OnEnergyChanged.Broadcast(Percentage);
}
void UModularRobotStatusComponent::AddMaxStun(const float InAmount)
{
RobotStatus.MaxStun+=InAmount;
const float Percentage=FMath::Clamp(RobotStatus.Stun/RobotStatus.MaxStun,0.f,1.f);
OnStunChanged.Broadcast(Percentage);
}
void UModularRobotStatusComponent::AddStun(const float InAmount)
{
const float Temp=FMath::Clamp(RobotStatus.Stun+InAmount,0.f,RobotStatus.MaxStun);
RobotStatus.Stun=Temp;
const float Percentage=FMath::Clamp(RobotStatus.Stun/RobotStatus.MaxStun,0.f,1.f);
OnStunChanged.Broadcast(Percentage);
}
void UModularRobotStatusComponent::AddStunResistance(const float InAmount)
{
RobotStatus.StunResistance+=InAmount;
}
void UModularRobotStatusComponent::AddArmorMultiplier(const float InAmount)
{
RobotStatus.ArmorMultiplier+=InAmount;
}
void UModularRobotStatusComponent::AddAttackPowerMultiplier(const float InAmount)
{
RobotStatus.AttackPowerMultiplier+=InAmount;
}
void UModularRobotStatusComponent::AddMaxWeight(const float InAmount)
{
RobotStatus.MaxWeight+=InAmount;
const float Percentage=FMath::Clamp(RobotStatus.Weight/RobotStatus.MaxWeight,0.f,1.f);
OnWeightChanged.Broadcast(Percentage);
}
void UModularRobotStatusComponent::AddWeight(const float InAmount)
{
const float Temp=FMath::Clamp(RobotStatus.Weight+InAmount,0.f,RobotStatus.MaxWeight);
RobotStatus.Weight=Temp;
const float Percentage=FMath::Clamp(RobotStatus.Weight/RobotStatus.MaxWeight,0.f,1.f);
OnWeightChanged.Broadcast(Percentage);
}
- 로봇에서만 관리하면 되는 추가 변수들을 관리하는 클래스
- BaseStatusComponent 상속
- UI 업데이트가 필요한 변수들은 델리게이트 추가
- BeginPlay 에서 데이터 에셋 값으로 구조체 변수 초기화
위젯 클래스 관련 변경
void UCharacterStatusWidget::ToggleWidgetMode(const bool bIsRobot)
{
if (Energy)
{
Energy->SetVisibility((bIsRobot
? ESlateVisibility::Visible
: ESlateVisibility::Hidden));
}
if (Stun)
{
Stun->SetVisibility((bIsRobot
? ESlateVisibility::Visible
: ESlateVisibility::Hidden));
}
if (Resource)
{
Resource->SetFillColorAndOpacity(bIsRobot
? FColor::Red
: FColor::Yellow);
}
}
- 로봇, 캐릭터 빙의 교체 시점에 호출되는 함수
- 로봇에 탑승하게 되면 Energy, Stun에 해당하는 UI 위젯들을 보이도록 처리
- 반대로 캐릭터로 돌아오면 위 2개 UI는 Hidden으로 설정하여 보이지 않게 처리
- 추가적으로 자원 관련 변수는 둘다 보이지만 교체 시점에 알맞은 색으로 변경
참고자료