虚幻引擎UE5专用服务器游戏开发-12 添加生命与法力属性到属性集

一、添加生命与法力属性

1.添加生命属性:Source/Crunch/Public/GAS/CAttributeSet.h

private:
	UPROPERTY()
	FGameplayAttributeData Health;

FGameplayAttributeData 的定义:

Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AttributeSet.h:

USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAttributeData
{
	GENERATED_BODY()
	FGameplayAttributeData()
		: BaseValue(0.f)
		, CurrentValue(0.f)
	{}

	FGameplayAttributeData(float DefaultValue)
		: BaseValue(DefaultValue)
		, CurrentValue(DefaultValue)
	{}

	virtual ~FGameplayAttributeData()
	{}

	/** Returns the current value, which includes temporary buffs */
	float GetCurrentValue() const;

	/** Modifies current value, normally only called by ability system or during initialization */
	virtual void SetCurrentValue(float NewValue);

	/** Returns the base value which only includes permanent changes */
	float GetBaseValue() const;

	/** Modifies the permanent base value, normally only called by ability system or during initialization */
	virtual void SetBaseValue(float NewValue);

protected:
	UPROPERTY(BlueprintReadOnly, Category = "Attribute")
	float BaseValue;

	UPROPERTY(BlueprintReadOnly, Category = "Attribute")
	float CurrentValue;
};

FGameplayAttributeData是虚幻引擎GAS(GameplayAbilitySystem)中用于表示游戏属性的核心数据结构。该结构体具有以下关键特性:

双值存储机制:

  • BaseValue:基础值,仅包含永久性修改(如角色初始属性或装备加成)
  • CurrentValue:当前值,包含临时buff/debuff效果

主要功能方法:

  • GetCurrentValue()/SetCurrentValue():获取/设置包含临时效果的当前值
  • GetBaseValue()/SetBaseValue():获取/设置永久性基础值
  • 两个构造函数:支持默认初始化为0或指定默认值

设计特点:

  • 通过UPROPERTY宏实现蓝图可读性
  • 推荐通过GameplayEffects修改属性以实现网络同步和预测回滚
  • 通常定义在AttributeSet类中并由其处理网络同步

实际应用示例:

  • 移动速度属性(MoveSpeed)的实现会使用此结构体
  • 伤害(Damage)和护甲(Armor)等战斗属性也采用相同结构
  • 装备系统可通过修改BaseValue实现永久属性加成

2.生成getter和setter函数,不过,有专门的宏函数:

Plugins/Runtime/GameplayAbilities/Source/GameplayAbilities/Public/AttributeSet.h:


 * To use this in your game you can define something like this, and then add game-specific functions as necessary:
 * 
 *	#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
 *	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
 *	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
 * 
 *	ATTRIBUTE_ACCESSORS(UMyHealthSet, Health)
 */

#define GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
	static FGameplayAttribute Get##PropertyName##Attribute() \
	{ \
		static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
		return Prop; \
	}

#define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
	FORCEINLINE float Get##PropertyName() const \
	{ \
		return PropertyName.GetCurrentValue(); \
	}

#define GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
	FORCEINLINE void Set##PropertyName(float NewVal) \
	{ \
		UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); \
		if (ensure(AbilityComp)) \
		{ \
			AbilityComp->SetNumericAttributeBase(Get##PropertyName##Attribute(), NewVal); \
		}; \
	}

#define GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
	FORCEINLINE void Init##PropertyName(float NewVal) \
	{ \
		PropertyName.SetBaseValue(NewVal); \
		PropertyName.SetCurrentValue(NewVal); \
	}

这是Unreal Engine游戏能力系统(GAS)中用于简化属性访问的宏定义系统,主要用于定义和管理游戏属性(如生命值、法力值等)。以下是关键点解析:

ATTRIBUTE_ACCESSORS宏‌:

  • 组合了4个基础功能宏,提供完整的属性访问能力
  • 示例用法:ATTRIBUTE_ACCESSORS(UMyHealthSet, Health)会生成Health属性的全套方法

