中文注释:UrealEngine-5.2.1源码-AbilitySystemComponent.h
学习内容:
cpp
/** 设置属性的基础值。不清除现有的活动修饰符,它们将对新的基础值起作用。 */
void SetNumericAttributeBase(const FGameplayAttribute &Attribute, float NewBaseValue);
这个方法是 Gameplay Ability System(GAS) 中用于设置属性基础值的重要函数。让我详细解释一下它的作用和使用场景:
功能说明
SetNumericAttributeBase 用于直接设置属性的基础值,而不会影响当前正在生效的任何修饰符(Modifiers)。
参数说明
-
Attribute: 要设置的游戏属性(如生命值、法力值等) -
NewBaseValue: 新的基础值
关键特点
1. 不影响活动修饰符
cpp
// 示例:设置生命值基础值
UAttributeSet* AttributeSet = GetAttributeSet();
AttributeSet->SetNumericAttributeBase(UAttributeSet::GetHealthAttribute(), 100.0f);
// 假设当前有 +20 的修饰符生效
// 设置前:基础值 80 + 修饰符 20 = 当前值 100
// 设置后:基础值 100 + 修饰符 20 = 当前值 120
2. 与 PreAttributeChange 的区别
cpp
// SetNumericAttributeBase - 直接设置基础值
void YourAttributeSet::SetNumericAttributeBase(const FGameplayAttribute& Attribute, float NewBaseValue)
{
// 这是引擎内部实现,直接修改基础值
}
// PreAttributeChange - 在属性变化前被调用,用于验证和限制
void YourAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
// 这里可以添加验证逻辑
if (Attribute == GetHealthAttribute())
{
NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
}
}
使用场景
1. 初始化属性
cpp
void AYourCharacter::InitializeAttributes()
{
if (DefaultAttributeEffect && AttributeSet)
{
// 应用初始属性
AttributeSet->SetNumericAttributeBase(UAttributeSet::GetHealthAttribute(), 100.0f);
AttributeSet->SetNumericAttributeBase(UAttributeSet::GetMaxHealthAttribute(), 100.0f);
AttributeSet->SetNumericAttributeBase(UAttributeSet::GetManaAttribute(), 50.0f);
}
}
2. 重置基础值
cpp
void UYourAttributeSet::OnRep_Level(const FGameplayAttributeData& OldLevel)
{
// 等级变化时重新计算基础属性
float NewBaseHealth = CalculateBaseHealthFromLevel();
SetNumericAttributeBase(GetHealthAttribute(), NewBaseHealth);
}
3. 与修饰符协同工作
cpp
// 假设当前状态:
// 基础生命值: 100
// 装备加成: +20 (修饰符)
// 当前生命值: 120
// 使用 SetNumericAttributeBase 后:
SetNumericAttributeBase(GetHealthAttribute(), 150.0f);
// 结果:
// 基础生命值: 150
// 装备加成: +20 (修饰符仍然存在)
// 当前生命值: 170
注意事项
-
不会触发 Gameplay Effects:这个方法直接修改基础值,不会触发普通的属性修改流程
-
需要手动通知:修改后可能需要手动触发属性变化通知
cppAttributeSet->SetNumericAttributeBase(GetHealthAttribute(), NewValue); AttributeSet->OnHealthChanged.Broadcast(NewValue); // 手动广播事件 -
谨慎使用:由于绕过正常的修改流程,应在明确知道需要直接修改基础值时使用
这个方法在需要直接控制属性基础值而不影响现有修饰符的场景下非常有用,特别是在属性初始化、等级重置或特殊系统交互时。
在大型网络游戏中,SetNumericAttributeBase 的应用需要特别考虑网络同步、性能优化和架构设计。以下是具体应用场景和最佳实践:
1. 玩家属性初始化系统
分布式初始化流程
cpp
// 在玩家控制器或游戏模式中
void AYourPlayerController::OnPlayerInitialized()
{
if (GetLocalRole() == ROLE_Authority)
{
// 服务器初始化基础属性
InitializeBaseAttributes();
// 同步到所有客户端
ClientInitializeAttributes(GetInitialAttributeData());
}
}
// 服务器端初始化
void AYourPlayerController::InitializeBaseAttributes()
{
UYourAttributeSet* AttributeSet = GetPawn()->FindComponentByClass<UYourAttributeSet>();
if (AttributeSet)
{
FInitialAttributeData InitData = CalculateInitialAttributes();
AttributeSet->SetNumericAttributeBase(UYourAttributeSet::GetHealthAttribute(), InitData.Health);
AttributeSet->SetNumericAttributeBase(UYourAttributeSet::GetManaAttribute(), InitData.Mana);
AttributeSet->SetNumericAttributeBase(UYourAttributeSet::GetStaminaAttribute(), InitData.Stamina);
// 保存初始值用于重置
CachedBaseAttributes = InitData;
}
}
// 客户端同步
UFUNCTION(Client, Reliable)
void AYourPlayerController::ClientInitializeAttributes(const FInitialAttributeData& AttributeData);
2. 等级和职业系统
批量属性更新
cpp
// 等级提升时的属性重计算
void UPlayerLevelSystem::OnLevelUp(int32 NewLevel)
{
if (GetOwnerRole() != ROLE_Authority) return;
UYourAttributeSet* AttributeSet = GetAttributeSet();
FLevelAttributeData LevelData = LevelConfig->GetAttributesForLevel(NewLevel);
// 批量更新基础属性,避免多次网络同步
BatchUpdateBaseAttributes(LevelData);
}
void UPlayerLevelSystem::BatchUpdateBaseAttributes(const FLevelAttributeData& LevelData)
{
UYourAttributeSet* AttributeSet = GetAttributeSet();
// 开始批量更新
AttributeSet->PreBatchUpdate();
AttributeSet->SetNumericAttributeBase(GetHealthAttribute(), LevelData.BaseHealth);
AttributeSet->SetNumericAttributeBase(GetManaAttribute(), LevelData.BaseMana);
AttributeSet->SetNumericAttributeBase(GetAttackAttribute(), LevelData.BaseAttack);
AttributeSet->SetNumericAttributeBase(GetDefenseAttribute(), LevelData.BaseDefense);
// 结束批量更新,触发一次同步
AttributeSet->PostBatchUpdate();
// 记录属性变化日志(用于审计和调试)
LogAttributeChange(GetPlayerId(), "LevelUp", LevelData);
}
3. 装备和技能系统
基础属性重算机制
cpp
// 装备系统
void UEquipmentSystem::RecalculateBaseAttributes()
{
if (GetOwnerRole() != ROLE_Authority) return;
UYourAttributeSet* AttributeSet = GetAttributeSet();
FBaseAttributeCalculator Calculator;
// 计算来自所有装备的基础属性
FCalculatedAttributes NewBases = Calculator.CalculateBaseAttributes(
GetEquippedItems(),
GetPlayerLevel(),
GetPlayerClass()
);
// 更新基础值,保持当前修饰符不变
AttributeSet->SetNumericAttributeBase(GetHealthAttribute(), NewBases.Health);
AttributeSet->SetNumericAttributeBase(GetManaAttribute(), NewBases.Mana);
// 通知客户端基础属性变化
Multicast_OnBaseAttributesUpdated(NewBases);
}
4. 副本和场景切换
状态保存和恢复
cpp
// 进入副本时的属性处理
void UDungeonSystem::OnEnterDungeon(ADungeonInstance* Dungeon)
{
// 保存原始基础属性
SaveOriginalBaseAttributes();
// 应用副本特定的基础属性调整
ApplyDungeonBaseAttributeModifiers(Dungeon);
}
void UDungeonSystem::ApplyDungeonBaseAttributeModifiers(ADungeonInstance* Dungeon)
{
UYourAttributeSet* AttributeSet = GetAttributeSet();
FDungeonAttributeRules Rules = Dungeon->GetAttributeRules();
// 根据副本规则调整基础属性
float NewBaseHealth = AttributeSet->GetHealthBaseValue() * Rules.HealthMultiplier;
AttributeSet->SetNumericAttributeBase(GetHealthAttribute(), NewBaseHealth);
// 记录修改用于退出时恢复
DungeonAttributeModifiers = Rules;
}
// 退出副本时恢复
void UDungeonSystem::OnExitDungeon()
{
RestoreOriginalBaseAttributes();
}
5. 网络优化策略
批量同步和压缩
cpp
// 优化的属性同步组件
void UOptimizedAttributeSyncComponent::ServerUpdateBaseAttributes(
const TArray<FAttributeBaseUpdate>& Updates)
{
if (GetOwnerRole() != ROLE_Authority) return;
// 验证更新请求
if (!ValidateAttributeUpdates(Updates)) return;
UYourAttributeSet* AttributeSet = GetAttributeSet();
// 应用批量更新
for (const FAttributeBaseUpdate& Update : Updates)
{
AttributeSet->SetNumericAttributeBase(Update.Attribute, Update.NewBaseValue);
}
// 压缩并同步到客户端
FCompressedAttributeData CompressedData = CompressAttributeData(Updates);
Multicast_SyncBaseAttributes(CompressedData);
// 记录到审计系统
AuditSystem->LogAttributeBaseUpdates(GetPlayerId(), Updates);
}
UFUNCTION(NetMulticast, Reliable)
void UOptimizedAttributeSyncComponent::Multicast_SyncBaseAttributes(
const FCompressedAttributeData& CompressedData);
6. 反作弊和安全考虑
服务器验证机制
cpp
// 属性修改验证
bool UAttributeSecuritySystem::ValidateBaseAttributeChange(
const FGameplayAttribute& Attribute,
float ProposedValue,
APlayerController* Requestor)
{
// 1. 检查权限
if (!Requestor->HasAuthorityToModifyAttribute(Attribute))
{
LogSecurityViolation(Requestor, "Unauthorized attribute modification");
return false;
}
// 2. 检查数值合理性
if (!IsValueWithinReasonableRange(Attribute, ProposedValue))
{
LogSecurityViolation(Requestor, "Attribute value out of range");
return false;
}
// 3. 检查修改频率
if (IsChangeRateTooHigh(Attribute, Requestor))
{
LogSecurityViolation(Requestor, "Attribute change rate too high");
return false;
}
return true;
}
// 包装的安全设置方法
void UAttributeSecuritySystem::SafeSetNumericAttributeBase(
const FGameplayAttribute& Attribute,
float NewBaseValue,
APlayerController* Requestor)
{
if (ValidateBaseAttributeChange(Attribute, NewBaseValue, Requestor))
{
GetAttributeSet()->SetNumericAttributeBase(Attribute, NewBaseValue);
// 记录合法修改
AuditSystem->LogValidAttributeChange(Requestor, Attribute, NewBaseValue);
}
}
7. 性能监控和调试
属性变化追踪
cpp
// 性能监控
void UAttributePerformanceMonitor::TrackBaseAttributeUpdate(
const FGameplayAttribute& Attribute,
float OldValue,
float NewValue)
{
FAttributeUpdateEvent Event;
Event.Attribute = Attribute;
Event.OldValue = OldValue;
Event.NewValue = NewValue;
Event.Timestamp = FDateTime::UtcNow();
Event.CallStack = CaptureCallStack();
// 记录到性能数据库
PerformanceDB->RecordAttributeUpdate(Event);
// 检查性能异常
if (DetectPerformanceAnomaly(Event))
{
AlertSystem->NotifyAttributePerformanceIssue(Event);
}
}
在大型网络游戏中,SetNumericAttributeBase 的正确使用需要结合:
-
网络同步优化:减少不必要的属性同步
-
安全验证:防止客户端作弊
-
性能监控:确保大规模玩家时的性能
-
事务性操作:保证属性修改的原子性
-
审计日志:追踪所有属性修改记录