BuffSystem的简单实现

设计

首先我们需要一个 BuffManager ,管理所有的 BuffComponent

首先,对于一个 BuffSystem,我们需要有一个挂在各个 Actor 上的 BuffComponent,我们通过这个 BuffComponent 管理该 Actor 自身的 Buff,对它们做一些 Add / Remove / Sync 等处理;这个 BuffComponent 会同步信息到每一个客户端上,用于访问其 Buff 信息;

同时,需要有一个 BuffManager 管理所有的 BuffComponent,从 BuffManager 发起 Commit / Query / Remove 的调用,同时记录下 BuffSetting

最后需要一个 BuffBase 作为 Buff 的基类;

image-20221227173707822

BuffManager

image-20221227180806212

  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
49
UGameplayBuffBase* UGameplayBuffManager::CommitBuff(UGameplayBuffComponent* Target, int BuffID)
{
if (Target == nullptr)
{
// LogWarning...
return nullptr;
}

UGameplayBuffBase* Buff = CreateBuff(Target, BuffID);
if (Buff == nullptr)
{
// LogWarning...
return nullptr;
}

auto SpecBuffGroup = Target->GetBuffGroupByBuffID(BuffID);
if (SpecBuffGroup.Num() && BuffSettings[BuffID].bNeedToMerge == true)
{
UGameplayBuffBase* OldBuff = SpecBuffGroup[0];
OldBuff->Merge(Buff);
Buff->Remove();
return OldBuff;
}
else
{
Target->AddBuff(Buff);
return Buff;
}
}

UGameplayBuffBase* UGameplayBuffManager::CreateBuff(UGameplayBuffComponent* Target, int BuffID)
{
if (!BuffSettings.Contains(BuffID) || !IsValid(BuffSettings[BuffID].BuffAsset))
{
// LogWarning...
return nullptr;
}

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

BuffInst->Create(Target, BuffID);

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

auto SpecBuffGroup = Target->GetBuffGroupByBuffID(BuffID);
return SpecBuffGroup;
}
  1. Remove:通过 BuffID 或者 Handler Remove掉 BuffComponent 上存在的对应 Buff,执行 RemoveBuff
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 UGameplayBuffManager::RemoveBuff(UGameplayBuffComponent* Target, int BuffID)
{
if (Target == nullptr)
{
// LogWarning...
return;
}

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

for (int Index = SpecBuffGroup.Num() - 1; Index >= 0; Index--)
{
Target->RemoveBuff(SpecBuffGroup[Index]);
}
}

void UGameplayBuffManager::RemoveBuff(UGameplayBuffComponent* Target, UGameplayBuffBase* Buff)
{
if (Target == nullptr)
{
// LogWarning...
return;
}
Target->RemoveBuff(Buff);
}
  1. Tick:遍历BuffComponent,遍历其中的 BuffGroup;对每一个Buff 执行 Tick,并且检查是否需要 Stop 。同时,Tick 一般只在服务器运行。
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::Tick()
{
if (IsClient(this)) return;

for (auto Target : BuffComponentArray)
{
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
for (int Index = BuffGroup.Num() - 1; Index >= 0; Index--)
{
auto& Buff = BuffGroup[Index];
if (Buff->CheckNeedToStop())
{
Target->RemoveBuff(Buff);
}
}
}
}

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
FORCEINLINE const TArray <USGGameplayBuffBase*>& GetBuffGroup() { return BuffGroup; }
FORCEINLINE const TArray <int>& GetBuffIDArray() { return BuffIDArray; }

void UGameplayBuffComponent::AddBuff(UGameplayBuffBase* InBuff)
{
BuffGroup.Add( InBuff );
BuffIDArray().Add( InBuff->GetBuffID() );
MARK_PROPERTY_DIRTY(this, BuffIDArray);

if (IsStandaloneOrDS(this))
{
OnRep_BuffIDArray();
}
}

void UGameplayBuffComponent::RemoveBuff(UGameplayBuffBase* InBuff)
{
if (InBuff == nullptr) return;
for (int Index = BuffGroup.Num() - 1; Index >= 0; Index--)
{
if (BuffGroup[Index] == InBuff)
{
BuffGroup[Index]->Remove();
BuffGroup.RemoveAt( Index );
BuffIDArray.RemoveAt( Index );
}
}
MARK_PROPERTY_DIRTY(this, BuffIDArray);
if (IsStandaloneOrDS(this))
{
OnRep_BuffIDArray();
}
}

void UGameplayBuffComponent::OnRep_BuffIDArray()
{
// 一些 NotifyEvent..
OnGameplayBuffIDArrayChanged.Broadcast( BuffIDArray );
}

BuffBase

  1. Create:执行 OnCreate,保存 BuffIDOwner 信息;
  2. Remove:执行 OnRemove,并且清空信息;
  3. Merge:执行 OnMerge
  4. Tick:执行 OnTick

每个 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
void UGameplayBuffBase::Create(UGameplayBuffComponent* InOwner, int InBuffID)
{
if (InOwner == nullptr) return;

Owner = InOwner;
BuffID = InBuffID;
// Log...

OnCreate();
}

void UGameplayBuffBase::OnCreate()
{
// Log...
}


void UGameplayBuffBase::Remove()
{
// Log...
OnRemove();


Owner = nullptr;
BuffID = 0;
}

void UGameplayBuffBase::OnRemove()
{
// Log...
}


void UGameplayBuffBase::Merge(UGameplayBuffBase* InBuff)
{
// Log...
OnMerge(InBuff);
}

void UGameplayBuffBase::OnMerge(UGameplayBuffBase* InBuff)
{
// Log...
}


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

void UGameplayBuffBase::OnTick()
{

}