组成宏的功能‌:

  • GAMEPLAYATTRIBUTE_PROPERTY_GETTER:生成获取属性元数据的静态方法
  • GAMEPLAYATTRIBUTE_VALUE_GETTER:生成获取当前属性值的inline方法
  • GAMEPLAYATTRIBUTE_VALUE_SETTER:生成通过AbilitySystem修改属性值的方法
  • GAMEPLAYATTRIBUTE_VALUE_INITTER:生成初始化属性值的方法

实现特点‌:

  • 使用##进行token拼接,动态生成方法名
  • 通过FindFieldChecked获取UProperty反射信息
  • 所有修改操作都通过AbilitySystemComponent进行

典型应用场景‌:

  • 在继承自UAttributeSet的类中使用
  • 需要配合GameplayAbilitySystem组件工作
  • 适用于需要网络同步的游戏属性

注意事项‌:

  • 属性变量需要定义为FGameplayAttributeData类型
  • Setter操作会触发属性变化事件
  • 确保AbilitySystemComponent有效才能进行修改操作

这套宏系统是UE GAS的标准实践,可以大幅减少属性访问的样板代码量。

所以,只要把宏定义粘贴进来就好:

Source/Crunch/Public/GAS/CAttributeSet.h:

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
public:
	
	ATTRIBUTE_ACCESSORS(UCAttributeSet, Health);

二、创建GameplayEffect

1.创建GameplayEffect蓝图类:

2.修改最大生命值、最大法力值:

3.怎么实际运用修改好的值呢?

在Source/Crunch/Public/GAS/CAbilitySystemComponent.h中:

创建子类:

private:

	TArray<TSubclassOf<UGameplayEffect>> InitialEffects;	//游戏效果子类数组:需要初始化的效果

建立一个函数:

public:
	//需要初始化所有值时调用
	void ApplyInitialEffects();

实现:

(1)循环遍历数组:

for (const TSubclassOf<UGameplayEffect>& EffectClass : InitialEffects)

(2)获取空间:

FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingSpec(EffectClass, 1.f, MakeEffectContext());

这段代码是虚幻引擎GAS框架中创建GameplayEffectSpec的标准用法,主要功能是生成一个游戏效果规格实例的句柄。具体解析如下:

核心参数说明:

  • EffectClass:要实例化的UGameplayEffect类引用
  • 1.f:效果等级(Level)参数,这里设为1级
  • MakeEffectContext():创建空的FGameplayEffectContext上下文对象

执行流程:

  • 首先通过MakeEffectContext()创建效果上下文容器
  • 然后使用MakeOutgoingSpec生成FGameplayEffectSpec运行时实例
  • 最终返回FGameplayEffectSpecHandle智能指针封装

典型应用场景:

  • 技能释放时创建临时效果实例
  • 状态效果(如中毒、眩晕)的运行时生成
  • 属性修改(伤害/治疗)的中间载体

后续操作建议:

  • 可通过EffectSpecHandle.Data访问实际Spec对象
  • 通常配合ApplyGameplayEffectSpecToSelf等函数应用效果
  • 可动态修改Spec的Level、SetByCaller等参数

(3)对自己释放效果

ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());

ApplyGameplayEffectSpecToSelf是GAS框架中用于将GameplayEffectSpec应用到当前AbilitySystemComponent的核心函数,其关键特性如下:

核心参数解析:

  • GameplayEffect参数:传入已构建的FGameplayEffectSpec运行时实例
  • PredictionKey参数:默认构造的预测键,用于客户端预测同步

函数特性:

  • 返回FActiveGameplayEffectHandle用于管理激活的效果实例
  • virtual修饰允许子类重写应用逻辑
  • const引用传递避免数据拷贝

内部处理流程:

  • 通过ActiveGameplayEffectsContainer管理效果生命周期
  • 区分即时(Instant)/持续(Duration)/永久(Infinite)效果类型处理
  • 执行Modifier运算和Execution计算

典型应用场景:

  • 技能释放后应用效果到自身
  • 状态效果(如BUFF/DEBUFF)的自我施加
  • 属性修改效果的链式调用

