GameplayBuffSystem框架

基础框架

BuffManager ,管理所有的 BuffComponent

对于每个 Player,由 BuffComponent 管理该 Player 身上持有的 Buff

classDiagram

	UGameplayBuffManager..>FGameplayBuffSetting
	UGameplayBuffManager..>UGameplayBuffComponent
	UGameplayBuffManager..>FGameplayBuffParams
    class UGameplayBuffManager {
        BuffSettings : TMap~int, FGameplayBuffSetting~
        
        +CommitBuff(Target, Buff, Params)
        +QueryBuff(Target, Buff)
        +CombineBuffParams(Target, Buff, Params)
        +RemoveBuff(Target, Buff, bRemoveImmediately)
        +ClearBuff(Target)
        -Tick()
        -CreateBuff(Target, Buff, Params)
        -GetMergedParams(Buff, Params)
        
        -InitBuffSetting()
    }
    
    UGameplayBuffComponent..>UGameplayBuffBase
    class UGameplayBuffComponent {
    	BuffGroup : TArray~UGameplayBuffBase*~
        BuffIDArray : int (For Sync)
        AddBuff(InBuff)
        RemoveBuff(InBuff, bRemoveImmediately)
    }
	
	class FGameplayBuffSetting {
		ID : int
        Name : FString
        bNeedToMerge : bool
        AssetPath : FString
        BuffAsset : TSubclassOf~UGameplayBuffBase~
        DefaultParams : FGameplayBuffParams
    }
    
	class FGameplayBuffParams {
        # ValueMap : TMap~FString, FVariant~
        # Tags : TArray~FString~
        
        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

BuffSetting

BuffManager 管理 Buff 本身的配置,即 FGameplayBuffSetting

在这里通过外部配置的 Buff 表的 Cfg,读取 AssetPath 并加载对应 BuffClass,保存外部配置的静态 TagsDefaultParams(后续可与运行时传入的 TagsParams 进行 Merge);

同时维护 TagsBuffIDBuffName 之间的映射,方便后续管理;

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
void UGameplayBuffManager::InitBuffSettings()
{
// 从 Buff 表读取所有相关配置
for (auto Cfg : Cfgs)
{
FGameplayBuffSetting BuffSetting;
bool bSuccess = CreateBuffSetting(Cfg, BuffSetting);
if (!bSuccess) continue;
ProcessBuffSetting(BuffSetting);
}
}

bool UGameplayBuffManager::CreateBuffSetting(GameplayBuffSettingCfg* Cfg, FGameplayBuffSetting& BuffSetting)
{
if (Cfg == nullptr) return false;

int BuffID = Cfg->ID();

FString RowAssetPath = Cfg->AssetPath();
FString AssetPath = GetProcessedAssetPath(RowAssetPath);
if (AssetPath.IsEmpty())
{
return false;
}

TSubclassOf<UGameplayBuffBase> Asset = LoadClass<UGameplayBuffBase>(this, *AssetPath);
if (!IsValid(Asset))
{
return false;
}

BuffSetting.ID = BuffID;
BuffSetting.Name = Cfg->Name();
BuffSetting.bNeedToMerge = Cfg->bNeedToMerge();
BuffSetting.AssetPath = AssetPath;
BuffSetting.Asset = Asset;

// DefaultParams
{
auto& DefaultParams = BuffSetting.DefaultParams;
for (int Index = 0; Index < FMath::Min(Cfg->Key_size(), Cfg->Value_size()); Index++)
{
FString Key = Cfg->Key(Index);
if (!DefaultParams.Contains(Key))
{
DefaultParams.SetValue(Key, Cfg->Value(Index) );
}
}

for (auto Tag : Cfg->Tag())
{
DefaultParams.Tags.AddUnique( Tag );
}
}

return true;
}


void UGameplayBuffManager::ProcessBuffSetting(const FGameplayBuffSetting& BuffSetting)
{
int BuffID = BuffSetting.ID;
FString BuffName = BuffSetting.Name;
if (BuffSettings.Contains( BuffID ))
{
return;
}

if (BuffNameToIDMap.Contains( BuffName ))
{
return;
}

BuffSettings.Add( BuffID, BuffSetting );
BuffNameToIDMap.Add( BuffName, BuffID );
for (const auto& Tag : BuffSetting.DefaultParams.Tags)
{
BuffTagToIDsMap.FindOrAdd(Tag).AddUnique( BuffID );
}
}

具体实现

  1. Commit:判断 BuffSettings 里是否有对应 BuffIDBuff,若存在则创建该实例;

    判断一下 BuffComponent 里是否原本已经有相同 BuffIDBuff,若存在,并且该 Buff 需要 Merge 的话,执行 OldBuffMerge ,并将新创建的 BuffRemove

    否则直接执行 BuffComponentAddBuff

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
UGameplayBuffBase* UGameplayBuffManager::CommitBuff(UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params)
{
if (Target == nullptr)
{
return nullptr;
}

FGameplayBuffParams MergedParams = GetMergedParams( BuffID, Params );

UGameplayBuffBase* Buff = CreateBuff(Target, BuffID, MergedParams);
if (Buff == nullptr)
{
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].Asset))
{
return nullptr;
}

