언리얼 엔진/Drag Down
[Drag Down] Stamina UI Interpolation
라구넹
2025. 4. 6. 19:55
이것도 UX 향상이다
너무 값이 딱딱 떨어지게 바뀌면 사용자가 보기에 불편하다
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UI/DDGASUserWidget.h"
#include "GameplayEffectTypes.h"
#include "DDGASStaminaBarUserWidget.generated.h"
/**
*
*/
UCLASS()
class DRAGDOWN_API UDDGASStaminaBarUserWidget : public UDDGASUserWidget
{
GENERATED_BODY()
public:
UDDGASStaminaBarUserWidget();
virtual void SetAbilitySystemComponent(AActor* InOwner) override;
void UpdateStaminaBar(float StaminaToDisplay);
protected:
virtual void OnStaminaChanged(const FOnAttributeChangeData& ChangeData);
virtual void OnMaxStaminaChanged(const FOnAttributeChangeData& ChangeData);
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
protected:
UPROPERTY(meta = (BindWidget))
TObjectPtr<class UProgressBar> PbStaminaBar;
UPROPERTY(EditAnywhere, Category = "UI|Stamina")
float InterpSpeed = 5.0f;
float InterpolatedStamina = 0.0f;
float CurrentStamina = 0.0f;
float CurrentMaxStamina = 0.1f;
// Preidction
private:
void PredictStaminaUI();
float PredictionDeltaValue;
float PredictionPeriod;
FTimerHandle StaminaPredictionHandle;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "UI/DDGASStaminaBarUserWidget.h"
#include "AbilitySystemComponent.h"
#include "Attribute/DDAttributeSet.h"
#include "Components/ProgressBar.h"
#include "DragDown.h"
UDDGASStaminaBarUserWidget::UDDGASStaminaBarUserWidget()
{
PredictionDeltaValue = 5.0f;
PredictionPeriod = 0.5f;
}
void UDDGASStaminaBarUserWidget::SetAbilitySystemComponent(AActor* InOwner)
{
Super::SetAbilitySystemComponent(InOwner);
if (ASC)
{
ASC->GetGameplayAttributeValueChangeDelegate(UDDAttributeSet::GetStaminaAttribute()).AddUObject(this, &UDDGASStaminaBarUserWidget::OnStaminaChanged);
ASC->GetGameplayAttributeValueChangeDelegate(UDDAttributeSet::GetMaxStaminaAttribute()).AddUObject(this, &UDDGASStaminaBarUserWidget::OnMaxStaminaChanged);
//UE_LOG(LogCS, Log, TEXT("[NetMode : %d] SetAbilitySystemComponent"), GetWorld()->GetNetMode());
const UDDAttributeSet* CurrentAttributeSet = ASC->GetSet<UDDAttributeSet>();
if (CurrentAttributeSet)
{
CurrentStamina = CurrentAttributeSet->GetStamina();
CurrentMaxStamina = CurrentAttributeSet->GetMaxStamina();
if (CurrentMaxStamina > 0.0f)
{
UpdateStaminaBar(CurrentStamina);
}
else
{
UE_LOG(LogDD, Warning, TEXT("CurrentMaxEnergy is 0"));
}
}
else
{
UE_LOG(LogDD, Warning, TEXT("CurrentAttributeSet is null!"));
}
}
else
{
UE_LOG(LogDD, Warning, TEXT("ASC is null! Ensure that the Ability System Component is properly initialized before calling this function."));
}
if ( !Owner->HasAuthority() )
{
GetWorld()->GetTimerManager().SetTimer(StaminaPredictionHandle, this, &UDDGASStaminaBarUserWidget::PredictStaminaUI,
PredictionPeriod, true, 0.0f);
}
}
void UDDGASStaminaBarUserWidget::UpdateStaminaBar(float StaminaToDisplay)
{
if (PbStaminaBar)
{
PbStaminaBar->SetPercent(StaminaToDisplay / CurrentMaxStamina);
}
}
void UDDGASStaminaBarUserWidget::OnStaminaChanged(const FOnAttributeChangeData& ChangeData)
{
if (CurrentStamina > ChangeData.NewValue)
{
InterpolatedStamina = ChangeData.NewValue;
UpdateStaminaBar(InterpolatedStamina);
}
CurrentStamina = ChangeData.NewValue;
}
void UDDGASStaminaBarUserWidget::OnMaxStaminaChanged(const FOnAttributeChangeData& ChangeData)
{
CurrentMaxStamina = ChangeData.NewValue;
}
void UDDGASStaminaBarUserWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
Super::NativeTick(MyGeometry, InDeltaTime);
if (!FMath::IsNearlyEqual(InterpolatedStamina, CurrentStamina, 0.01f))
{
InterpolatedStamina = FMath::FInterpTo(InterpolatedStamina, CurrentStamina, InDeltaTime, InterpSpeed);
UpdateStaminaBar(InterpolatedStamina);
}
}
void UDDGASStaminaBarUserWidget::PredictStaminaUI()
{
CurrentStamina += PredictionDeltaValue;
}
Tick에서 FInterpTo를 사용해서 보간 처리하고 해당 값으로 업데이트한다
Tick에서 처리하는 걸 좋아하진 않는데, Tick에서 처리하는게 제일 부드럽다
이거 하나로 성능에 무리가 가진 않을 거라 그냥 Tick에서 처리한다.
void UDDGASStaminaBarUserWidget::OnStaminaChanged(const FOnAttributeChangeData& ChangeData)
{
if (CurrentStamina > ChangeData.NewValue)
{
InterpolatedStamina = ChangeData.NewValue;
UpdateStaminaBar(InterpolatedStamina);
}
CurrentStamina = ChangeData.NewValue;
}
근데 깎이는 건 바로 줄어드는게 보기가 좋은 것 같아 깎이는 건 바로 깎이도록 처리한다.