最近在用UE5学习GASDocumentation入门GAS,记录一些学习内容,如有错误请指正。
1 初始化AttributeSet
AS可以通过GE和DataTable两种不同方式初始化,Epic推荐使用GE
1.1 通过GamePlayEffect
GASDocumentation中使用Instant的GameplayEffect,首先创建一个以DefaultAttributes
然后在初始化的时候Apply即可
void AGDCharacterBase::InitializeAttributes()
{
if (!AbilitySystemComponent.IsValid())
{
return;
}
if (!DefaultAttributes)
{
UE_LOG(LogTemp, Error, TEXT("%s() Missing DefaultAttributes for %s. Please fill in the character's Blueprint."), *FString(__FUNCTION__), *GetName());
return;
}
// Can run on Server and Client
FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
EffectContext.AddSourceObject(this);
FGameplayEffectSpecHandle NewHandle = AbilitySystemComponent->MakeOutgoingSpec(DefaultAttributes, GetCharacterLevel(), EffectContext);
if (NewHandle.IsValid())
{
FActiveGameplayEffectHandle ActiveGEHandle = AbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*NewHandle.Data.Get(), AbilitySystemComponent.Get());
}
}
1.2 通过DataTable
创建一个DataTable,行结构选择AttributeMetaData,其格式如下。
添加行,将RowName修改为[AttributeSet].[Attribute]并保存该DataTable。
然后找到Character的ASC组件,在Attribute Test条目填上表格即可。
源码流程分析:
初始化数据表的逻辑就放在ASC的OnRegister函数中,可以看到其直接调用了AttrubiteSet内的InitFromMetaDataTable函数。
void UAbilitySystemComponent::OnRegister()
{
............
// Init starting data
for (int32 i=0; i < DefaultStartingData.Num(); ++i)
{
if (DefaultStartingData[i].Attributes && DefaultStartingData[i].DefaultStartingTable)
{
UAttributeSet* Attributes = const_cast<UAttributeSet*>(GetOrCreateAttributeSubobject(DefaultStartingData[i].Attributes));
Attributes->InitFromMetaDataTable(DefaultStartingData[i].DefaultStartingTable);
}
}
...................
}
先通过 GetOrCreateAttributeSubobject函数获取AttributeSet,其内部其实就是从SpawnedAttributes中获取,如果没有,调用AddSpawnedAttribute函数添加
void UAbilitySystemComponent::AddSpawnedAttribute(UAttributeSet* Attribute)
{
if (!IsValid(Attribute))
{
return;
}
if (SpawnedAttributes.Find(Attribute) == INDEX_NONE)
{
if (IsUsingRegisteredSubObjectList() && IsReadyForReplication())
{
AddReplicatedSubObject(Attribute);
}
SpawnedAttributes.Add(Attribute);
SetSpawnedAttributesListDirty();
}
}
InitFromMetaDataTable函数的初始化方式也很简单,通过TFieldIterator迭代该AttrubiteSet下的所有属性,然后会分别处理数值类型或FGameplayAttributeData类型。
void UAttributeSet::InitFromMetaDataTable(const UDataTable* DataTable)
{
static const FString Context = FString(TEXT("UAttribute::BindToMetaDataTable"));
for( TFieldIterator<FProperty> It(GetClass(), EFieldIteratorFlags::IncludeSuper) ; It ; ++It )
{
FProperty* Property = *It;
FNumericProperty *NumericProperty = CastField<FNumericProperty>(Property);
if (NumericProperty)
{
// Set Value ...
}
else if (FGameplayAttribute::IsGameplayAttributeDataProperty(Property))
{
FString RowNameStr = FString::Printf(TEXT("%s.%s"), *Property->GetOwnerVariant().GetName(), *Property->GetName());
FAttributeMetaData * MetaData = DataTable->FindRow<FAttributeMetaData>(FName(*RowNameStr), Context, false);
if (MetaData)
{
...
FGameplayAttributeData* DataPtr = StructProperty->ContainerPtrToValuePtr<FGameplayAttributeData>(this);
// Set Value ...
DataPtr->SetBaseValue(MetaData->BaseValue);
DataPtr->SetCurrentValue(MetaData->BaseValue);
}
}
}
...
}
2 技能冷却CooldownGameplayEffect
首先创建一个GameEffect,配置为Has Duration,并且使用TargetTagsGamePlayEffectComponent添加一个Tag
然后在配置在技能Cooldowns即可
在使用技能的时候,会调用CanActivateAbility函数,里面会调用CheckCooldown判断技能CD,其内部就是根据Tag去比对,技能消耗Cost也是类似的实现方案
bool UGameplayAbility::CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, OUT FGameplayTagContainer* OptionalRelevantTags) const
{
.....................
if (!AbilitySystemGlobals.ShouldIgnoreCooldowns() && !CheckCooldown(Handle, ActorInfo, OptionalRelevantTags))
{
if (FScopedCanActivateAbilityLogEnabler::IsLoggingEnabled())
{
ABILITY_VLOG(ActorInfo->OwnerActor.Get(), Verbose, TEXT("Ability could not be activated due to Cooldown: %s"), *GetName());
}
return false;
}
if (!AbilitySystemGlobals.ShouldIgnoreCosts() && !CheckCost(Handle, ActorInfo, OptionalRelevantTags))
{
if (FScopedCanActivateAbilityLogEnabler::IsLoggingEnabled())
{
ABILITY_VLOG(ActorInfo->OwnerActor.Get(), Verbose, TEXT("Ability could not be activated due to Cost: %s"), *GetName());
}
return false;
}
......................
return true;
}