스킬의 쿨타임을 관리하는 컴포넌트입니다. 쿨다운이 진행중인 스킬들을 Map<SkillTag, CooldownData>형태로 갖고있고, Tick()함수를 통해 해당 스킬들의 쿨타임을 줄이는 방식을 활용했습니다.
유저가 단축키를 누르면 SkillSystemComponent에서 (1) 을 통해 해당 스킬의 쿨타임을 확인합니다.
만약 쿨타임이 진행중이지 않으면 (2) 를 통해 스킬을 활성화 하고 해당 스킬의 쿨타임이 시작됩니다.
void USHCooldownSystemComponent::StartCooldown(const FGameplayTag& InSkillTag, float Duration)
{
if (!ActiveCooldowns.Contains(InSkillTag))
{
ActiveCooldowns.Add(InSkillTag, FCooldownData(Duration));
}
ActiveCooldowns[InSkillTag].CooldownDuration = Duration;
ActiveCooldowns[InSkillTag].TimeRemaining = Duration;
}
SkillSystemComponent에 의해서 스킬이 활성화 되고 StartCooldown함수를 호출하게 되면 ActiveCooldowns에 있는지 확인 후 리스트에 없으면 ActiveCooldowns에 추가를 해줍니다.
쿨타임이 진행중인 스킬은 들어올 수 없지만 만약을 대비해 쿨다운이 진행중인 스킬이 들어오게 되면 초기화 되도록 설정했습니다.
void USHCooldownSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
TArray<FGameplayTag> CooldownsToRemove;
for (auto& Elem : ActiveCooldowns)
{
FCooldownData& CooldownData = Elem.Value;
if (CooldownData.TimeRemaining > 0.f)
{
CooldownData.TimeRemaining -= DeltaTime;
if (CooldownData.TimeRemaining <= 0.f)
{
CooldownData.TimeRemaining = 0.f;
CooldownsToRemove.Add(Elem.Key);
}
}
}
if (ActiveCooldowns.Num() == 0)
return;
for (const FGameplayTag& SkillTag : CooldownsToRemove)
{
ActiveCooldowns.Remove(SkillTag);
}
if (OnTick.IsBound() == true)
OnTick.Broadcast();
}
CooldownSystem은 Tick()함수를 돌며 쿨다운이 진행중인 스킬들의 남은 쿨다임 시간을 계산합니다. 인자로 넘어오는 DeltaTime은 직전에 호출된 Tick()함수와 현재 호출된 Tick()함수사이의 경과 시간을 나타냅니다. 따라서 ActiveCooldowns을 반복문을 통해 돌면서 각 스킬의 남은 쿨타임에서 DeltaTime만큼 감소 시켜줍니다. TimeRemaining <= 0이 되면 해당 스킬은 쿨타임이 다 돌았기 때문에 List에서 제거해 줍니다.
OnTick delegator로 HUD의 SkillBotBarWidget에 스킬들의 쿨타임 데이터을 넘겨주고, SkillHotBarWidget은 해당 데이터를 통해 ProgressBar의 Persent를 설정합니다.
전체 코드
***************************
SHCooldownSystemComponent.h
***************************
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SHCooldownSystemComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDOnTick);
USTRUCT(BlueprintType)
struct SHPROJECT_API FCooldownData
{
GENERATED_BODY()
public:
FCooldownData()
: CooldownDuration(0.f), TimeRemaining(0.f) {}
FCooldownData(float InCooldownDuration)
: CooldownDuration(InCooldownDuration), TimeRemaining(0.f) {}
public:
UPROPERTY(BlueprintReadOnly)
float CooldownDuration;
UPROPERTY(BlueprintReadOnly)
float TimeRemaining;
};
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SHPROJECT_API USHCooldownSystemComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
USHCooldownSystemComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
public:
void StartCooldown(const FGameplayTag& InSkillTag, float Duration);
bool IsOnCooldown(const FGameplayTag& InSkillTag) const;
float GetCooldownRemainingBySkillTag(const FGameplayTag& InSkillTag) const;
float GetCooldownRemainingPercentBySkillTag(const FGameplayTag& InSkillTag) const;
public:
FDOnTick OnTick;
private:
UPROPERTY()
TMap<FGameplayTag, FCooldownData> ActiveCooldowns;
};
*****************************
SHCooldownSystemComponent.cpp
*****************************
// Fill out your copyright notice in the Description page of Project Settings.
#include "SkillSystem/SHCooldownSystemComponent.h"
// Sets default values for this component's properties
USHCooldownSystemComponent::USHCooldownSystemComponent()
{
// 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 USHCooldownSystemComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void USHCooldownSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
TArray<FGameplayTag> CooldownsToRemove;
for (auto& Elem : ActiveCooldowns)
{
FCooldownData& CooldownData = Elem.Value;
if (CooldownData.TimeRemaining > 0.f)
{
CooldownData.TimeRemaining -= DeltaTime;
if (CooldownData.TimeRemaining <= 0.f)
{
CooldownData.TimeRemaining = 0.f;
CooldownsToRemove.Add(Elem.Key);
}
}
}
if (ActiveCooldowns.Num() == 0)
return;
for (const FGameplayTag& SkillTag : CooldownsToRemove)
{
ActiveCooldowns.Remove(SkillTag);
}
if (OnTick.IsBound() == true)
OnTick.Broadcast();
}
void USHCooldownSystemComponent::StartCooldown(const FGameplayTag& InSkillTag, float Duration)
{
if (!ActiveCooldowns.Contains(InSkillTag))
{
ActiveCooldowns.Add(InSkillTag, FCooldownData(Duration));
}
ActiveCooldowns[InSkillTag].CooldownDuration = Duration;
ActiveCooldowns[InSkillTag].TimeRemaining = Duration;
}
bool USHCooldownSystemComponent::IsOnCooldown(const FGameplayTag& InSkillTag) const
{
if (GetCooldownRemainingBySkillTag(InSkillTag) > 0.f)
return true;
return false;
}
float USHCooldownSystemComponent::GetCooldownRemainingBySkillTag(const FGameplayTag& InSkillTag) const
{
if (const FCooldownData* CooldownData = ActiveCooldowns.Find(InSkillTag))
{
return CooldownData->TimeRemaining;
}
return 0.0f;
}
float USHCooldownSystemComponent::GetCooldownRemainingPercentBySkillTag(const FGameplayTag& InSkillTag) const
{
if (const FCooldownData* CooldownData = ActiveCooldowns.Find(InSkillTag))
{
return CooldownData->TimeRemaining / CooldownData->CooldownDuration;
}
return 0.f;
}
'Unreal Project' 카테고리의 다른 글
GAS를 활용한 콤보 공격 (0) | 2025.01.23 |
---|---|
FloatingDamageWidget (0) | 2024.10.19 |
GameInstance 및 캐릭터 정보 저장 (0) | 2024.09.23 |
Control Rig을 활용한 캐릭터 다리 조절 (1) | 2024.09.23 |
Material을 활용한 캐릭터 Outline 그리기 (1) | 2024.08.13 |