UGameplayBuffBase* BuffInst = NewObject<UGameplayBuffBase>( Target, BuffSettings[BuffID].Asset );
if (BuffInst == nullptr)
{
return nullptr;
}


BuffInst->Create(Target, BuffID, Params);

return BuffInst;
}
  1. Query:判断BuffComponent上是否有对应ID的Buff,返回对应 Handle
1
2
3
4
5
6
7
8
9
10
TArray<UGameplayBuffBase*> UGameplayBuffManager::QueryBuff(UGameplayBuffComponent* Target, int BuffID)
{
if (Target == nullptr)
{
return TArray<UGameplayBuffBase*>();
}

auto SpecBuffGroup = Target->GetBuffGroupByBuffID(BuffID);
return SpecBuffGroup;
}
  1. Remove:通过 BuffID 或者 HandleBuffComponent 上存在的对应 BuffRemove
    特别的,为了解决 Buff 之间的依赖问题(比如 Buff(A->B),在 A、BRemove 都调用到了另一个 BuffRemove,会导致循环 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
void UGameplayBuffManager::RemoveBuff(UGameplayBuffComponent* Target, int BuffID, bool bRemoveImmediately)
{
if (Target == nullptr)
{
return;
}

auto SpecBuffGroup = Target->GetBuffGroupByBuffID(BuffID);
if (!SpecBuffGroup.Num())
{
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)
{
return;
}
Target->RemoveBuff(Buff, bRemoveImmediately);
}

void UGameplayBuffManager::ClearBuff(UGameplayBuffComponent* Target)
{
if (Target == 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);
}
}
  1. CombineBuff 显然需要支持传入参数,在 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
void UGameplayBuffManager::CombineBuffParams(UGameplayBuffComponent* Target, int BuffID, const FGameplayBuffParams& Params)
{
if (Target == nullptr)
{
return;
}

auto SpecBuffGroup = Target->GetBuffGroupByBuffID(BuffID);
if (!SpecBuffGroup.Num())
{
return;
}

for (auto Buff : SpecBuffGroup)
{
if (IsValid(Buff))
{
Buff->CombineParams(Params);
}
}
}

void UGameplayBuffManager::CombineBuffParams(UGameplayBuffBase* Buff, const FGameplayBuffParams& Params)
{
if (Buff == nullptr)
{
return;
}

Buff->CombineParams(Params);
}
  1. 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
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();

// Tick
for (auto Buff : BuffGroup)
{
if (Buff == nullptr) continue;
if (!Buff->CheckNeedToStop())
{
Buff->Tick(DeltaTime);
}
}

// Remove
int TotalCount = BuffGroup.Num();
for (int Index = TotalCount - 1; Index >= 0; Index--)
{
auto& Buff = BuffGroup[Index];
if (Buff->CheckNeedToStop() || Buff->CheckNeedToRemove())
{
Target->RemoveBuff(Buff, true);
}
}
}
}

