Replicated

** [Drag Down] UI 자체 Local Prediction 구현 ** 본문

언리얼 엔진/Drag Down (캡스톤 디자인)

** [Drag Down] UI 자체 Local Prediction 구현 **

라구넹 2025. 4. 5. 18:43

진짜 값을, 그것도 주기적으로 변하는 걸 로컬 프레딕션 하려고 했는데, 너무 위험하다.

보이는 값이 프레딕션 되도록 처리해보자!

 

// 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();
protected:
	virtual void OnStaminaChanged(const FOnAttributeChangeData& ChangeData);
	virtual void OnMaxStaminaChanged(const FOnAttributeChangeData& ChangeData);

protected:
	UPROPERTY(meta = (BindWidget))
	TObjectPtr<class UProgressBar> PbStaminaBar;

	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();
			}
			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()
{
	if (PbStaminaBar)
	{
		if ( !Owner->HasAuthority() )
		{
			UE_LOG(LogDD, Log, TEXT("[NetMode %d] UpdateEnergyBar : %f / %f"), GetWorld()->GetNetMode(), CurrentStamina, CurrentMaxStamina);

		}
		PbStaminaBar->SetPercent(CurrentStamina / CurrentMaxStamina);
	}
}

void UDDGASStaminaBarUserWidget::OnStaminaChanged(const FOnAttributeChangeData& ChangeData)
{
	//UE_LOG(LogDD, Log, TEXT("[NetMode %d]OnEnergyChanged"), GetWorld()->GetNetMode());
	CurrentStamina = ChangeData.NewValue;
	UpdateStaminaBar();
}

void UDDGASStaminaBarUserWidget::OnMaxStaminaChanged(const FOnAttributeChangeData& ChangeData)
{
	CurrentMaxStamina = ChangeData.NewValue;
	UpdateStaminaBar();
}

void UDDGASStaminaBarUserWidget::PredictStaminaUI()
{
	CurrentStamina += PredictionDeltaValue;
	UpdateStaminaBar();
}

 

이런식으로 내부 CurrentStamina를 조절한다

 

 

일단 테스트용으로 0.5초에 한 번 작동하도록 했다

이제 다음은 UI가 보간되어 부드럽게 움직이도록 할 것이다