ASC学习笔记0024:移除一个现有的属性集

中文注释:UrealEngine-5.2.1源码-AbilitySystemComponent.h

学习内容:

/** 移除一个现有的属性集 */
	void RemoveSpawnedAttribute(UAttributeSet* Attribute);

这是一个在Gameplay Ability System (GAS) 中常见的函数声明,用于从Ability System Component中移除已添加的属性集。以下是这个函数的典型实现方式:

基本实现

void UYourComponent::RemoveSpawnedAttribute(UAttributeSet* Attribute)
{
    if (!Attribute || !GetOwnerActor())
    {
        return;
    }
    
    // 从已生成的属性集列表中移除
    SpawnedAttributes.Remove(Attribute);
    
    // 如果这个属性集还在主属性集列表中,也从中移除
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        ASC->RemoveSpawnedAttribute(Attribute);
    }
    
    // 可选:销毁属性集对象
    if (Attribute->IsValidLowLevel())
    {
        Attribute->ConditionalBeginDestroy();
    }
}

更完整的实现示例

void UYourAbilitySystemComponent::RemoveSpawnedAttribute(UAttributeSet* Attribute)
{
    if (!Attribute)
    {
        UE_LOG(LogTemp, Warning, TEXT("Attempted to remove null AttributeSet"));
        return;
    }
    
    if (!SpawnedAttributes.Contains(Attribute))
    {
        UE_LOG(LogTemp, Warning, TEXT("AttributeSet not found in SpawnedAttributes"));
        return;
    }
    
    // 从内部列表中移除
    SpawnedAttributes.Remove(Attribute);
    
    // 从ASC的属性集列表中移除
    FGameplayAttributeData* AttributeData = nullptr;
    for (auto It = AttributeSetMap.CreateIterator(); It; ++It)
    {
        if (It.Value() == Attribute)
        {
            It.RemoveCurrent();
            break;
        }
    }
    
    // 清理并销毁
    Attribute->PreDestroy();
    Attribute->ConditionalBeginDestroy();
    
    UE_LOG(LogTemp, Log, TEXT("Successfully removed AttributeSet: %s"), 
        *GetNameSafe(Attribute->GetClass()));
}

使用示例

// 假设你有一个对属性集的引用
UAttributeSet* HealthAttributeSet = FindObject<UAttributeSet>(...);

// 移除属性集
RemoveSpawnedAttribute(HealthAttributeSet);
HealthAttributeSet = nullptr; // 避免悬空指针

注意事项

  1. 空指针检查:始终验证输入参数

  2. 所有权管理:确保正确管理属性集的生命周期

  3. 组件验证:确认Ability System Component有效

  4. 列表一致性:保持所有相关列表的同步更新

  5. 内存管理:适当销毁不再使用的对象

在GAS中的典型使用场景

  • 动态添加/移除角色的属性集

  • 处理装备系统的属性加成

  • 实现临时的状态效果

  • 清理不再需要的属性数据

这个函数通常在需要动态管理角色属性时使用,特别是在运行时需要修改属性集配置的情况下。

在实际项目中,RemoveSpawnedAttribute 函数在GAS系统中有着广泛的应用场景。以下是一些具体的实际应用案例:

1. 装备系统 - 动态属性管理

// 装备组件中管理装备带来的属性集
void UEquipmentComponent::UnequipItem(UEquipableItem* Item)
{
    if (!Item || !Item->GetAttributeSet())
    {
        return;
    }
    
    // 移除装备提供的属性集
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        ASC->RemoveSpawnedAttribute(Item->GetAttributeSet());
    }
    
    // 从本地记录中移除
    EquippedAttributeSets.Remove(Item->GetAttributeSet());
    
    // 广播装备移除事件
    OnItemUnequipped.Broadcast(Item);
}

// 装备物品时的对应函数
void UEquipmentComponent::EquipItem(UEquipableItem* Item)
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        UAttributeSet* NewAttributeSet = NewObject<UAttributeSet>(this, Item->AttributeSetClass);
        ASC->AddSpawnedAttribute(NewAttributeSet);
        EquippedAttributeSets.Add(NewAttributeSet);
    }
}

2. 状态效果系统 - 临时属性加成

// 状态效果组件管理临时属性
void UStatusEffectComponent::RemoveStatusEffect(FGameplayTag EffectTag)
{
    if (FStatusEffectData* EffectData = ActiveEffects.Find(EffectTag))
    {
        if (EffectData->AttributeSet && EffectData->AttributeSet->IsValidLowLevel())
        {
            // 移除状态效果对应的属性集
            if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
            {
                ASC->RemoveSpawnedAttribute(EffectData->AttributeSet);
            }
        }
        
        ActiveEffects.Remove(EffectTag);
        OnStatusEffectRemoved.Broadcast(EffectTag);
    }
}