BuffComponent

  1. AddBuff:将 Buff 添加到 BuffGroup / BuffIDArray,通过 BuffIDArray 做客户端的同步(仅同步 BuffID 到客户端)
  2. RemoveBuff:移除 Buff,并执行该 BuffRemove 方法,同步对应信息;
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

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->CombineParams( {"Uninit", true} );
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
void UGameplayBuffBase::Create(UGameplayBuffComponent* InOwner, int InBuffID, const FGameplayBuffParams& Params)
{
if (InOwner == nullptr) return;

Owner = InOwner;
BuffID = InBuffID;
BuffName = UGameplayBuffUtils::GetBuffName(BuffID);
BuffParams = Params;
OnCreate();
}

void UGameplayBuffBase::Remove()
{
if (Owner == nullptr) return;

OnRemove();

Owner = nullptr;
BuffID = 0;
}

void UGameplayBuffBase::Merge(UGameplayBuffBase* InBuff, const FGameplayBuffParams& Params)
{
if (Owner == nullptr) return;
OnMerge(InBuff, Params);
}

void UGameplayBuffBase::Tick(float DeltaTime)
{
OnTick();
}

void UGameplayBuffBase::CombineParams(const FGameplayBuffParams& Params)
{
BuffParams = BuffParams + Params;
}

TimeDurationBuff

Time时间流逝的 Buff,需要支持 RefreshPauseUpdateSpeedFactor 等操作;

  1. 基础的 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();
}
}
  1. 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();
}
  1. 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
void UGameplayBuff_TimeDuration::BeginPauseTime(FString Reason)
{
if (Owner == nullptr) return;

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;

PauseReasons.Remove(Reason);

if (bInPause == false) return;
if (PauseReasons.Num() > 0) return;
bInPause = false;

OnStopPauseTime();
}
  1. UpdateSpeedFactor : 更新 Buff 的速度
1
2
3
4
5
6
7
void UGameplayBuff_TimeDuration::UpdateSpeedFactor(float InFactor)
{
if (Owner == nullptr) return;
if (SpeedFactor == InFactor) return;
SpeedFactor = InFactor;
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
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#pragma region Base

public:
UFUNCTION()
static UGameplayBuffManager* GetBuffSystem(UWorld* World = nullptr);
UFUNCTION()
static UGameplayBuffComponent* GetBuffComponent(APlayerState* PS);

#pragma endregion Base

#pragma region Setting

public:
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);
static TArray<int> GetBuffIDsByTag(FString Tag);

#pragma endregion Setting


#pragma region Get

public:
UFUNCTION()
static TArray<UGameplayBuffBase*> GetGameplayBuff(APlayerState* PS, FString BuffName);
static TArray<UGameplayBuffBase*> GetGameplayBuff(APlayerState* PS, int ID);

#pragma endregion Get


#pragma region Add

public:
UFUNCTION(BlueprintCallable)
static UGameplayBuffBase* AddGameplayBuff(APlayerState* PS, FString BuffName);
static UGameplayBuffBase* AddGameplayBuff(APlayerState* PS, int BuffID);
static UGameplayBuffBase* AddGameplayBuff(APlayerState* PS, FString BuffName, const FGameplayBuffParams& Params);
static UGameplayBuffBase* AddGameplayBuff(APlayerState* PS, int BuffID, const FGameplayBuffParams& Params);

#pragma endregion Add


#pragma region Remove

public:
// bRemoveImmediately = true 可能会导致依赖问题,非必要时序依赖,建议使用 false
UFUNCTION(BlueprintCallable)
static void RemoveGameplayBuff(APlayerState* PS, FString BuffName, bool bRemoveImediately = false);
static void RemoveGameplayBuff(APlayerState* PS, FString BuffName, bool bRemoveImediately, const FGameplayBuffParams& Params);
static void RemoveGameplayBuff(APlayerState* PS, int ID, bool bRemoveImediately = false, const FGameplayBuffParams& Params = {});
static void RemoveGameplayBuff(APlayerState* PS, UGameplayBuffBase* Buff, bool bRemoveImediately = false, const FGameplayBuffParams& Params = {});
static void RemoveGameplayBuffsByTag(APlayerState* PS, FString Tag, bool bRemoveImediately = false, const FGameplayBuffParams& Params = {});
static void ClearGameplayBuff(APlayerState* PS, const FGameplayBuffParams& Params = {});

