Получение старого значения (вопрос 1)
Функции UnrealEngine OnRep предоставляют предыдущее состояние реплицируемой переменной в качестве первого параметра в функции OnRep
. Так что добавьте параметр
void UAttributeSetBase::OnRep_MyAttribute(const FGameplayAttributeData& Previous)
{
const auto PreviousValue = Previous.GetCurrentValue(); // See below for possible usage.
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MyAttribute);
}
Спасибо @Dan с канала Unreal GAS.
Переслать значение делегату (вопрос 2)
Идея
Когда ваша цель - не изменять исходный код UE4, одной из возможностей является кэширование предыдущего значения в наборе атрибутов, чтобы вы могли получить к нему доступ извне.
- Кэшировать это значение для каждого атрибута в функции набора атрибутов
OnRep
.
- Используйте кэшированное значение в делегате, но только в том случае, если оно действительно . Поскольку значение присваивается в функции
OnRep
, оно не существует на сервере. Это прекрасно, так как мы хотим сохранить поведение на сервере, который использует FOnAttributeChangeData::OldValue
(имеет правильное значение только на сервере).
Пример реализации
Кэширование предыдущего значения
AttributeSetBase.h:
// Wrapper for a TMap. If you need thread safety, use another container or allocator.
class CachePreviousDataFromReplication
{
TMap<FName, FGameplayAttributeData> CachedPreviousData;
public:
void Add(const FName, const FGameplayAttributeData&);
auto Find(const FName) const -> const FGameplayAttributeData*;
};
class YOUR_API UAttributeSetBase : public UAttributeSet
{
// ...
private:
UFUNCTION() void OnRep_MyAttribute(const FGameplayAttributeData& Previous);
// ...
private:
CachePreviousDataFromReplication CachedDataFromReplication;
public:
// \param[in] AttributeName Use GET_MEMBER_NAME_CHECKED() to retrieve the name.
auto GetPreviousDataFromReplication(const FName AttributeName) const -> const FGameplayAttributeData*;
}
AttributeSetBase.cpp:
void CachePreviousDataFromReplication::Add(const FName AttributeName, const FGameplayAttributeData& AttributeData)
{
this->CachedPreviousData.Add(AttributeName, AttributeData);
}
auto CachePreviousDataFromReplication::Find(const FName AttributeName) const -> const FGameplayAttributeData*
{
return CachedPreviousData.Find(AttributeName);
}
void UAttributeSetBase::OnRep_MyAttribute(const FGameplayAttributeData& Previous)
{
CachedDataFromReplication.Add(GET_MEMBER_NAME_CHECKED(UAttributeSetBase, MyAttribute), Previous); // Add this to every OnRep function.
GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, MyAttribute);
}
auto UAttributeSetBase::GetPreviousDataFromReplication(const FName AttributeName) const -> const FGameplayAttributeData*
{
return CachedDataFromReplication.Find(AttributeName);
}
Доступ к предыдущему значению в делегате
ACharacterBase.h:
class YOUR_API ACharacterBase : public ACharacter, public IAbilitySystemInterface
{
// ...
void OnMyAttributeValueChange(const FOnAttributeChangeData& Data); // The callback to be registered within GAS.
// ...
}
ACharacterBase.cpp:
void ACharacterBase::OnMyAttributeValueChange(const FOnAttributeChangeData& Data)
{
// This delegate is fired either from
// 1. `SetBaseAttributeValueFromReplication` or from
// 2. `InternalUpdateNumericalAttribute`
// #1 is called on clients, after the attribute has changed its value. This implies,
// that the previous value is not present on the client anymore. Therefore, the
// value of `Data.OldValue` is erroneously identical to `Data.NewValue`.
// In that case (and only in that case), the previous value is retrieved from a cache
// in the AttributeSet. This cache will be only present on client, after it had
// received an update from replication.
auto deltaValue = 0.f;
if (Data.NewValue == Data.OldValue)
{
const auto attributeName = GET_MEMBER_NAME_CHECKED(UAttributeSetBase, MyAttribute);
if (auto previousData = AttributeSetComponent->GetPreviousDataFromReplication(attributeName))
{
// This will be called on the client, when coming from replication.
deltaValue = Data.NewValue - previousData->GetCurrentValue();
}
}
else
{
// This might be called on the server or clients, when coming from
// `InternalUpdateNumericalAttribute`.
deltaValue = Data.NewValue - Data.OldValue;
}
// Use deltaValue as you like.
}