// 添加临时状态效果
void UStatusEffectComponent::ApplyStatusEffect(TSubclassOf<UAttributeSet> EffectAttributeClass, float Duration)
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        UAttributeSet* NewEffectAttribute = NewObject<UAttributeSet>(this, EffectAttributeClass);
        ASC->AddSpawnedAttribute(NewEffectAttribute);
        
        FStatusEffectData NewEffect;
        NewEffect.AttributeSet = NewEffectAttribute;
        NewEffect.RemainingTime = Duration;
        ActiveEffects.Add(EffectAttributeClass->GetFName(), NewEffect);
    }
}

3. 职业/专精系统

// 角色职业管理
void UCharacterClassComponent::SwitchClass(UCharacterClass* NewClass)
{
    if (CurrentClassAttributeSet)
    {
        // 移除旧职业的属性集
        if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
        {
            ASC->RemoveSpawnedAttribute(CurrentClassAttributeSet);
            CurrentClassAttributeSet = nullptr;
        }
    }
    
    // 添加新职业的属性集
    if (NewClass && NewClass->ClassAttributeSet)
    {
        UAttributeSet* NewAttributeSet = NewObject<UAttributeSet>(this, NewClass->ClassAttributeSet);
        if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
        {
            ASC->AddSpawnedAttribute(NewAttributeSet);
            CurrentClassAttributeSet = NewAttributeSet;
        }
    }
    
    CurrentClass = NewClass;
    OnClassChanged.Broadcast(NewClass);
}

4. 技能树/天赋系统

// 天赋系统管理选择的属性加成
void UTalentSystemComponent::ResetTalents()
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        // 移除所有通过天赋添加的属性集
        for (UAttributeSet* TalentAttribute : ActiveTalentAttributes)
        {
            if (TalentAttribute && TalentAttribute->IsValidLowLevel())
            {
                ASC->RemoveSpawnedAttribute(TalentAttribute);
            }
        }
    }
    
    ActiveTalentAttributes.Empty();
    SelectedTalents.Empty();
    
    // 重新应用基础职业属性
    ApplyBaseClassAttributes();
}

5. 多人游戏中的同步处理

// 在网络游戏中安全地处理属性集移除
void UPlayerCharacter::Server_RemoveAttributeSet_Implementation(UAttributeSet* AttributeSet)
{
    if (GetLocalRole() == ROLE_Authority && AttributeSet)
    {
        // 服务器端移除
        if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
        {
            ASC->RemoveSpawnedAttribute(AttributeSet);
        }
        
        // 通知客户端
        Multicast_OnAttributeSetRemoved(AttributeSet);
    }
}

// 客户端响应
void UPlayerCharacter::Multicast_OnAttributeSetRemoved_Implementation(UAttributeSet* AttributeSet)
{
    // 客户端清理本地引用
    if (GetLocalRole() != ROLE_Authority && AttributeSet)
    {
        // 避免客户端重复销毁
        if (OwnedAttributeSets.Contains(AttributeSet))
        {
            OwnedAttributeSets.Remove(AttributeSet);
        }
    }
}

6. 完整的生命周期管理

// 在角色销毁时清理所有动态属性集
void AAdvancedCharacter::BeginDestroy()
{
    // 清理所有动态添加的属性集
    for (UAttributeSet* DynamicAttributeSet : DynamicAttributeSets)
    {
        if (DynamicAttributeSet && DynamicAttributeSet->IsValidLowLevel())
        {
            if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
            {
                ASC->RemoveSpawnedAttribute(DynamicAttributeSet);
            }
        }
    }
    DynamicAttributeSets.Empty();
    
    Super::BeginDestroy();
}

// 安全的属性集移除函数
void AAdvancedCharacter::SafeRemoveAttributeSet(UAttributeSet* AttributeSet)
{
    if (!AttributeSet || !IsValid(AttributeSet))
    {
        return;
    }
    
    // 检查是否属于这个角色
    if (!DynamicAttributeSets.Contains(AttributeSet))
    {
        UE_LOG(LogGame, Warning, TEXT("Attempting to remove AttributeSet that doesn't belong to this character"));
        return;
    }
    
    // 从ASC移除
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        ASC->RemoveSpawnedAttribute(AttributeSet);
    }
    
    // 从本地记录移除
    DynamicAttributeSets.Remove(AttributeSet);
    
    // 标记为待销毁
    AttributeSet->MarkAsGarbage();
}

实际项目中的最佳实践

  1. 引用管理:始终保持对动态属性集的引用,避免内存泄漏

  2. 网络同步:在多人游戏中确保服务器和客户端状态一致

  3. 错误处理:添加充分的空指针和有效性检查

  4. 生命周期:在适当的时机(如角色销毁、关卡切换)清理属性集

  5. 性能考虑:避免频繁的添加/移除操作,使用属性修饰器(Modifiers)代替

这些实际应用展示了RemoveSpawnedAttribute在构建复杂游戏系统时的重要性,特别是在需要动态修改角色属性的RPG、MMO等游戏类型中。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值