ASC学习笔记0022:在不打算修改属性集时访问生成的属性集

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

学习内容:

cpp 复制代码
/** 在不打算修改列表时访问生成的属性列表。 */
	const TArray<UAttributeSet*>& GetSpawnedAttributes() const;

这是一个C++代码片段,来自Unreal Engine项目。让我来解释这个函数声明:

函数说明

功能:获取已生成的属性集列表的只读引用

关键特点

  1. 返回类型const TArray<UAttributeSet*>&

    • 返回对 UAttributeSet 指针数组的常量引用

    • 避免拷贝整个数组,提高性能

    • 防止调用者修改原始数据

  2. const 成员函数

    • 函数末尾的 const 表示该函数不会修改类的成员变量

    • 可以在 const 对象上调用

  3. 使用场景

    • 只需要查看已生成的属性集,而不需要修改它们

    • 用于迭代、查询或显示属性信息

典型用法示例

cpp 复制代码
// 假设在某个类的成员函数中
void SomeFunction() const
{
    // 获取属性列表(只读访问)
    const TArray<UAttributeSet*>& Attributes = GetSpawnedAttributes();
    
    // 可以遍历查看,但不能修改
    for (UAttributeSet* Attr : Attributes)
    {
        if (Attr)
        {
            // 查看属性信息...
            UE_LOG(LogTemp, Warning, TEXT("Attribute Set: %s"), *Attr->GetName());
        }
    }
    
    // 以下代码会编译错误(因为是const引用):
    // Attributes.Add(NewAttribute); // 错误!
    // Attributes.RemoveAt(0);       // 错误!
}

这种设计模式在UE中很常见,既保证了数据的安全性,又提供了高效的访问方式。

在UE项目中,这个函数在Gameplay Ability System (GAS) 架构中有广泛的应用。以下是几个实际应用场景:

1. UI显示属性值

cpp 复制代码
// 在HUD或Widget中显示角色所有属性
void UAttributeWidget::UpdateAttributeDisplay()
{
    if (ACharacter* PlayerCharacter = GetPlayerCharacter())
    {
        if (UAbilitySystemComponent* ASC = PlayerCharacter->FindComponentByClass<UAbilitySystemComponent>())
        {
            const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
            
            for (UAttributeSet* AttributeSet : AttributeSets)
            {
                if (UMyHealthAttributeSet* HealthAttrs = Cast<UMyHealthAttributeSet>(AttributeSet))
                {
                    HealthBar->SetPercent(HealthAttrs->GetHealth() / HealthAttrs->GetMaxHealth());
                }
                else if (UMyStaminaAttributeSet* StaminaAttrs = Cast<UMyStaminaAttributeSet>(AttributeSet))
                {
                    StaminaBar->SetPercent(StaminaAttrs->GetStamina() / StaminaAttrs->GetMaxStamina());
                }
            }
        }
    }
}

2. 属性监听和响应

cpp 复制代码
// 监听所有属性变化
void UAttributeMonitorComponent::InitializeAttributeListeners()
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
        
        for (UAttributeSet* AttributeSet : AttributeSets)
        {
            if (UMyHealthAttributeSet* HealthAttrs = Cast<UMyHealthAttributeSet>(AttributeSet))
            {
                // 监听血量变化
                ASC->GetGameplayAttributeValueChangeDelegate(
                    UMyHealthAttributeSet::GetHealthAttribute()
                ).AddUObject(this, &UAttributeMonitorComponent::OnHealthChanged);
            }
        }
    }
}

3. 存档系统

cpp 复制代码
// 保存所有属性状态
void UGameSaveSystem::SaveAttributes(UGameSave* SaveGame, UAbilitySystemComponent* ASC)
{
    if (!SaveGame || !ASC) return;
    
    const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
    
    for (UAttributeSet* AttributeSet : AttributeSets)
    {
        if (UMyHealthAttributeSet* HealthAttrs = Cast<UMyHealthAttributeSet>(AttributeSet))
        {
            SaveGame->SavedHealth = HealthAttrs->GetHealth();
            SaveGame->SavedMaxHealth = HealthAttrs->GetMaxHealth();
        }
        else if (UMyManaAttributeSet* ManaAttrs = Cast<UMyManaAttributeSet>(AttributeSet))
        {
            SaveGame->SavedMana = ManaAttrs->GetMana();
            SaveGame->SavedMaxMana = ManaAttrs->GetMaxMana();
        }
    }
}