注意事项:

  • 需确保传入的Spec已正确设置Level和Context
  • 预测键需在客户端预测场景下正确设置
  • 返回的Handle可用于后续效果管理

源码:

Source/Crunch/Public/GAS/CAbilitySystemComponent.h:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "CAbilitySystemComponent.generated.h"

/**
 * 
 */
UCLASS()
class CRUNCH_API UCAbilitySystemComponent : public UAbilitySystemComponent
{
	GENERATED_BODY()

public:
	//需要初始化所有值时调用
	void ApplyInitialEffects();


private:

	TArray<TSubclassOf<UGameplayEffect>> InitialEffects;	//游戏效果子类数组:需要初始化的效果
	
};

Source/Crunch/Private/GAS/CAbilitySystemComponent.cpp:

// Copyright@ChenChao


#include "GAS/CAbilitySystemComponent.h"

void UCAbilitySystemComponent::ApplyInitialEffects()
{
	for (const TSubclassOf<UGameplayEffect>& EffectClass : InitialEffects)
	{
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingSpec(EffectClass, 1.f, MakeEffectContext());
		ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
	}
	
}

这段代码实现了GAS框架中AbilitySystemComponent的初始效果应用逻辑,主要功能解析如下:

核心流程:

  • 遍历InitialEffects数组中的每个GameplayEffect类
  • 通过MakeOutgoingSpec创建效果规格(Spec)
  • 使用ApplyGameplayEffectSpecToSelf将效果应用到当前ASC

三、 搭建服务端、客户端调用链

1.为了防止作弊,上述代码只允许在服务器端运行,所以需要加上判定条件:

Source/Crunch/Private/GAS/CAbilitySystemComponent.cpp:

// Copyright@ChenChao


#include "GAS/CAbilitySystemComponent.h"

void UCAbilitySystemComponent::ApplyInitialEffects()
{
	if (!GetOwner() || !GetOwner()->HasAuthority()) return;
	
	for (const TSubclassOf<UGameplayEffect>& EffectClass : InitialEffects)
	{
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingSpec(EffectClass, 1.f, MakeEffectContext());
		ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
	}
}

2.第二个防止作弊的方法是:只在服务器端调用ApplyInitialEffects()

(1)我们在角色类里定义两个函数,分别在服务器初始化、客户端初始化:

	void ServerSideInit();		//服务器端初始化
	void ClientSideInit();		//客户端初始化
void ACCharacter::ServerSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
	CAbilitySystemComponent->ApplyInitialEffects();		//效果初始化
}
void ACCharacter::ClientSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
}

(2)我们定义了方法,现在要到只有服务器调用的方法里和只有客户端(或者监听服务器)调用的方法里去实现他们:

到角色控制器里(Source/Crunch/Public/Player/CPlayerController.h)

重写函数:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "CPlayerController.generated.h"

class ACPlayerCharacter;
/**
 * 
 */
UCLASS()
class CRUNCH_API ACPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	// 只有服务器调用的方法
	virtual void OnPossess(APawn* InPawn) override;
	// 只有客户端(或者监听服务器)调用的方法
	virtual void AcknowledgePossession(class APawn* P) override;

private:
	UPROPERTY()
	TObjectPtr<ACPlayerCharacter> CPlayerCharacter;
};

Source/Crunch/Private/Player/CPlayerController.cpp:

// Copyright@ChenChao


#include "Player/CPlayerController.h"

#include "Player/CPlayerCharacter.h"

void ACPlayerController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);
	CPlayerCharacter = Cast<ACPlayerCharacter>(InPawn);
	if (CPlayerCharacter)
	{
		CPlayerCharacter->ServerSideInit();
	}
}

void ACPlayerController::AcknowledgePossession(class APawn* P)
{
	Super::AcknowledgePossession(P);
	CPlayerCharacter = Cast<ACPlayerCharacter>(P);
	if (CPlayerCharacter)
	{
		CPlayerCharacter->ClientSideInit();
	}
}

PS:为了在蓝图默认里修改初始值,需要修改:

Source/Crunch/Public/GAS/CAbilitySystemComponent.h:

