구현이유
플레이어의 기본 스탯(Base Stat), 버프/디버프, 힐(회복), 피해 처리(GetDamage) 등
캐릭터 능력치와 관련된 모든 기능을 하나의 모듈로 통합하고 재사용이 가능하도록 하기 위해
플레이어에 StatComponent를 별도로 붙여서 관리하도록 설계했다.
구현방식
플레이어의 기본 스탯(BaseStat) 을 데이터테이블에서 불러와 레벨에 따라 계산하고,
버프·디버프(EntryBonus, EntryRate) 및 데미지/회복/체간게이지(Posture) 처리
로직을 하나의 StatComponent에 모듈화했으며,
데이터 테이블에서 가져온 기본 스탯을 ApplyLevelStat 함수로 레벨 성장 공식에 따라
재계산해 entryBase에 설정하고 각 스탯은
EntryTotal() = base * ( 1 + rate ) + bonus 공식을 통해 버프 / 디버프 가 반영된 최종값을 산출
합연산과 곱연산을 구축하였다
또한 플레이어가 데미지를 받을때의 GetDamage함수를 직접만들기위해
"가드" 라는시스템이 존재하기때문에 가드 상황에 맞게 체간게이지, 받는데미지를 다르게 계산해주었다.
StatComponent는 TMap 기반의 스탯 저장 구조와
레벨 성장·버프/디버프·가드 처리 알고리즘을 조합한
모듈형 스탯 시스템으로 설계하였습니다
UENUM(BlueprintType)
enum class EGuardType : uint8
{
None UMETA(DisplayName = "None"),
Normal UMETA(DisplayName = "Normal"),
Perfect UMETA(DisplayName = "Perfect"),
};
struct FGuardModifier
{
float damageRate;
float postureRate;
};
static const TMap<EGuardType, FGuardModifier> guard_Modifiers =
{
{EGuardType::None, {1.f, 0.1f}},
{EGuardType::Normal, {0.5f, 0.5f}},
{EGuardType::Perfect,{0.f, 0.05f}}
};
UENUM(BlueprintType)
enum class EBaseStatType : uint8
{
Hp,
Posture,
AttackPower,
PostureRecoverySpeed
};
//엔트리 계산식 : base + bonus + rate
USTRUCT(BlueprintType)
struct FStatEntry
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
float entryBase = 0.f;
UPROPERTY(VisibleAnywhere)
float entryBonus = 0.f;
UPROPERTY(VisibleAnywhere)
float entryRate = 0.f;
float EntryTotal() const
{
return (entryBase * (1.f + entryRate)) + entryBonus;
}
};
데이터테이블에서 기본 스탯을 불러와 레벨 성장 공식을 적용해 EntryBase 값을 설정하고 현재 Hp/Posture을 초기화하며
UpdateCurrentStats에서 현재 Hp/Posture를 최종 스탯(EntryTotal)기준으로 클램핑하여
변화가 있으면 델리게이트로 UI에 갱신을 전달
void UStatComponent::ApplyLevelStat()
{
float hpGrowthRate = 50.f;
float attackGrowthRate = 5.f;
float postureRecoveryGrowthRate = 0.5f;
float exponent = 1.1f;
stats.FindOrAdd(EBaseStatType::Hp).entryBase = CalculateLevelStat(cachedBaseStat.baseHp, level, hpGrowthRate, exponent);
stats.FindOrAdd(EBaseStatType::AttackPower).entryBase =CalculateLevelStat(cachedBaseStat.baseAttackPower, level, attackGrowthRate, exponent);
// Posture는 기본값, RecoverySpeed는 선형 증가
stats.FindOrAdd(EBaseStatType::Posture).entryBase = cachedBaseStat.basePosture;
stats.FindOrAdd(EBaseStatType::PostureRecoverySpeed).entryBase = cachedBaseStat.basePostureRecoverySpeed + postureRecoveryGrowthRate * (level - 1);
currentHp = stats[EBaseStatType::Hp].EntryTotal();
currentPosture = 0.f;
UpdateCurrentStats();
}
void UStatComponent::InitStats()
{
if (!characterBaseTable)
return;
const FString ContextString(TEXT("Character Base Stats"));
FCharacterBaseStat* baseStat = characterBaseTable->FindRow<FCharacterBaseStat>(FName("BaseStat"), ContextString);
cachedBaseStat = *baseStat;
ApplyLevelStat();
}
void UStatComponent::UpdateCurrentStats()
{
if (stats.Contains(EBaseStatType::Hp))
currentHp = FMath::Clamp(currentHp, 0.f, stats[EBaseStatType::Hp].EntryTotal());
// Posture
if (stats.Contains(EBaseStatType::Posture))
currentPosture = FMath::Clamp(currentPosture, 0.f, stats[EBaseStatType::Posture].EntryTotal());
if (onHpChanged.IsBound())
onHpChanged.Broadcast(currentHp, stats[EBaseStatType::Hp].EntryTotal());
if (onPostureChanged.IsBound())
onPostureChanged.Broadcast(currentPosture, stats[EBaseStatType::Posture].EntryTotal());
}
구현한 합연산 및 곱연산입니다
//---------------------------buff----------------------
//고정값 + 계산식
void UStatComponent::AddEntryBonus(EBaseStatType statType, float amount)
{
if (stats.Contains(statType))
{
stats[statType].entryBonus += amount;
UpdateCurrentStats();
}
}
//퍼센트 보너스 비율 증가 계산식
void UStatComponent::AddEntryRate(EBaseStatType statType, float rateAmount)
{
if (stats.Contains(statType))
{
stats[statType].entryRate += rateAmount;
UpdateCurrentStats();
}
}
구현한 데미지를 받는 함수입니다
피격시 체력과 체간게이지를 감소.증가 계산하는 함수로
가드종류에 따라 미리 정의된 데미지비율 / 자세 증가 비율을 적용해 체력을 깎고 체간은 증가시킵니다
void UStatComponent::ApplyGetDamage(float damageAmount, EGuardType guardType)
{
const FGuardModifier* guardModifier = guard_Modifiers.Find(guardType);
currentHp -= damageAmount * guardModifier->damageRate;
currentPosture += damageAmount * guardModifier->postureRate;
UpdateCurrentStats();
}
캐릭터 Base스탯값을 가지고있는 데이터테이블입니다
Level1기준으로 플레이어가 가진 기본 스탯 값들로 이루어져있으며 시작시 이와같은 값들로 초기화됩니다

구조를 보기쉽게 다이어그램으로 변환해봤습니다

'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글
| Unreal - 인벤토리(1) ( 크기변경하기, 창옮기기 ) (2) | 2025.11.22 |
|---|---|
| Unreal - UI (HpWidget, PostureWidget) (0) | 2025.11.21 |
| Unreal - 상호작용(사다리 타기) (0) | 2025.11.15 |
| Unreal - 상호작용(승강기) (0) | 2025.11.12 |
| Unreal - 상호작용(Edge Traversal) (0) | 2025.11.09 |