4. Debug和开发工具

cpp 复制代码
// 在控制台显示所有属性值
void AMyPlayerController::DebugShowAttributes()
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponent())
    {
        const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
        
        UE_LOG(LogTemp, Warning, TEXT("=== Current Attributes ==="));
        
        for (UAttributeSet* AttributeSet : AttributeSets)
        {
            FString AttributeSetName = AttributeSet->GetClass()->GetName();
            UE_LOG(LogTemp, Warning, TEXT("Attribute Set: %s"), *AttributeSetName);
            
            // 使用反射获取所有属性值
            for (TFieldIterator<FProperty> PropIt(AttributeSet->GetClass()); PropIt; ++PropIt)
            {
                FProperty* Property = *PropIt;
                if (Property->IsA<FGameplayAttribute>())
                {
                    // 显示属性名称和值...
                }
            }
        }
    }
}

5. 条件检查和验证

cpp 复制代码
// 检查是否满足技能施放条件
bool UMyGameplayAbility::CheckAttributeRequirements()
{
    if (UAbilitySystemComponent* ASC = GetAbilitySystemComponentFromActorInfo())
    {
        const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
        
        for (UAttributeSet* AttributeSet : AttributeSets)
        {
            if (UMyResourceAttributeSet* ResourceAttrs = Cast<UMyResourceAttributeSet>(AttributeSet))
            {
                // 检查法力值是否足够
                if (ResourceAttrs->GetMana() < RequiredManaCost)
                {
                    return false;
                }
                
                // 检查血量是否高于最低要求
                if (ResourceAttrs->GetHealth() < MinimumHealthThreshold)
                {
                    return false;
                }
            }
        }
    }
    
    return true;
}

6. AI决策系统

cpp 复制代码
// AI根据属性状态做出决策
ECombatBehavior UMyAIController::EvaluateCombatBehavior()
{
    if (UAbilitySystemComponent* ASC = GetPawn()->FindComponentByClass<UAbilitySystemComponent>())
    {
        const TArray<UAttributeSet*>& AttributeSets = ASC->GetSpawnedAttributes();
        
        for (UAttributeSet* AttributeSet : AttributeSets)
        {
            if (UMyHealthAttributeSet* HealthAttrs = Cast<UMyHealthAttributeSet>(AttributeSet))
            {
                float HealthPercent = HealthAttrs->GetHealth() / HealthAttrs->GetMaxHealth();
                
                if (HealthPercent < 0.3f) return ECombatBehavior::Retreat;
                if (HealthPercent < 0.6f) return ECombatBehavior::Defensive;
            }
        }
    }
    
    return ECombatBehavior::Aggressive;
}

关键优势

  1. 性能高效 - 返回引用避免数组拷贝

  2. 数据安全 - const引用防止意外修改

  3. 接口清晰 - 明确表示这是只读操作

  4. GAS集成 - 完美配合UE的Gameplay Ability System

这种设计模式在需要查看但不修改属性集的场景中非常实用,特别是在UI、存档、调试和AI系统中。

相关推荐
许长安3 小时前
RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
c++·经验分享·笔记·rpc
kyriewen113 小时前
WebAssembly:前端界的“外挂”,让C++代码在浏览器里跑起来
开发语言·前端·javascript·c++·单元测试·ecmascript
浅念-7 小时前
刷穿LeetCode:BFS 解决 Flood Fill 算法
数据结构·c++·算法·leetcode·职场和发展·bfs·宽度优先
做cv的小昊7 小时前
【TJU】研究生应用统计学课程笔记(8)——第四章 线性模型(4.1 一元线性回归分析)
笔记·线性代数·算法·数学建模·回归·线性回归·概率论
楼田莉子8 小时前
Linux网络:NAT_代理
linux·运维·服务器·开发语言·c++·后端
我命由我123458 小时前
程序员的心理学学习笔记 - 空杯心态
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
南境十里·墨染春水8 小时前
C++日志 2——实现单线程日志系统
java·jvm·c++
stm32 菜鸟8 小时前
nucleo-f411re学习记录-13,flash的操作
学习
zh_xuan8 小时前
api测试工具添加历史记录功能
c++·libcurl·duilib
晓梦林8 小时前
3170靶场学习笔记
笔记·学习