private:
	UPROPERTY(EditDefaultsOnly, Category="Gameplay Effects")
	TArray<TSubclassOf<UGameplayEffect>> InitialEffects;	//游戏效果子类数组:需要初始化的效果
	

顺便把之前的改掉:

Source/Crunch/Public/Character/CCharacter.h:

private:
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAbilitySystemComponent> CAbilitySystemComponent;
	
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAttributeSet> CAttributeSet;

四、应用游戏效果并复制属性

1.在角色蓝图中设置:

这样就可以将GE_Init应用到能力系统组件了!

运行调试模式,按一下~,输入showdebug abilitysystem。看看:

哪里出错了?

到GE_Init里看看:

原来持续时间,没有改成永久:

好了:

2.创建同用的游戏效果:GE_FullStat

效果:实现状态的回满

到角色蓝图里,将游戏效果添加上:

 以单机的形式,运行游戏:

3.但是,我如果以客户端的形式,运行游戏。效果就不会出现:

这是怎么回事呢?

GE_Init执行了,但是GE_FullStat没有执行

可能是5.5引擎版本上,永久效果拥有服务器权限,可以复制更新,立即效果,没有服务器效果,复制更新不了:

void UCAbilitySystemComponent::ApplyInitialEffects()
{
	if (!GetOwner() || !GetOwner()->HasAuthority()) return;
	
	for (const TSubclassOf<UGameplayEffect>& EffectClass : InitialEffects)
	{
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingSpec(EffectClass, 1.f, MakeEffectContext());
		ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
	}
}

方法:将属性值添加上可复制!

4.属性添加复制

Source/Crunch/Public/GAS/CAttributeSet.h:

(1)变量属性添加可复制:ReplicatedUsing = OnRep_Health

private:
	UPROPERTY(ReplicatedUsing = OnRep_Health)
	FGameplayAttributeData Health;		//生命值

这段代码是Unreal Engine中GAS(游戏技能系统)定义可复制属性的标准写法,用于声明一个需要网络同步的生命值属性。关键要素解析如下:

属性声明‌:

  • FGameplayAttributeData是GAS专用的属性容器,包含BaseValueCurrentValue两个浮点值
  • ReplicatedUsing指定属性变化时的回调函数OnRep_Health

配套实现要求‌:

  • 需在头文件使用ATTRIBUTE_ACCESSORS宏生成配套访问器
  • 必须实现OnRep_Health回调函数处理客户端同步
  • 需在GetLifetimeReplicatedProps中注册复制属性

典型应用场景‌:

  • 基础生命值存储在BaseValue
  • 临时buff/debuff影响CurrentValue
  • 通过PreAttributeChange实现数值范围限制(如0-MaxHealth)

(2)写上OnRep_Health函数:

	UFUNCTION()
	void OnRep_Health(const FGameplayAttributeData& OldValue);
void UCAttributeSet::OnRep_Health(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, Health, OldValue);	//通知属性复制
}

这段代码是Unreal Engine中GAS(游戏技能系统)架构下处理属性网络复制的标准实现,主要用于Health属性在客户端接收更新时的回调处理。以下是关键点解析:

核心功能‌:

  • OnRep_Health是网络复制的回调函数,当服务器同步Health属性到客户端时自动触发
  • GAMEPLAYATTRIBUTE_REPNOTIFY宏是GAS系统的标准通知机制,会处理属性预测和UI更新等逻辑

配套实现要求‌:

  • 需在头文件声明UFUNCTION()修饰符和回调函数原型
  • 必须与GetLifetimeReplicatedProps中的DOREPLIFETIME_CONDITION_NOTIFY配置对应
  • 建议配合PreAttributeChange实现数值范围限制(如生命值不低于0)

设计规范‌:

  • 属性定义需使用ATTRIBUTE_ACCESSORS宏生成配套的Getter/Setter
  • 对于重要属性,应在PostGameplayEffectExecute中处理业务逻辑(如角色死亡)
  • 元属性(如伤害值)通常不需要复制,仅在服务器计算

完整实现还应包含属性元数据配置和效果处理,这是GAS属性系统的标准实践。

 