#pragma endregion Remove


#pragma region CombineParams

public:
UFUNCTION()
static void CombineGameplayBuffParams(APlayerState* PS, FString BuffName, const FGameplayBuffParams& Params = {});
static void CombineGameplayBuffParams(APlayerState* PS, int ID, const FGameplayBuffParams& Params = {});
static void CombineGameplayBuffParams(UGameplayBuffBase* Buff, const FGameplayBuffParams& Params = {});
static void CombineGameplayBuffParams(APlayerState* PS, const FGameplayBuffParams& Params = {});

#pragma endregion CombineParams

#pragma region CheckExist

public:
UFUNCTION()
static bool CheckGameplayBuffExist(APlayerState* PS, FString BuffName);
static bool CheckGameplayBuffExist(APlayerState* PS, int BuffID);
static bool CheckGameplayBuffHandleExist(APlayerState* PS, UGameplayBuffBase* Buff);

#pragma endregion CheckExist

其它信息

GameplayBuffParams

BuffParams 部分,

继承 FCommonVariantParams ,实现一个 ValueMap 记录各种类型的参数,同时额外记录 Tags

这样使用的时候就可以这样使用 Params

1
2
3
4
5
6
7
// 外部调用创建Buff时:

UGameplayBuffUtils::AddGameplayBuff(Target, BuffID, { {"ParamA", (float)A}, {"ParamsB", (int)B } });

// Buff内部 (BuffParams 已经传入 Buff 中)
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
31
32
33
34
35
USTRUCT()
struct FGameplayBuffParams : public FCommonVariantParams
{
GENERATED_BODY()

FGameplayBuffParams() = default;
FGameplayBuffParams(const FString& Key, FVariant Value, const TArray<FString>& Tags = {})
: FCommonVariantParams(Key, Value), Tags(Tags) {}
FGameplayBuffParams(std::initializer_list<TPairInitializer<const FString&, FVariant>> ValuePairs, const TArray<FString>& InTags = {})
: FCommonVariantParams(ValuePairs), Tags(InTags) {}
FGameplayBuffParams(const TArray<FString>& Tags)
: Tags(Tags) {}

FString ToString() const;
bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);

FGameplayBuffParams& operator=(const FCommonVariantParams& OtherParams);
FGameplayBuffParams operator+(const FGameplayBuffParams& OtherParams);

void Merge(const FGameplayBuffParams& OtherParams);
void Clear();
public:
UPROPERTY()
TArray <FString> Tags;
};

template<>
struct TStructOpsTypeTraits<FGameplayBuffParams> : TStructOpsTypeTraitsBase2<FGameplayBuffParams>
{
enum
{
WithNetSerializer = true,
};
};

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
bool FGameplayBuffParams::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
{
bOutSuccess = true;
Ar << ValueMap;
Ar << Tags;
return true;
}

FGameplayBuffParams& FGameplayBuffParams::operator=(const FCommonVariantParams& OtherParams)
{
ValueMap = OtherParams.GetValueMap();
Tags = {};
return *this;
}

FGameplayBuffParams FGameplayBuffParams::operator+(const FGameplayBuffParams& OtherParams)
{
FGameplayBuffParams CombinedParams = *this;
CombinedParams.Merge(OtherParams);
return CombinedParams;
}

void FGameplayBuffParams::Merge(const FGameplayBuffParams& OtherParams)
{
FCommonVariantParams::Merge(OtherParams);
for (const auto& Tag : OtherParams.Tags)
{
Tags.AddUnique( Tag );
}
}

void FGameplayBuffParams::Clear()
{
FCommonVariantParams::Clear();
Tags.Empty();
}

GameplayCountDownData

提供给 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

  1. Buff 本身的同步:目前 BuffSystem 只同步了一些 Info(比如 BuffID 等)信息到 ClientUE5 提供了完整的 ReplicateSubobject 解决方案,可以通过 AddReplicatedSubObject 将一个 UObjectBuffComponentSubObject 的方式同步下去;UE4ReplicateSubobject 会将所有的 SubobjActorChanel->CreatSubObjects 持有,并且不能方便地 Remove