Реализуйте универсальную систему перезарядки способностей на C++ для Unreal Engine.
Реализуйте универсальный компонент перезарядки способностей с ключом по id способности. Он должен сообщать, готова ли способность, расходовать использование при срабатывании и быть устойчивым к переменному FPS без покадрового счётчика.
Требования:
(GetWorld()->GetTimeSeconds()) с длительностью перезарядки — не отсчитывайте в Tick и не считайте кадры.
записывает метку и возвращает true.
- Гейтите готовность сравнением меток времени мира
- Способность, ни разу не использованная, готова (нет записанной метки ⇒ разрешено).
TryUseвозвращаетfalseи ничего не меняет, если ещё на перезарядке; иначе
Допишите две функции-члены (LastUsedTime — это TMap<FName, float>):
bool UCooldownComponent::IsReady(FName AbilityId, float CooldownSeconds) const
{
// ваш код здесь
}
bool UCooldownComponent::TryUse(FName AbilityId, float CooldownSeconds)
{
// ваш код здесь
}
Допишите реализацию.
Запишите время мира при срабатывании способности, затем гейтите следующее использование, сравнивая прошедшее время с длительностью перезарядки. Используйте GetWorld()->GetTimeSeconds() для метки — сравнение времён избегает счётчика в Tick и устойчиво к переменному FPS.
- ✗Отсчитывать в
Tickвместо сравнения меток — дрейфует с частотой кадров - ✗Использовать кадры вместо секунд — ломается при переменном FPS
- ✗Забывать, что первое использование должно проходить, пока метка ещё не записана
- →Как бы вы предоставили оставшееся время перезарядки для UI с радиальной заливкой?
- →Как бы вы приостанавливали перезарядки, когда игра на паузе?
Универсальная система перезарядки
// CooldownComponent.h
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class UCooldownComponent : public UActorComponent
{
GENERATED_BODY()
public:
// true, если способность готова прямо сейчас
UFUNCTION(BlueprintCallable, Category="Cooldown")
bool IsReady(FName AbilityId, float CooldownSeconds) const;
// вызывается при срабатывании способности — запускает перезарядку
UFUNCTION(BlueprintCallable, Category="Cooldown")
bool TryUse(FName AbilityId, float CooldownSeconds);
// оставшееся время для UI (0, если готово)
UFUNCTION(BlueprintCallable, Category="Cooldown")
float GetRemaining(FName AbilityId, float CooldownSeconds) const;
private:
// метка времени мира последнего использования каждой способности
TMap<FName, float> LastUsedTime;
};
// CooldownComponent.cpp
bool UCooldownComponent::IsReady(FName AbilityId, float CooldownSeconds) const
{
const float* Last = LastUsedTime.Find(AbilityId);
if (!Last)
{
return true; // никогда не использовалась — готова
}
const float Now = GetWorld()->GetTimeSeconds();
return (Now - *Last) >= CooldownSeconds;
}
bool UCooldownComponent::TryUse(FName AbilityId, float CooldownSeconds)
{
if (!IsReady(AbilityId, CooldownSeconds))
{
return false; // ещё на перезарядке — использование отклонено
}
LastUsedTime.Add(AbilityId, GetWorld()->GetTimeSeconds());
return true;
}
float UCooldownComponent::GetRemaining(FName AbilityId, float CooldownSeconds) const
{
const float* Last = LastUsedTime.Find(AbilityId);
if (!Last)
{
return 0.f;
}
const float Elapsed = GetWorld()->GetTimeSeconds() - *Last;
return FMath::Max(0.f, CooldownSeconds - Elapsed);
}
Система не зависит от Tick: она сравнивает метки времени мира, поэтому работает одинаково при любом FPS и легко расширяется на любое число способностей через FName-ключи.