GameplayBuffSystem框架
设计
首先我们需要一个 BuffManager
,管理所有的 BuffComponent
。
(实现的时候,可以让 BuffManager
作为一个 GameStateComponent
;BuffComponent
作为一个 PlayerStateComponent
)
对于每个 Player
,需要一个 BuffComponent
管理该 Player
身上持有的 Buff
;
对于 Buff
,需要一个 BuffBase
作为 Buff
的基类;
classDiagram
UGameplayBuffManager..>FGameplayBuffSetting
UGameplayBuffManager..>UGameplayBuffComponent
UGameplayBuffManager..>FGameplayBuffParams
class UGameplayBuffManager {
BuffSettings : TMap~int|FGameplayBuffSetting~
+CommitBuff(Target, BuffID, Params)
+QueryBuff(Target, BuffID)
+CombineBuffParams(Target, BuffID, Params)
+RemoveBuff(Target, BuffID, bRemoveImmediately)
+RemoveBuff(Target, BuffHandle, bRemoveImmediately)
+ClearBuff(Target)
-Tick()
-CreateBuff(Target, BuffID, Params)
}
UGameplayBuffComponent..>UGameplayBuffBase
class UGameplayBuffComponent {
BuffGroup : TArray~UGameplayBuffBase*~
BuffIDArray : int (For Sync)
AddBuff(InBuff)
RemoveBuff(InBuff, bRemoveImmediately)
}
class FGameplayBuffSetting {
BuffName : FString
bNeedToMerge : bool
BuffAsset : TSubclassOf~UGameplayBuffBase~
}
class FGameplayBuffParams {
# ValueMap : TMap~FString, FVariant~ ValueMap
FGameplayBuffParams(std::initializer_list ~TPairInitializer[const FString&, FVariant]~ ValuePairs)
SetValue(const FString& FieldName, FVariant Value)
Contains(const FString& FieldName)
IsEmpty()
Merge(const FGameplayBuffParams& OtherParams)
GetValueMap() const
operator+(const FGameplayBuffParams& OtherParams)
}
class FGameplayCountDownData {
StartTime
TotalTime
LeftTime
IsPause
SpeedFactor
}
UGameplayBuffBase..>FGameplayBuffParams
class UGameplayBuffBase {
Owner : TWeakObjectPtr~UGameBuffComponent~
BuffID : int
BuffName : FString
BuffParams : FGameplayBuffParams
+Create(Owner, BuffID, Params)
+Remove()
+Merge(BuffHandle, Params)
+CombineParams(Params)
+Tick(DeltaTime)
+CheckNeetToStop()
+SetNeedToRemove(bEnable)
#OnCreate()
#OnRemove()
#OnMerge()
#OnTick()
}
UGameplayBuffBase<|--UGameplayBuff_TimeDuration
UGameplayBuff_TimeDuration..>FGameplayCountDownData
class UGameplayBuff_TimeDuration {
# TimeDuration : float
# PassDuration : float
# TickInternal : float
# LastTickTime : float
- SpeedFactor : float
- bInPause : false
- PauseReasons : TArray~FString~
# Tick(DeltaTime)
+ Refresh(InPassDuration, InTimeDuration)
+ BeginPauseTime(Reason)
+ StopPauseTime(Reason)
+ UpdateSpeedFactor(InFactor)
+ GetTimeDuration()
+ GetLeftTime()
+ GetCountDownData()
# OnBeginPauseTime()
# OnStopPauseTime()
# OnUpdateSpeedFactor()
# OnRefresh()
# OnTimeStateChanged()
}
UGameplayBuffUtils..>UGameplayBuffManager
BuffManager
Commit
:判断 BuffSettings
里是否有对应 BuffID
的 Buff
,若存在则创建该实例;
判断一下 BuffComponent
里是否原本已经有相同 BuffID
的 Buff
,若存在,并且该 Buff
需要 Merge
的话,执行 OldBuff
的 Merge
,并将新创建的 Buff
给 Remove
;
否则直接执行 BuffComponent
的 AddBuff
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 UGameplayBuffBase* UGameplayBuffManager::CommitBuff (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::CommitBuff, Target is nullptr! BuffID=%d" ), BuffID); return nullptr ; } FGameplayBuffParams MergedParams = GetMergedParams ( BuffID, Params ); UGameplayBuffBase* Buff = CreateBuff (Target, BuffID, MergedParams); if (Buff == nullptr ) { LogD (TEXT ("UGameplayBuffManager::CommitBuff, Buff is nullptr! UID=%llu, BuffID=%d" ), Target->GetUID (), BuffID); return nullptr ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (SpecBuffGroup.Num () && BuffSettings[BuffID].bNeedToMerge == true ) { UGameplayBuffBase* OldBuff = SpecBuffGroup[0 ]; OldBuff->Merge (Buff, MergedParams); Buff->Remove (); return OldBuff; } else { Target->AddBuff (Buff); return Buff; } } UGameplayBuffBase* UGameplayBuffManager::CreateBuff (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (!BuffSettings.Contains (BuffID) || !IsValid (BuffSettings[BuffID].BuffAsset)) { LogW (TEXT ("UGameplayBuffManager::CreateBuff, Miss BuffSetting! UID=%llu, BuffID=%d" ), Target->GetUID (), BuffID); return nullptr ; } UGameplayBuffBase* BuffInst = NewObject<UGameplayBuffBase>( Target, BuffSettings[BuffID].BuffAsset ); if (BuffInst == nullptr ) { LogW (TEXT ("UGameplayBuffManager::CreateBuff, BuffInst is nullptr! UID=%llu, BuffID=%d" ), Target->GetUID (), BuffID); return nullptr ; } BuffInst->Create (Target, BuffID, Params); return BuffInst; }
Query
:判断BuffComponent
上是否有对应ID的Buff
,返回对应 Handle
:
1 2 3 4 5 6 7 8 9 10 11 TArray<UGameplayBuffBase*> UGameplayBuffManager::QueryBuff (UGameplayBuffComponent* Target, int BuffID) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::QueryBuff, Target is nullptr! BuffID=%d" ), BuffID); return TArray<UGameplayBuffBase*>(); } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); return SpecBuffGroup; }
Remove
:通过 BuffID
或者 Handle
把 BuffComponent
上存在的对应 Buff
给 Remove
。
特别的,为了解决 Buff
之间的依赖问题(比如 Buff(A->B)
,在 A、B
的 Remove
都调用到了另一个 Buff
的 Remove
,会导致循环 Remove
问题),维护一个 bRemoveImmediately
(默认为false
),每次调用 Remove
时只是 MarkDirty
(把移除标记设为 true
),在下一次 Tick
才会实际移除。
这样就可以一次 Tick
移除一个 Buff
,通过时间来解开了这个循环依赖的链条。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 void UGameplayBuffManager::RemoveBuff (UGameplayBuffComponent* Target, int BuffID, bool bRemoveImmediately) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::RemoveBuff, Target is nullptr! BuffID=%d" ), BuffID); return ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (!SpecBuffGroup.Num ()) { LogD (TEXT ("UGameplayBuffManager::RemoveBuff, Buff is not exist! UID=%llu, BuffID=%d" ), Target->GetUID (), BuffID); return ; } for (int Index = SpecBuffGroup.Num () - 1 ; Index >= 0 ; Index--) { Target->RemoveBuff (SpecBuffGroup[Index], bRemoveImmediately); } } void UGameplayBuffManager::RemoveBuff (UGameplayBuffComponent* Target, UGameplayBuffBase* Buff, bool bRemoveImmediately) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::RemoveBuff, Target is nullptr!" )); return ; } Target->RemoveBuff (Buff, bRemoveImmediately); } void UGameplayBuffManager::ClearBuff (UGameplayBuffComponent* Target) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::RemoveBuff, Target is nullptr!" )); return ; } auto BuffGroup = Target->GetBuffGroup (); for (int Index = BuffGroup.Num () - 1 ; Index >= 0 ; Index--) { if (Index >= BuffGroup.Num ()) continue ; auto Buff = BuffGroup[Index]; if (Buff == nullptr ) continue ; Target->RemoveBuff (Buff); } }
Combine
:Buff
显然需要支持传入参数,在 Create
的时候传参,或者通过 CombineParams
将参数传入 Buff
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void UGameplayBuffManager::CombineBuffParams (UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params) { if (Target == nullptr ) { LogD (TEXT ("UGameplayBuffManager::CombineBuffParams, Target is nullptr! BuffID=%d" ), BuffID); return ; } auto SpecBuffGroup = Target->GetBuffGroupByBuffID (BuffID); if (!SpecBuffGroup.Num ()) { LogD (TEXT ("UGameplayBuffManager::CombineBuffParams, Buff is not exist! UID=%llu, BuffID=%d" ), Target->GetUID (), BuffID); return ; } for (auto Buff : SpecBuffGroup) { if (IsValid (Buff)) { Buff->CombineParams (Params); } } } void UGameplayBuffManager::CombineBuffParams (UGameplayBuffBase* Buff, const FGameplayBuffParams& Params) { if (Buff == nullptr ) { LogD (TEXT ("UGameplayBuffManager::CombineBuffParams, Buff is nullptr!" )); return ; } Buff->CombineParams (Params); }
Tick
:遍历BuffComponent
,遍历其中的 BuffGroup
;对每一个Buff
执行 Tick
,并且检查是否需要 Stop
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void UGameplayBuffManager::TickComponent (float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { if (IsClient (this )) return ; Super::TickComponent (DeltaTime, TickType, ThisTickFunction); auto GS = GetOwner<AGameStateBase>(); auto PlayerArray = GS->GetAllPlayerState (); for (auto Player : PlayerArray) { auto Target = UFunctionLibrary::GetPlayerStateComponent<UGameplayBuffComponent>(Player); if (Target == nullptr ) continue ; const auto BuffGroup = Target->GetBuffGroup (); for (auto Buff : BuffGroup) { if (Buff == nullptr ) continue ; if (!Buff->CheckNeedToStop ()) { Buff->Tick (DeltaTime); } } int TotalCount = BuffGroup.Num (); for (int Index = TotalCount - 1 ; Index >= 0 ; Index--) { auto & Buff = BuffGroup[Index]; if (Buff->CheckNeedToStop () || Buff->CheckNeedToRemove ()) { LogD (TEXT ("UGameplayBuffManager::TickComponent, Remove Buff (UID=%llu, BuffID=%d)" ), Player->GetActorStateUID (), Buff->GetBuffID ()); Target->RemoveBuff (Buff, true ); } } } }
BuffComponent
AddBuff
:将 Buff
添加到 BuffGroup
/ BuffIDArray
,通过 BuffIDArray
做客户端的同步(仅同步 BuffID
到客户端)
RemoveBuff
:移除 Buff
,并执行该 Buff
的 Remove
方法,同步对应信息;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 void UGameplayBuffComponent::AddBuff (UGameplayBuffBase* InBuff) { BuffGroup.Add ( InBuff ); GetBuffIDArray_Mutable ().Add ( InBuff->GetBuffID () ); if (IsStandaloneOrDS (this )) { OnRep_BuffIDArray (); } } void UGameplayBuffComponent::RemoveBuff (UGameplayBuffBase* InBuff, bool bRemoveImmediately) { if (InBuff == nullptr ) return ; int TotalCount = BuffGroup.Num (); for (int Index = TotalCount - 1 ; Index >= 0 ; Index--) { if (BuffGroup[Index] == InBuff) { if (bRemoveImmediately == true ) { BuffGroup.RemoveAt ( Index ); GetBuffIDArray_Mutable ().RemoveAt ( Index ); InBuff->SetNeedToRemove (true ); InBuff->Remove (); if (IsStandaloneOrDS (this )) { OnRep_BuffIDArray (); } } else { InBuff->SetNeedToRemove (true ); } break ; } } } void UGameplayBuffComponent::OnUninit () { Super::OnUninit (); if (!IsStandaloneOrDS (this )) return ; for (int Index = BuffGroup.Num () - 1 ; Index >= 0 ; Index--) { if (Index >= BuffGroup.Num ()) continue ; auto Buff = BuffGroup[Index]; if (Buff == nullptr ) continue ; BuffGroup.RemoveAt ( Index ); Buff->Remove (); } } void UGameplayBuffComponent::OnRep_BuffIDArray () { }
Buff
BuffBase
每个 Buff
的实际持有者为 BuffComponent
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 void UGameplayBuffBase::Create (UGameplayBuffComponent* InOwner, int InBuffID, const FGameplayBuffParams& Params) { if (InOwner == nullptr ) return ; Owner = InOwner; BuffID = InBuffID; BuffName = UGameplayBuffUtils::GetBuffName (BuffID); BuffParams = Params; LogD (TEXT ("UGameplayBuffBase::Create, UID=%llu, BuffID=%d, BuffName=%s, Params=%s" ), Owner->GetUID (), BuffID, *BuffName, *BuffParams.ToString ()); OnCreate (); } void UGameplayBuffBase::Remove () { if (Owner == nullptr ) return ; LogD (TEXT ("UGameplayBuffBase::Remove, UID=%llu, BuffID=%d, BuffName=%s" ), Owner->GetUID (), BuffID, *BuffName); OnRemove (); Owner = nullptr ; BuffID = 0 ; } void UGameplayBuffBase::Merge (UGameplayBuffBase* InBuff, const FGameplayBuffParams& Params) { if (Owner == nullptr ) return ; LogD (TEXT ("UGameplayBuffBase::Merge, UID=%llu, BuffID=%d, BuffName=%s" ), Owner->GetUID (), BuffID, *BuffName); OnMerge (InBuff, Params); } void UGameplayBuffBase::Tick (float DeltaTime) { OnTick (); } void UGameplayBuffBase::CombineParams (const FGameplayBuffParams& Params) { BuffParams = BuffParams + Params; }
TimeDurationBuff
Time时间流逝的 Buff
,需要支持 Refresh
、 Pause
、UpdateSpeedFactor
等操作;
基础的 TimeDurationBuff
的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void UGameplayBuff_TimeDuration::OnCreate () { Super::OnCreate (); TimeDuration = BaseTimeDuration; float InDuration = BuffParams.GetValue ("Duration" ); if (InDuration != 0 ) TimeDuration = InDuration; PassDuration = 0.0f ; LastTickTime = GetWorldTimeNow (); } void UGameplayBuff_TimeDuration::Tick (float DeltaTime) { if (GetWorld () == nullptr || GetWorld ()->GetGameState () == nullptr ) return ; float CurrentTickTime = GetWorldTimeNow (); TickInternal = CurrentTickTime - LastTickTime; LastTickTime = CurrentTickTime; if (CheckInPause () == false && CheckNeedToStop () == false ) { PassDuration += TickInternal * SpeedFactor; OnTick (); } }
Refresh
:刷新倒计时时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void UGameplayBuff_TimeDuration::Refresh (float InPassDuration, float InTimeDuration) { if (InPassDuration >= 0 ) { PassDuration = InPassDuration; } if (InTimeDuration >= 0 ) { TimeDuration = InTimeDuration; } OnRefresh (); }
Pause
:根据不同的 Reason
暂停/重启 时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void UGameplayBuff_TimeDuration::BeginPauseTime (FString Reason) { if (Owner == nullptr ) return ; LogD (TEXT ("UGameplayBuff_TimeDuration::BeginPauseTime, UID=%llu, BuffID=%d, TimeDuration=%.2f, bInPause=%d, Reason=%s" ), Owner->GetUID (), BuffID, TimeDuration, bInPause, *Reason); PauseReasons.AddUnique (Reason); if (bInPause == true ) return ; if (PauseReasons.Num () <= 0 ) return ; bInPause = true ; OnBeginPauseTime (); } void UGameplayBuff_TimeDuration::StopPauseTime (FString Reason) { if (Owner == nullptr ) return ; LogD (TEXT ("UGameplayBuff_TimeDuration::StopPauseTime, UID=%llu, BuffID=%d, TimeDuration=%.2f, bInPause=%d, Reason=%s" ), Owner->GetUID (), BuffID, TimeDuration, bInPause, *Reason); PauseReasons.Remove (Reason); if (bInPause == false ) return ; if (PauseReasons.Num () > 0 ) return ; bInPause = false ; OnStopPauseTime (); }
UpdateSpeedFactor
: 更新 Buff
的速度
1 2 3 4 5 6 7 8 9 void UGameplayBuff_TimeDuration::UpdateSpeedFactor (float InFactor) { if (Owner == nullptr ) return ; if (SpeedFactor == InFactor) return ; SpeedFactor = InFactor; LogD (TEXT ("UGameplayBuff_TimeDuration::UpdateSpeedFactor, UID=%llu, BuffID=%d, TimeDuration=%.2f, SpeedFactor=%.2f" ), Owner->GetUID (), BuffID, TimeDuration, SpeedFactor); OnUpdateSpeedFactor (); }
BuffUtils
暴露给外部系统使用的 Utils
,期望外部的调用都从这里走,实际上是一些胶水代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 UFUNCTION ()static int GetBuffID ( FString BuffName ) ;UFUNCTION ()static FString GetBuffName ( int BuffID ) ;UFUNCTION ()static float GetBuffParam (FString BuffName, FString ParamName) ;static float GetBuffParam (int BuffID, FString ParamName) ;UFUNCTION ()static TArray<UGameplayBuffBase*> GetGameplayBuff (APlayerStateBase* PS, FString BuffName) ;static TArray<UGameplayBuffBase*> GetGameplayBuff (APlayerStateBase* PS, int ID) ;UFUNCTION (BlueprintCallable)static UGameplayBuffBase* AddGameplayBuff (APlayerStateBase* PS, FString BuffName) ;static UGameplayBuffBase* AddGameplayBuff (APlayerStateBase* PS, int BuffID) ;static UGameplayBuffBase* AddGameplayBuff (APlayerStateBase* PS, FString BuffName, const FGameplayBuffParams& Params) ;static UGameplayBuffBase* AddGameplayBuff (APlayerStateBase* PS, int BuffID, const FGameplayBuffParams& Params) ;UFUNCTION (BlueprintCallable)static void RemoveGameplayBuff (APlayerStateBase* PS, FString BuffName, bool bRemoveImediately = false ) ;static void RemoveGameplayBuff (APlayerStateBase* PS, FString BuffName, bool bRemoveImediately, const FGameplayBuffParams& Params) ;static void RemoveGameplayBuff (APlayerStateBase* PS, int ID, bool bRemoveImediately = false , const FGameplayBuffParams& Params = {}) ;static void RemoveGameplayBuff (APlayerStateBase* PS, UGameplayBuffBase* Buff, bool bRemoveImediately = false , const FGameplayBuffParams& Params = {}) ;UFUNCTION ()static void CombineGameplayBuffParams (APlayerStateBase* PS, FString BuffName, const FGameplayBuffParams& Params = {}) ;static void CombineGameplayBuffParams (APlayerStateBase* PS, int ID, const FGameplayBuffParams& Params = {}) ;static void CombineGameplayBuffParams (UGameplayBuffBase* Buff, const FGameplayBuffParams& Params = {}) ;static void ClearGameplayBuff (APlayerStateBase* PS) ;UFUNCTION ()static bool CheckGameplayBuffExist (APlayerStateBase* PS, FString BuffName) ;static bool CheckGameplayBuffExist (APlayerStateBase* PS, int BuffID) ;static bool CheckGameplayBuffHandleExist (APlayerStateBase* PS, UGameplayBuffBase* Buff) ;
其它信息
FGameplayBuffParams
Buff
的 Params
部分,实现一个 ValueMap
记录各种类型的参数;
这样使用的时候就可以这样使用 Params
:
1 2 3 4 5 UGameplayBuffUtils::AddGameplayBuff (Target, BuffID, { {"ParamA" , (float )A}, {"ParamsB" , (int )B } }); float A = BuffParams.GetValue<float >("ParamA" );int B = BuffParams.GetValue<int >("ParamB" );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 USTRUCT (BlueprintType)struct FGameplayBuffParams { GENERATED_BODY () FGameplayBuffParams () = default ; FGameplayBuffParams (const FString& Key, FVariant Value); FGameplayBuffParams (std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs); void SetValue (const FString& FieldName, FVariant Value) ; bool Contains (const FString& FieldName) const ; bool IsEmpty () const ; template <typename ValueType = float > ValueType GetValue (const FString& FieldName, ValueType Default = {}) const { if (!ValueMap.Contains (FieldName)) return Default; if (TVariantTraits<ValueType>::GetType () != ValueMap[FieldName].GetType ()) return Default; return ValueMap[FieldName].GetValue<ValueType>(); } FString ToString () const ; const TMap<FString, FVariant>& GetValueMap () const ; bool NetSerialize (FArchive& Ar, class UPackageMap* Map, bool & bOutSuccess) ; FGameplayBuffParams operator +(const FGameplayBuffParams& OtherParams); void Merge (const FGameplayBuffParams& OtherParams) ; protected : TMap<FString, FVariant> ValueMap; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 FGameplayBuffParams::FGameplayBuffParams (const FString& Key, FVariant Value) { ValueMap.Add (Key, Value); } FGameplayBuffParams::FGameplayBuffParams (std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs) { for (const auto & Pair : ValuePairs) { ValueMap.Add (Pair.Key, Pair.Value); } } void FGameplayBuffParams::SetValue (const FString& FieldName, FVariant Value) { ValueMap.Add (FieldName, FVariant (Value)); } bool FGameplayBuffParams::Contains (const FString& FieldName) const { return ValueMap.Contains (FieldName); } bool FGameplayBuffParams::IsEmpty () const { return ValueMap.IsEmpty (); } FString FGameplayBuffParams::ToString () const { FString DebugString = FString::Printf (TEXT ("Params:" )); for (const auto & [Key, Value] : ValueMap) { DebugString += FString::Printf (TEXT ("[%s=%s]" ), *Key, *UStringUtils::ToString (Value)); } return DebugString; } const TMap<FString, FVariant>& FGameplayBuffParams::GetValueMap () const { return ValueMap; } bool FGameplayBuffParams::NetSerialize (FArchive& Ar, class UPackageMap* Map, bool & bOutSuccess) { bOutSuccess = true ; Ar << ValueMap; return true ; } FGameplayBuffParams FGameplayBuffParams::operator +(const FGameplayBuffParams& OtherParams) { FGameplayBuffParams CombinedParams = *this ; CombinedParams.Merge (OtherParams); return CombinedParams; } void FGameplayBuffParams::Merge (const FGameplayBuffParams& OtherParams) { for (const auto & [Key, Value] : OtherParams.GetValueMap ()) { SetValue (Key, Value); } }
FGameplayCountDownData
提供给 UGameplayBuff_TimeDuration
使用,一份记录倒计时的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 USTRUCT ()struct FGameplayCountDownData { GENERATED_USTRUCT_BODY () FGameplayCountDownData () = default ; FGameplayCountDownData (float StartTime, float TotalTime = 0.0f , float LeftTime = 0.0f , bool IsPause = false , float SpeedFactor = 1.0f ) : StartTime (StartTime), TotalTime (TotalTime), LeftTime (LeftTime), IsPause (IsPause), SpeedFactor (SpeedFactor) {} bool NetSerialize (FArchive& Ar, class UPackageMap* Map, bool & bOutSuccess) { Ar << StartTime; Ar << TotalTime; Ar << LeftTime; Ar << IsPause; Ar << SpeedFactor; bOutSuccess = true ; return true ; } bool operator ==(const FGameplayCountDownData& Other) const { if (StartTime != Other.StartTime) return false ; if (TotalTime != Other.TotalTime) return false ; if (LeftTime != Other.LeftTime) return false ; if (IsPause != Other.IsPause) return false ; if (SpeedFactor != Other.SpeedFactor) return false ; return true ; } bool operator !=(const FGameplayCountDownData& Other) const { return !(*this == Other); } FString ToString () const ; UPROPERTY () float StartTime = 0 ; UPROPERTY () float TotalTime = 0 ; UPROPERTY () float LeftTime = 0 ; UPROPERTY () bool IsPause = false ; UPROPERTY () float SpeedFactor = 1.0 ; };
TODO
Buff
本身的同步:目前 BuffSystem
只同步了一些 Info
(比如 BuffID
等)信息到 Client
,UE5
提供了 Replicate Subobject List
解决方案,可以通过 AddReplicatedSubObject
将一个 UObject
以 BuffComponent
的SubObject
的方式同步下去(UE4
需要自定义 ReplicateSubobjects
规则);这样可以将 Buff
本身同步下去;
Tag
的支持:通过 Tag
统一管理一些 Buff
,一个 Buff
支持不同的 Tag
;