(3)在此之前,要重写一个函数GetLifetimeReplicatedProps,来规范复制的方式:

	//~Begin override
	virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
	//~End override
void UCAttributeSet::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, Health, COND_None, REPNOTIFY_Always);	//通知复制实时更新
}

这段代码是Unreal Engine中AttributeSet类的网络复制配置实现,主要用于同步游戏属性到客户端。具体分析如下:

函数作用‌:

  • GetLifetimeReplicatedProps是UE网络复制系统的核心函数,用于声明需要同步的属性
  • 通过DOREPLIFETIME_CONDITION_NOTIFY宏将Health属性标记为需要网络复制

参数说明‌:

  • COND_None表示无条件复制
  • REPNOTIFY_Always确保属性变化时始终触发复制通知
  • 这种配置适合需要实时同步的关键属性(如生命值)

配套实现‌:

  • 通常需要配合OnRep_Health函数处理客户端接收到的属性更新
  • 建议在属性变化时使用GAMEPLAYATTRIBUTE_REPNOTIFY宏确保正确的网络同步

设计规范‌:

  • AttributeSet应当在OwnerActor构造函数中创建并自动注册到ASC
  • 对于重要属性,建议在PreAttributeChange中实现数值范围限制

完整实现还应包含属性变化回调和处理逻辑,这是GAS(游戏技能系统)架构的标准实践。

效果:

源码:

Source/Crunch/Public/GAS/CAttributeSet.h:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "CAttributeSet.generated.h"

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
	GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

/**
 * 
 */
UCLASS()
class CRUNCH_API UCAttributeSet : public UAttributeSet
{
	GENERATED_BODY()
public:
	
	ATTRIBUTE_ACCESSORS(UCAttributeSet, Health);
	ATTRIBUTE_ACCESSORS(UCAttributeSet, MaxHealth);
	
	ATTRIBUTE_ACCESSORS(UCAttributeSet, Mana);
	ATTRIBUTE_ACCESSORS(UCAttributeSet, MaxMana);

	//~Begin override
	virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
	//~End override
	
private:
	UPROPERTY(ReplicatedUsing = OnRep_Health)
	FGameplayAttributeData Health;		//生命值
	UPROPERTY(ReplicatedUsing = OnRep_MaxHealth)
	FGameplayAttributeData MaxHealth;		//最大生命值
	
	UPROPERTY(ReplicatedUsing = OnRep_Mana)
	FGameplayAttributeData Mana;		//魔法值
	UPROPERTY(ReplicatedUsing = OnRep_MaxMana)
	FGameplayAttributeData MaxMana;		//最大魔法值


	UFUNCTION()
	void OnRep_Health(const FGameplayAttributeData& OldValue);

	UFUNCTION()
	void OnRep_MaxHealth(const FGameplayAttributeData& OldValue);

	UFUNCTION()
	void OnRep_Mana(const FGameplayAttributeData& OldValue);

	UFUNCTION()
	void OnRep_MaxMana(const FGameplayAttributeData& OldValue);

	
};

Source/Crunch/Private/GAS/CAttributeSet.cpp:

// Copyright@ChenChao


#include "GAS/CAttributeSet.h"

#include "Net/UnrealNetwork.h"

void UCAttributeSet::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
	DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, Health, COND_None, REPNOTIFY_Always);	//通知复制实时更新
	DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);	//通知复制实时更新
	DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, Mana, COND_None, REPNOTIFY_Always);	//通知复制实时更新
	DOREPLIFETIME_CONDITION_NOTIFY(UCAttributeSet, MaxMana, COND_None, REPNOTIFY_Always);	//通知复制实时更新
}

void UCAttributeSet::OnRep_Health(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, Health, OldValue);	//通知属性复制
}

void UCAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, MaxHealth, OldValue);	//通知属性复制
}

void UCAttributeSet::OnRep_Mana(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, Mana, OldValue);	//通知属性复制
}

void UCAttributeSet::OnRep_MaxMana(const FGameplayAttributeData& OldValue)
{
	GAMEPLAYATTRIBUTE_REPNOTIFY(UCAttributeSet, MaxMana, OldValue);	//通知属性复制
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值