구현내용
플레이어의 경험치 와 레벨업 시스템을 구현하였습니다
사용한 클래스
| StatComponent | 플레이어의 스탯관련부분을 담당하는 커스텀 컴포넌트 |
구현
C++구현
필요한 .h 선언
UPROPERTY()
float currentExp = 0.f;
UPROPERTY()
float expNextLevel = 100.f;
UPROPERTY(EditAnywhere, Category="Level")
int32 maxLevel = 30;
UFUNCTION()
void LevelUp();
UFUNCTION()
void CalcExpNextLev();
UFUNCTION()
void AddExp(float amount);
UFUNCTION()
void MaxLevel();
경험치가 소수로 나오면 이상하니 내림하여 정수로 만들고 레벨이 올라갈수록 (1.2% -> 20%) 증가한
레벨이 올라갈수록 경험치가 곱셈으로 점점 커지는 구조를 만들어주었습니다
void UStatComponent::CalcExpNextLev()
{
float baseExp = 100.f;
float growRate = 1.2f;
expNextLevel = FMath::Floor(baseExp * FMath::Pow(growRate, float(level - 1)));
}
매개변수값 amount만큼 경험치를 더합니다
또한 동일하게 소수점을 버리고 정수형으로 반환하며 max레벨이 될떄까지 반복합니다
void UStatComponent::AddExp(float amount)
{
currentExp = FMath::FloorToFloat(currentExp + amount);
while (currentExp >= expNextLevel && level < maxLevel)
{
currentExp -= expNextLevel;
LevelUp();
}
if (level >= 30)
MaxLevel();
if (onExpChanged.IsBound())
onExpChanged.Broadcast(currentExp, expNextLevel, level);
}
캐릭터의 레벨업 함수입니다
레벨을 1 증가시키며 레벨에 따른 스탯을 적용합니다
void UStatComponent::LevelUp()
{
level++;
ApplyLevelStat();
TriggerBlink();
GetLevelUpPoints();
//레벨 경험치 공식 -> 1.2배
CalcExpNextLev();
//////////////////////////
currentExp = FMath::FloorToFloat(currentExp);
//브로드캐스팅
onExpChanged.Broadcast(currentExp, expNextLevel, level);
onCenterNotiChanged.Broadcast(level, "", "");
onLevelChanged.Broadcast(level);
onAbilityPointChanged.Broadcast(currentAbilityPoint, totalAbilityPoint);
onStatPointChanged.Broadcast(currentStatPoint, totalStatPoint);
onAttackPowerChanged.Broadcast(stats[EBaseStatType::AttackPower].EntryTotal(), stats[EBaseStatType::AttackPower].EntryTotal());
RestoreHpPosture();
}
레벨의 짝수와 홀수에 따라서 지급되는 스탯포인트와 어빌리티포인트를 다르게 계산해주었습니다
void UStatComponent::GetLevelUpPoints()
{
if (level <= 0)
return;
bool bIsEven = (level % 2 == 0); //짝
int32 statGain = 0;
int32 abilityGain = 0;
if (bIsEven)
{
statGain = 3;
abilityGain = 2;
}
else
{
statGain = 2;
abilityGain = 1;
}
AddStatPoint(statGain);
AddAbilityPoint(abilityGain);
//ui갱신 예약
UpdateStatPointUI();
UpdateAbilityPointUI();
}
위젯
캐릭터 메인 위젯의 센터위쪽을 담당할 노티위젯을 만들어주었습니다

메인위젯의 모습

위젯c++
UPROPERTY(meta = (BindWidget))
class UOverlay* Overlay_Base;
UPROPERTY(meta = (BindWidget))
class UTextBlock* Text_Top;
UPROPERTY(meta = (BindWidget))
class UTextBlock* Text_Bottom;
UPROPERTY(meta = (BindWidgetAnim), Transient)
class UWidgetAnimation* FadeLevel;
UPROPERTY(meta = (BindWidgetAnim), Transient)
class UWidgetAnimation* FadeHighLight;
UFUNCTION()
void UpdateCenterWidget(int32 currentLevel = -1, const FString& mapExplantion = "", const FString& mapName = "");
UFUNCTION()
void OnLevelAnimFinished_Internal();
bool bIsFadingOut = false;
애니메이션 순서를 FadeLevel -> FadeHighLight -> FadeLevel(리버스) 로 설정하였습니다
델리게이트를 사용하여 순차적으로 실행되도록 구현하였습니다
void UCenterNotiWidget::NativeConstruct()
{
Super::NativeConstruct();
Overlay_Base->SetVisibility(ESlateVisibility::Collapsed);
FWidgetAnimationDynamicEvent finishFadeEv;
finishFadeEv.BindDynamic(this, &UCenterNotiWidget::OnLevelAnimFinished_Internal);
BindToAnimationFinished(FadeLevel, finishFadeEv);
BindToAnimationFinished(FadeHighLight, finishFadeEv);
}
//애니메이션순서 FadeLevel -> FadeHighLight -> FadeLevel(Reverse)
void UCenterNotiWidget::UpdateCenterWidget(int32 currentLevel, const FString& mapExplantion, const FString& mapName)
{
Overlay_Base->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
//레벨용
if (currentLevel)
{
if (Text_Top)
Text_Top->SetText(FText::FromString(TEXT("Level Up!")));
if (Text_Bottom)
{
FString levelText = FString::Printf(TEXT("Lv . %d"), currentLevel);
Text_Bottom->SetText(FText::FromString(levelText));
}
bIsFadingOut = false;
PlayAnimation(FadeLevel, 0.f, 1, EUMGSequencePlayMode::Forward, 3.f);
return;
}
//맵용
if (!mapExplantion.IsEmpty() && !mapName.IsEmpty())
{
if(Text_Top)
Text_Top->SetText(FText::FromString(mapExplantion));
if (Text_Bottom)
Text_Bottom->SetText(FText::FromString(mapName));
}
}
void UCenterNotiWidget::OnLevelAnimFinished_Internal()
{
if (!bIsFadingOut && GetAnimationCurrentTime(FadeLevel) >= FadeLevel->GetEndTime())
{
bIsFadingOut = true;
PlayAnimation(FadeHighLight, 0.f, 1, EUMGSequencePlayMode::Forward, 3.f);
}
else if (bIsFadingOut && GetAnimationCurrentTime(FadeHighLight) >= FadeHighLight->GetEndTime())
{
PlayAnimation(FadeLevel, 0.f, 1, EUMGSequencePlayMode::Reverse, 3.f);
}
else if (bIsFadingOut && GetAnimationCurrentTime(FadeLevel) <= 0.f)
{
Overlay_Base->SetVisibility(ESlateVisibility::Collapsed);
bIsFadingOut = false;
}
}
결과
좌측하단의 주황색 바 가 경험치 Exp와 바인드된 위젯입니다


'Unreal 프로젝트 다이어리 > 두번째 프로젝트' 카테고리의 다른 글
| Unreal - 스탯창 (0) | 2025.12.02 |
|---|---|
| Unreal - 세이브 게임 (0) | 2025.12.02 |
| Unreal - 데미지 오버레이 (0) | 2025.12.01 |
| Unreal - 인벤토리(5-2) (UI 디테일 추가) (0) | 2025.11.29 |
| Unreal - 인벤토리(5) ( 아이템 정보 ) (0) | 2025.11.28 |