92. UE5 GAS RPG 使用C++创建GE实现灼烧的负面效果

news2024/12/23 23:16:27

在正常游戏里,有些伤害技能会携带一些负面效果,比如火焰伤害的技能会携带燃烧效果,敌人在受到伤害后,会接受一个燃烧的效果,燃烧效果会在敌人身上持续一段时间,并且持续受到火焰灼烧。
我们将在这一篇文章里,实现伤害技能附带负面效果,并可以设置负面效果的参数,来实现对敌人添加负面buff

添加负面效果标签

首先我们添加对应的伤害类型的负面标签

	FGameplayTag DeBuff_Burn; //火属性负面效果 燃烧
	FGameplayTag DeBuff_Stun; //雷属性负面效果 眩晕
	FGameplayTag DeBuff_Arcane; //魔法伤害负面效果
	FGameplayTag DeBuff_Physical; //物理伤害负面效果 流血

并且添加一个Map,用于负面效果标签和属性抵抗表情对应,抵抗可以用于降低负面效用的成功率

TMap<FGameplayTag, FGameplayTag> DeBuffsToResistance; //属性伤害标签对应负面标签

将标签注册的标签管理器

	/*
	 * 负面标签注册
	*/
	GameplayTags.DeBuff_Burn = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Burn"),
			FString("火属性燃烧负面标签")
			);
	GameplayTags.DeBuff_Stun = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Stun"),
			FString("雷属性眩晕负面标签")
			);
	GameplayTags.DeBuff_Arcane = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Arcane"),
			FString("魔法属性负面标签")
			);
	GameplayTags.DeBuff_Physical = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Physical"),
			FString("物理属性流血负面标签")
			);

我们在应用负面效果时,目标角色可以通过自身的对应类型的抵抗来降低负面效果应用的成功率,所以,我们需要一个对应的Map

	/*
	 * 负面标签和属性抵抗标签对于对应
	*/
	GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Burn, GameplayTags.Attributes_Resistance_Fire);
	GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Stun, GameplayTags.Attributes_Resistance_Lightning);
	GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Arcane, GameplayTags.Attributes_Resistance_Arcane);
	GameplayTags.DeBuffsToResistance.Add(GameplayTags.DeBuff_Physical, GameplayTags.Attributes_Resistance_Physical);

添加负面效果配置项

如果我们需要添加一些负面效果相关的配置项,然后使用Set By Caller 的方式去设置,我们先在RPGDamageGameplayAbility.h里添加对负面效果的配置项

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Damage")
	FGameplayTag DeBuffDamageType = FGameplayTag(); //负面效果伤害类型
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Damage")
	float DeBuffChance = 20.f; //触发负面的机率
	
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Damage")
	float DeBuffDamage = 5.f; //负面伤害

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Damage")
	float DeBuffFrequency = 1.f; //负面伤害触发间隔时间

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Damage")
	float DeBuffDuration = 5.f; //负面效果持续时间

我们要通过Set ByCaller设置负面效果GE的属性,那么,我们选择使用标签,这样不会出错
接着,我们创建四个对应的标签

	FGameplayTag DeBuff_Chance; //负面效果触发几率标签
	FGameplayTag DeBuff_Damage; //负面效果伤害标签
	FGameplayTag DeBuff_Duration; //负面效果持续时间标签
	FGameplayTag DeBuff_Frequency; //负面效果触发间隔标签

然后注册

	/*
	 * 负面效果配置标签
	*/
	GameplayTags.DeBuff_Chance = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Chance"),
			FString("负面效果 触发几率")
			);
	GameplayTags.DeBuff_Damage = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Damage"),
			FString("负面效果 伤害")
			);
	GameplayTags.DeBuff_Duration = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Duration"),
			FString("负面效果 持续时间")
			);
	GameplayTags.DeBuff_Frequency = UGameplayTagsManager::Get()
		.AddNativeGameplayTag(
			FName("DeBuff.Frequency"),
			FString("负面效果 触发间隔")
			);

创建负面效果使用的结构体

接下来,我们创建一个结构体,用于在给目标应用负面效果时使用,由于这个结构体的数据需要序列化以后,传输到服务器端进行处理,所以,我们将其设置到RPGAbilityTypes.h文件内,之前我们创建它是为了在代码内创建GE句柄时,能够使用我们自定义的结构体,增加了暴击和格挡的数据。
我们在里面创建一个结构体,用来配置应用一个负面效果时,所需要的所有数据

USTRUCT(BlueprintType)
struct FDamageEffectParams
{
	GENERATED_BODY()

	FDamageEffectParams(){}

	UPROPERTY()
	TObjectPtr<UObject> WorldContextObject = nullptr; //当前场景上下文对象

	UPROPERTY()
	TSubclassOf<UGameplayEffect> DamageGameplayEffectClass = nullptr; //需要应用的GE的类

	UPROPERTY()
	TObjectPtr<UAbilitySystemComponent> SourceAbilitySystemComponent; //源ASC

	UPROPERTY()
	TObjectPtr<UAbilitySystemComponent> TargetAbilitySystemComponent; //目标ASC

	UPROPERTY()
	TMap<FGameplayTag, float> DamageTypes; //技能造成的多种伤害和伤害类型

	UPROPERTY()
	float AbilityLevel = 1.f; //技能等级

	UPROPERTY()
	FGameplayTag DeBuffDamageType = FGameplayTag(); //负面效果伤害类型

	UPROPERTY()
	float DeBuffChance = 0.f; //触发负面效果概率

	UPROPERTY()
	float DeBuffDamage = 0.f; //负面效果伤害

	UPROPERTY()
	float DeBuffDuration = 0.f; //负面效果持续时间

	UPROPERTY()
	float DeBuffFrequency = 0.f; //负面效果触发频率
};

在伤害技能基础类增加一个创建配置项的函数

有了配置项的结构体,我们需要实现一个函数,在伤害技能的基类上,通过伤害技能的配置生成结构体

	//创建技能负面效果使用的结构体
	FDamageEffectParams MakeDamageEffectParamsFromClassDefaults(AActor* TargetActor = nullptr);

然后实现通过技能上的配置生成配置项

FDamageEffectParams URPGDamageGameplayAbility::MakeDamageEffectParamsFromClassDefaults(AActor* TargetActor)
{
	FDamageEffectParams Params;
	Params.WorldContextObject = GetAvatarActorFromActorInfo();
	Params.DamageGameplayEffectClass = DamageEffectClass;
	Params.SourceAbilitySystemComponent = GetAbilitySystemComponentFromActorInfo();
	Params.TargetAbilitySystemComponent = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
	for(auto& Pair : DamageTypes)
	{
		const float ScaledDamage = Pair.Value.GetValueAtLevel(GetAbilityLevel()); //根据等级获取技能伤害
		Params.DamageTypes.Add(Pair.Key, ScaledDamage);
	}
	Params.AbilityLevel = GetAbilityLevel();
	Params.DeBuffDamageType = DeBuffDamageType;
	Params.DeBuffChance = DeBuffChance;
	Params.DeBuffDamage = DeBuffDamage;
	Params.DeBuffDuration = DeBuffDuration;
	Params.DeBuffFrequency = DeBuffFrequency;
	return Params;
}

在函数库添加一个通过配置项实现GE的应用

我们有了配置项,可以将应用设置为通用的函数,所以我们在函数库里增加一个静态函数,只要结构体配置完全,我们可以直接调用此函数完成内部逻辑
我们添加一个函数,传入参数就是配置项

	//通过技能生成的负面配置项应用技能负面效果
	UFUNCTION(BlueprintCallable, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static FGameplayEffectContextHandle ApplyDamageEffect(const FDamageEffectParams& DamageEffectParams);

然后实现此函数,在函数内存创建GE的上下文和实例,并通过标签的SetByCaller设置GE所使用的值,最后应用到目标ASC上,并返回GE的上下文句柄。
注意,这里我们设置伤害的标签是属性伤害类型标签,在应用时,我们就可以负面效果类型获取当前GE是否设置了对应的负面效果

FGameplayEffectContextHandle URPGAbilitySystemBlueprintLibrary::ApplyDamageEffect(const FDamageEffectParams& DamageEffectParams)
{
	const FRPGGameplayTags& GameplayTags = FRPGGameplayTags::Get();
	const AActor* SourceAvatarActor = DamageEffectParams.SourceAbilitySystemComponent->GetAvatarActor();

	//创建GE的上下文句柄
	FGameplayEffectContextHandle EffectContextHandle = DamageEffectParams.SourceAbilitySystemComponent->MakeEffectContext();
	EffectContextHandle.AddSourceObject(SourceAvatarActor);

	//根据句柄和类创建GE实例
	const FGameplayEffectSpecHandle SpecHandle = DamageEffectParams.SourceAbilitySystemComponent->MakeOutgoingSpec(DamageEffectParams.DamageGameplayEffectClass, DamageEffectParams.AbilityLevel, EffectContextHandle);

	//通过标签设置GE使用的配置
	for(auto& Pair : DamageEffectParams.DamageTypes)
	{
		UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, Pair.Key, Pair.Value);
	}
	UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Chance, DamageEffectParams.DeBuffChance);
	UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, DamageEffectParams.DeBuffDamageType, DamageEffectParams.DeBuffDamage);
	UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Duration, DamageEffectParams.DeBuffDuration);
	UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, GameplayTags.DeBuff_Frequency, DamageEffectParams.DeBuffFrequency);

	//将GE应用给目标ASC
	DamageEffectParams.TargetAbilitySystemComponent->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());
	return EffectContextHandle;
}

修改生成发射物的函数

我们在ProjectileSpell.cpp文件里,由于修改了增加的负面效果增加的部分,所以,我们需要对发射物生成进行修改,不再创建发射物时创建GE,而是修改为生成一个配置结构体,供后续使用。

void UProjectileSpell::SpawnProjectile(const FVector& ProjectileTargetLocation, const FGameplayTag& SocketTag, const FName SocketName, const bool bOverridePitch, const float PitchOverride)
{
	const bool bIsServer = GetAvatarActorFromActorInfo()->HasAuthority(); //判断此函数是否在服务器运行
	if (!bIsServer) return;

	if (GetAvatarActorFromActorInfo()->Implements<UCombatInterface>())
	{
		const FVector SocketLocation = ICombatInterface::Execute_GetCombatSocketLocationByTag(GetAvatarActorFromActorInfo(), SocketTag, SocketName);
		FRotator Rotation = (ProjectileTargetLocation - SocketLocation).Rotation(); //将方向转为旋转
		if(bOverridePitch)
		{
			Rotation.Pitch = PitchOverride; //覆写发射角度
		}
		
		FTransform SpawnTransform;
		SpawnTransform.SetLocation(SocketLocation);
		SpawnTransform.SetRotation(Rotation.Quaternion());
		
		//SpawnActorDeferred将异步创建实例,在实例创建完成时,相应的数据已经应用到了实例身上
		AProjectile* Projectile = GetWorld()->SpawnActorDeferred<AProjectile>(
			ProjectileClass,
			SpawnTransform,
			GetOwningActorFromActorInfo(),
			Cast<APawn>(GetAvatarActorFromActorInfo()),
			ESpawnActorCollisionHandlingMethod::AlwaysSpawn);

		Projectile->DamageEffectParams = MakeDamageEffectParamsFromClassDefaults();

		//确保变换设置被正确应用
		Projectile->FinishSpawning(SpawnTransform);
	}
}

在AbilityTypes.h里增加参数

在之前的文章里 51. UE5 RPG 自定义FGameplayEffectContext,我们实现自定义GE上下文的属性,增加了格挡和暴击两个属性是否触发,并实现了对其的序列化,可以在客户端将内容复制到服务器端,因为属性的处理都是在服务器端进行的,这里有个交互的过程,需要序列化。
我们给GE上下文增加多个属性,这样,生成的实例里可以设置这些属性
注意,这里的标签没有设置反射,需要我们自己实现类型,在序列化时也有所体现。

protected:

	UPROPERTY()
	bool bIsBlockedHit = false; //格挡

	UPROPERTY()
	bool bIsCriticalHit = false; //暴击

	UPROPERTY()
	bool bIsSuccessfulDeBuff = false; //成功应用负面效果
	
	UPROPERTY()
	float DeBuffDamage = 0.f; //负面效果每次造成的伤害

	UPROPERTY()
	float DeBuffDuration = 0.f; //负面效果持续时间

	UPROPERTY()
	float DeBuffFrequency = 0.f; //负面效果触发频率间隔

	TSharedPtr<FGameplayTag> DamageType; //负面效果的伤害类型

并增加其对应的设置方法

public:

	bool IsBlockedHit() const { return bIsBlockedHit; } //获取 格挡
	bool IsCriticalHit() const { return bIsCriticalHit; } //获取 暴击
	bool IsSuccessfulDeBuff() const { return bIsSuccessfulDeBuff; } //获取 应用负面效果
	float GetDeBuffDamage() const { return DeBuffDamage; } //获取 负面效果伤害
	float GetDeBuffDuration() const { return DeBuffDuration; } //获取 负面效果持续时间
	float GetDeBuffFrequency() const { return DeBuffFrequency; } //获取 负面效果伤害触发间隔
	TSharedPtr<FGameplayTag> GetDeBuffDamageType() const { return DamageType; } //获取 负面效果伤害类型

	void SetIsBlockedHit(const bool bInIsBlockedHit) { bIsBlockedHit = bInIsBlockedHit; } // 设置 格挡
	void SetIsCriticalHit(const bool bInIsCriticalHit) { bIsCriticalHit = bInIsCriticalHit; } // 设置 暴击
	void SetIsSuccessfulDeBuff(const bool bInIsSuccessfulDeBuff) { bIsSuccessfulDeBuff = bInIsSuccessfulDeBuff; } //设置 应用负面效果
	void SetDeBuffDamage(const float InDamage) { DeBuffDamage = InDamage; } //设置 负面效果伤害
	void SetDeBuffDuration(const float InDuration) { DeBuffDuration = InDuration; } //设置 负面效果伤害
	void SetDeBuffFrequency(const float InFrequency) { DeBuffFrequency = InFrequency; } //设置 负面效果伤害
	void SetDeBuffDamageType(const TSharedPtr<FGameplayTag>& InDamageType) { DamageType = InDamageType; } //设置 负面效果伤害类型

我们还要修改它的序列化的方法,让其能够复制到服务器端去处理。
首先是增加序列化内容

		//自定义内容,增加暴击和格挡触发存储
		if(bIsBlockedHit)
		{
			RepBits |= 1 << 7;
		}
		if(bIsCriticalHit)
		{
			RepBits |= 1 << 8;
		}
		if(bIsSuccessfulDeBuff)
		{
			RepBits |= 1 << 9;
		}
		if(DeBuffDamage > 0.f)
		{
			RepBits |= 1 << 10;
		}
		if(DeBuffDuration > 0.f)
		{
			RepBits |= 1 << 11;
		}
		if(DeBuffFrequency > 0.f)
		{
			RepBits |= 1 << 12;
		}
		if(DamageType.IsValid())
		{
			RepBits |= 1 << 13;
		}

接着就是设置序列内容的长度

	//使用了多少长度,就将长度设置为多少
	Ar.SerializeBits(&RepBits, 14);

接着就是在服务器端的反序列化

	//新增对暴击格挡的序列化或反序列化处理
	if (RepBits & (1 << 7))
	{
		Ar << bIsBlockedHit;
	}
	if (RepBits & (1 << 8))
	{
		Ar << bIsCriticalHit;
	}
	if (RepBits & (1 << 9))
	{
		Ar << bIsSuccessfulDeBuff;
	}
	if (RepBits & (1 << 10))
	{
		Ar << DeBuffDamage;
	}
	if (RepBits & (1 << 11))
	{
		Ar << DeBuffDuration;
	}
	if (RepBits & (1 << 12))
	{
		Ar << DeBuffFrequency;
	}
	if (RepBits & (1 << 13))
	{
		if (Ar.IsLoading()) //判断是否在加载资源
		{
			if (!DamageType.IsValid())
			{
				DamageType = TSharedPtr<FGameplayTag>(new FGameplayTag());
			}
		}
		DamageType->NetSerialize(Ar, Map, bOutSuccess);
	}

这就完成了对自定义属性的添加,并且能够实现服务器获取,在AttributeSet里对属性进行处理。

接着,我们在函数库里增加一些函数,可以直接调用函数库内的函数实现对属性设置和获取

	//获取当前GE是否成功应用负面效果
	UFUNCTION(BlueprintPure, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static bool IsSuccessfulDeBuff(const FGameplayEffectContextHandle& EffectContextHandle);

	//获取当前GE负面效果伤害
	UFUNCTION(BlueprintPure, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static float GetDeBuffDamage(const FGameplayEffectContextHandle& EffectContextHandle);

	//获取当前GE负面效果持续时间
	UFUNCTION(BlueprintPure, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static float GetDeBuffDuration(const FGameplayEffectContextHandle& EffectContextHandle);

	//获取当前GE负面效果触发间隔
	UFUNCTION(BlueprintPure, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static float GetDeBuffFrequency(const FGameplayEffectContextHandle& EffectContextHandle);

	//获取当前GE负面效果伤害类型
	UFUNCTION(BlueprintPure, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static FGameplayTag GetDeBuffDamageType(const FGameplayEffectContextHandle& EffectContextHandle);
bool URPGAbilitySystemBlueprintLibrary::IsSuccessfulDeBuff(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get()))
	{
		return RPGEffectContext->IsSuccessfulDeBuff();
	}
	return false;
}

float URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get()))
	{
		return RPGEffectContext->GetDeBuffDamage();
	}
	return 0.f;
}

float URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get()))
	{
		return RPGEffectContext->GetDeBuffDuration();
	}
	return 0.f;
}

float URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get()))
	{
		return RPGEffectContext->GetDeBuffFrequency();
	}
	return 0.f;
}

FGameplayTag URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if(const FRPGGameplayEffectContext* RPGEffectContext = static_cast<const FRPGGameplayEffectContext*>(EffectContextHandle.Get()))
	{
		//如果当前存在设置了伤害类型
		if(RPGEffectContext->GetDeBuffDamageType().IsValid())
		{
			//取消指针
			return *RPGEffectContext->GetDeBuffDamageType();
		}
	}
	return FGameplayTag();
}

在设置这里,因为是需要在同一地方使用,所以,我们直接将其合并为了一个函数设置

	//设置GE是否应用负面效果
	UFUNCTION(BlueprintCallable, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static void SetIsSuccessfulDeBuff(UPARAM(ref) FGameplayEffectContextHandle& EffectContextHandle, bool bInIsSuccessfulDeBuff);

	//设置GE负面效果相关数值 负面效果伤害类型 负面效果伤害 负面效果持续时间 负面效果触发间隔时间
	UFUNCTION(BlueprintCallable, Category="RPGAbilitySystemLibrary|GameplayEffects")
	static void SetDeBuff(UPARAM(ref) FGameplayEffectContextHandle& EffectContextHandle, FGameplayTag& InDamageType, float InDamage, float InDuration, float InFrequency);
void URPGAbilitySystemBlueprintLibrary::SetIsSuccessfulDeBuff(FGameplayEffectContextHandle& EffectContextHandle, const bool bInIsSuccessfulDeBuff)
{
	FRPGGameplayEffectContext* RPGEffectContext = static_cast<FRPGGameplayEffectContext*>(EffectContextHandle.Get());
	RPGEffectContext->SetIsSuccessfulDeBuff(bInIsSuccessfulDeBuff);
}

void URPGAbilitySystemBlueprintLibrary::SetDeBuff(FGameplayEffectContextHandle& EffectContextHandle, FGameplayTag& InDamageType, const float InDamage, const float InDuration, const float InFrequency)
{
	FRPGGameplayEffectContext* RPGEffectContext = static_cast<FRPGGameplayEffectContext*>(EffectContextHandle.Get());
	//通过标签创建一个共享指针
	const TSharedPtr<FGameplayTag> DamageType = MakeShared<FGameplayTag>(InDamageType);
	RPGEffectContext->SetDeBuffDamageType(DamageType);
	RPGEffectContext->SetDeBuffDamage(InDamage);
	RPGEffectContext->SetDeBuffDuration(InDuration);
	RPGEffectContext->SetDeBuffFrequency(InFrequency);
}

实现负面效果应用

实现了相应的函数后,我们需要接着实现负面效果的应用,我们在ExecCalc_Damage.cpp里,专门实现了对目标进行造成的最终伤害的计算,我们在内部实现负面效果的是否应用成功的计算,首先获取到负面效果命中率,然后通过目标抵抗降低命中率,接着通过随机数判断当前是否需要应用负面效果。

void UExecCalc_Damage::DetermineDeBuff(const FGameplayEffectCustomExecutionParameters& ExecutionParams, const FGameplayEffectSpec& Spec, const FAggregatorEvaluateParameters& EvaluationParameters, TMap<FGameplayTag, FGameplayEffectAttributeCaptureDefinition> TagsToCaptureDefs)
{
	const FRPGGameplayTags& GameplayTags = FRPGGameplayTags::Get();

	//遍历所有的负面效果伤害类型,根据伤害类型是否赋值来判断是否需要应用负面效果
	for(const TTuple<FGameplayTag, FGameplayTag>& Pair : GameplayTags.DeBuffsToResistance)
	{
		FGameplayTag DeBuffDamageType = Pair.Key; //获取到负面效果伤害类型
		const FGameplayTag ResistanceType = Pair.Value; //获取到负面效果抵抗类型
		const float TypeDamage = Spec.GetSetByCallerMagnitude(DeBuffDamageType, false, -1.f);

		//如果负面效果设置了伤害,即使为0,也需要应用负面效果
		if(TypeDamage > -.5f)
		{
			//获取负面效果命中率
			const float SourceDeBuffChance = Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Chance, false, -1.f);

			//----------------获取负面效果抵抗------------
			float TargetDeBuffResistance = 0.f; //计算目标对收到的负面效果类型的抵抗
			//检查对应的属性快照是否设置,防止报错
			checkf(TagsToCaptureDefs.Contains(ResistanceType), TEXT("在ExecCalc_Damage中,无法获取到Tag[%s]对应的属性快照"), *ResistanceType.ToString());
			//通过抗性标签获取到属性快照的值
			const FGameplayEffectAttributeCaptureDefinition CaptureDef = TagsToCaptureDefs[ResistanceType];
			ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(CaptureDef, EvaluationParameters, TargetDeBuffResistance);
			TargetDeBuffResistance = FMath::Clamp(TargetDeBuffResistance, 0.f, 100.f); //将抗住限制在0到100

			//----------------计算负面效果是否应用------------
			const float EffectiveDeBuffChance = SourceDeBuffChance * (100 - TargetDeBuffResistance) / 100.f; //计算出负面效果的实际命中率
			const bool bDeBuff = FMath::RandRange(1, 100) < EffectiveDeBuffChance; //判断此次效果是否实现命中
			if(bDeBuff)
			{
				//获取GE上下文设置负面效果相关配置
				FGameplayEffectContextHandle ContextHandle = Spec.GetContext();

				//设置当前应用负面效果成功
				URPGAbilitySystemBlueprintLibrary::SetIsSuccessfulDeBuff(ContextHandle, true);

				const float SourceDeBuffDuration = Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Duration, false, 0.f);
				const float SourceDeBuffFrequency = Spec.GetSetByCallerMagnitude(GameplayTags.DeBuff_Frequency, false, 0.f);
				//设置负面效果 伤害类型 伤害 持续时间 触发频率
				URPGAbilitySystemBlueprintLibrary::SetDeBuff(ContextHandle, DeBuffDamageType, TypeDamage, SourceDeBuffDuration, SourceDeBuffFrequency);
			}
		}
	}
}

将一部分代码转换为函数

有个小技巧,我们还可以将其内容提取为单独函数
在这里插入图片描述
设置命名和参数
在这里插入图片描述
可以去除不需要的参数。
在这里插入图片描述

整理AttributeSet内的代码

代码写的越来越多,会变成一坨,所以我们需要将代码分离成函数,这样方便后期的维护。
我们在自定义的AttributeSet里增加三个函数,分别用于处理接收实际受到的伤害属性,获取到的经验值,以及需要处理应用负面效果的函数。

	//处理传入的参数为伤害属性时,处理的逻辑
	void HandleIncomingDamage(const FEffectProperties& Props);

	//处理传入的参数为经验属性时,处理的逻辑
	void HandleIncomingXP(const FEffectProperties& Props);

	//如果当前伤害触发了负面效果,处理的逻辑
	void HandleDeBuff(const FEffectProperties& Props);

有了这几个函数,我们的代码就清晰了很多,比如接收到属性的函数代码,这里防止敌人在死亡后还会受到伤害,我们在进行属性处理前判断角色是否处于死亡状态。

void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	FEffectProperties Props;
	SetEffectProperties(Data, Props);

	//判断当前目标是否已经死亡,如果死亡,将不再进行处理
	if(Props.TargetCharacter->Implements<UCombatInterface>() && ICombatInterface::Execute_IsDead(Props.TargetCharacter)) return;

	if(Data.EvaluatedData.Attribute == GetHealthAttribute())
	{
		SetHealth(FMath::Clamp(GetHealth(), 0.f, GetMaxHealth()));
		// UE_LOG(LogTemp, Warning, TEXT("%s 的生命值发生了修改,当前生命值:%f"), *Props.TargetAvatarActor->GetName(), GetHealth());
	}

	if(Data.EvaluatedData.Attribute == GetManaAttribute())
	{
		SetMana(FMath::Clamp(GetMana(), 0.f, GetMaxMana()));
	}

	if(Data.EvaluatedData.Attribute == GetIncomingDamageAttribute())
	{
		HandleIncomingDamage(Props);
	}

	if(Data.EvaluatedData.Attribute == GetIncomingXPAttribute())
	{
		HandleIncomingXP(Props);
	}
	
}

由于它们都只需要在Props里拿值进行运算,我们只需要一个Props属性即可。

void URPGAttributeSet::HandleIncomingXP(const FEffectProperties& Props)
{
	const float LocalIncomingXP = GetIncomingXP();
	SetIncomingXP(0);
	// UE_LOG(LogRPG, Log, TEXT("获取传入经验值:%f"), LocalIncomingXP);
		
	if(Props.SourceCharacter->Implements<UPlayerInterface>() && Props.SourceCharacter->Implements<UCombatInterface>())
	{
		//获取角色当前等级和经验
		const int32 CurrentLevel = ICombatInterface::Execute_GetPlayerLevel(Props.SourceCharacter);
		const int32 CurrentXP = IPlayerInterface::Execute_GetXP(Props.SourceCharacter);

		//获取获得经验后的新等级
		const int32 NewLevel = IPlayerInterface::Execute_FindLevelForXP(Props.SourceCharacter, CurrentXP + LocalIncomingXP);
		const int32 NumLevelUps = NewLevel - CurrentLevel; //查看等级是否有变化
		if(NumLevelUps > 0)
		{
			//如果连升多级,我们通过for循环获取每个等级的奖励
			for(int32 i = CurrentLevel; i < NewLevel; i++)
			{
				//获取升级提供的技能点和属性点
				const int32 AttributePointsReward = IPlayerInterface::Execute_GetAttributePointsReward(Props.SourceCharacter, i);
				const int32 SpellPointsReward = IPlayerInterface::Execute_GetSpellPointsReward(Props.SourceCharacter, i);

				//增加角色技能点和属性点
				IPlayerInterface::Execute_AddToAttributePoints(Props.SourceCharacter, AttributePointsReward);
				IPlayerInterface::Execute_AddToSpellPoints(Props.SourceCharacter, SpellPointsReward);
			}

			//提升等级
			IPlayerInterface::Execute_AddToPlayerLevel(Props.SourceCharacter, NumLevelUps);

			//播放升级效果
			IPlayerInterface::Execute_LevelUp(Props.SourceCharacter);

			//将血量和蓝量填充满, 我们将设置变量
			SetHealth(GetMaxHealth());
			SetMana(GetMaxMana());
		}
			
		//将经验应用给自身,通过事件传递,在玩家角色被动技能GA_ListenForEvents里接收
		IPlayerInterface::Execute_AddToXP(Props.SourceCharacter, LocalIncomingXP);
	}
}

在接收伤害的函数里,我们增加对负面效果应用的判断,如果当前应用的负面效果,我们将调用负面效果函数处理逻辑。

void URPGAttributeSet::HandleIncomingDamage(const FEffectProperties& Props)
{
	const float LocalIncomingDamage = GetIncomingDamage();
	SetIncomingDamage(0.f);
	if(LocalIncomingDamage > 0.f)
	{
		const float NewHealth = GetHealth() - LocalIncomingDamage;
		SetHealth(FMath::Clamp(NewHealth, 0.f, GetMaxHealth()));

		const bool bFatal = NewHealth <= 0.f; //血量小于等于0时,角色将会死亡
		if(bFatal)
		{
			//调用死亡函数
			ICombatInterface* CombatInterface = Cast<ICombatInterface>(Props.TargetAvatarActor);
			if(CombatInterface)
			{
				CombatInterface->Die();
			}
			//死亡时,发送经验事件
			SendXPEvent(Props);
		}
		else
		{
			//激活受击技能
			FGameplayTagContainer TagContainer;
			TagContainer.AddTag(FRPGGameplayTags::Get().Effects_HitReact);
			// Props.TargetASC->CancelAbilities(&TagContainer); //先取消之前的受击
			Props.TargetASC->TryActivateAbilitiesByTag(TagContainer); //根据tag标签激活技能
		}

		//获取格挡和暴击
		const bool IsBlockedHit = URPGAbilitySystemBlueprintLibrary::IsBlockedHit(Props.EffectContextHandle);
		const bool IsCriticalHit = URPGAbilitySystemBlueprintLibrary::IsCriticalHit(Props.EffectContextHandle);

		//显示伤害数字
		ShowFloatingText(Props, LocalIncomingDamage, IsBlockedHit, IsCriticalHit);

		//判断当前是否应用负面效果
		if(URPGAbilitySystemBlueprintLibrary::IsSuccessfulDeBuff(Props.EffectContextHandle))
		{
			HandleDeBuff(Props);
		}
	}
}

通过代码创建GameplayEffect类

接下来,我们要实现设置GE,并将其应用到角色身上,这里,采用c++编写的方式实现,我会将代码和在UE里编辑的配置进行比对,方便大家更清晰的熟悉每个配置项。
我们将在之前添加的HandleDeBuff函数中增加处理,这个函数将在可以对目标应用负面效果时调用。
我们首先通过库函数获取到创建GE类的相关参数

	//获取负面效果相关参数
	const FGameplayTag DeBuffType = URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(Props.EffectContextHandle);
	const float DeBuffDamage = URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(Props.EffectContextHandle);
	const float DeBuffDuration = URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(Props.EffectContextHandle);
	const float DeBuffFrequency = URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(Props.EffectContextHandle);

然后我们设置一个名称并创建一个GE类

	//创建GE所使用的名称,并创建一个可实例化的GE
	FString DeBuffName = FString::Printf(TEXT("DynamicDeBuff_%s"), *DeBuffType.ToString());
	UGameplayEffect* Effect = NewObject<UGameplayEffect>(GetTransientPackage(), FName(DeBuffName));

接下来就是修改GE类的配置项,它们将在我们应用GE实例时,产生效果。
首先我们设置GE为有时间限制的,并设置对应时间

	//设置动态创建GE的属性
	Effect->DurationPolicy = EGameplayEffectDurationType::HasDuration; //设置GE为有时间限制的效果
	Effect->DurationMagnitude = FScalableFloat(DeBuffDuration); //设置GE的持续时间

在这里插入图片描述
然后就是设置Period

	Effect->Period = FScalableFloat(DeBuffFrequency); //设置GE的触发策略,间隔时间
	Effect->bExecutePeriodicEffectOnApplication = false; //在应用后不会立即触发,而是在经过了Period后才会触发
	Effect->PeriodicInhibitionPolicy = EGameplayEffectPeriodInhibitionRemovedPolicy::NeverReset; //设置每次应用后不会重置触发时间

在这里插入图片描述
接着就是叠加层数相关设置,这里和我们所需的不同,但是为了显示所有设置,我全添加了

	//设置可叠加层数
	Effect->StackingType = EGameplayEffectStackingType::AggregateBySource; //设置GE应用基于释放者查看
	Effect->StackLimitCount = 1; //设置叠加层数
	Effect->StackDurationRefreshPolicy = EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; //在应用后重置时,重置持续时间
	Effect->StackPeriodResetPolicy = EGameplayEffectStackingPeriodPolicy::ResetOnSuccessfulApplication; //在应用时,触发并重置Period时间
	Effect->StackExpirationPolicy = EGameplayEffectStackingExpirationPolicy::ClearEntireStack; //GE时间到了默认清除所有层数,还有可以清除单层的设置
	//Effect->OverflowEffects.Add() //在叠加层数超出时,将触发此数组内的GE应用到角色
	Effect->bDenyOverflowApplication = true; //设置为true时,叠加层数超出时,将不会刷新GE实例
	Effect->bClearStackOnOverflow = true; //设置为true时,叠加层数超出时,将清除GE

在这里插入图片描述
在5.3版本修改为了通过GEComponent来设置Actor身上的标签,在老版是可以直接通过InheritableOwnedTagsContainer获取容器去修改

	//在5.3版本修改为通过GEComponent来设置GE应用的标签,向目标Actor增加对应的标签
	UTargetTagsGameplayEffectComponent& TargetTagsGameplayEffectComponent = Effect->AddComponent<UTargetTagsGameplayEffectComponent>();
	FInheritedTagContainer InheritableOwnedTagsContainer = TargetTagsGameplayEffectComponent.GetConfiguredTargetTagChanges(); //获取到标签容器
	InheritableOwnedTagsContainer.AddTag(DeBuffType); //添加标签
	TargetTagsGameplayEffectComponent.SetAndApplyTargetTagChanges(InheritableOwnedTagsContainer); //应用并更新

在这里插入图片描述
接着就是设置属性的修改

	//设置属性修改
	const int32 Index = Effect->Modifiers.Num(); //获取当前修改属性的Modifiers的长度,也就是下一个添加的modify的下标索引
	Effect->Modifiers.Add(FGameplayModifierInfo()); //添加一个新的Modify
	FGameplayModifierInfo& ModifierInfo = Effect->Modifiers[Index]; //通过下标索引获取Modify

	ModifierInfo.ModifierMagnitude = FScalableFloat(DeBuffDamage); //设置应用的属性值
	ModifierInfo.ModifierOp = EGameplayModOp::Additive; //设置属性运算符号
	ModifierInfo.Attribute = URPGAttributeSet::GetIncomingDamageAttribute(); //设置修改的属性

在这里插入图片描述
然后就通过GE创建GE实例,并应用到目标身上

	//创建GE实例,并添加伤害类型标签,应用GE
	FGameplayEffectContextHandle EffectContextHandle = Props.SourceASC->MakeEffectContext();
	EffectContextHandle.AddSourceObject(Props.SourceCharacter);
	if(const FGameplayEffectSpec* MutableSpec = new FGameplayEffectSpec(Effect, EffectContextHandle, 1.f))
	{
		FRPGGameplayEffectContext* RPGContext = static_cast<FRPGGameplayEffectContext*>(MutableSpec->GetContext().Get());
		const TSharedPtr<FGameplayTag> DeBuffDamageType = MakeShareable(new FGameplayTag(DeBuffType));
		RPGContext->SetDeBuffDamageType(DeBuffDamageType);
		
		Props.TargetASC->ApplyGameplayEffectSpecToSelf(*MutableSpec);
	}

最后,粘贴一下完整代码

void URPGAttributeSet::HandleDeBuff(const FEffectProperties& Props)
{
	//获取负面效果相关参数
	const FGameplayTag DeBuffType = URPGAbilitySystemBlueprintLibrary::GetDeBuffDamageType(Props.EffectContextHandle);
	const float DeBuffDamage = URPGAbilitySystemBlueprintLibrary::GetDeBuffDamage(Props.EffectContextHandle);
	const float DeBuffDuration = URPGAbilitySystemBlueprintLibrary::GetDeBuffDuration(Props.EffectContextHandle);
	const float DeBuffFrequency = URPGAbilitySystemBlueprintLibrary::GetDeBuffFrequency(Props.EffectContextHandle);

	//创建GE所使用的名称,并创建一个可实例化的GE
	FString DeBuffName = FString::Printf(TEXT("DynamicDeBuff_%s"), *DeBuffType.ToString());
	UGameplayEffect* Effect = NewObject<UGameplayEffect>(GetTransientPackage(), FName(DeBuffName));

	//设置动态创建GE的属性
	Effect->DurationPolicy = EGameplayEffectDurationType::HasDuration; //设置GE为有时间限制的效果
	Effect->DurationMagnitude = FScalableFloat(DeBuffDuration); //设置GE的持续时间
	
	Effect->Period = FScalableFloat(DeBuffFrequency); //设置GE的触发策略,间隔时间
	Effect->bExecutePeriodicEffectOnApplication = false; //在应用后不会立即触发,而是在经过了Period后才会触发
	Effect->PeriodicInhibitionPolicy = EGameplayEffectPeriodInhibitionRemovedPolicy::NeverReset; //设置每次应用后不会重置触发时间

	//设置可叠加层数
	Effect->StackingType = EGameplayEffectStackingType::AggregateBySource; //设置GE应用基于释放者查看
	Effect->StackLimitCount = 1; //设置叠加层数
	Effect->StackDurationRefreshPolicy = EGameplayEffectStackingDurationPolicy::RefreshOnSuccessfulApplication; //在应用后重置时,重置持续时间
	Effect->StackPeriodResetPolicy = EGameplayEffectStackingPeriodPolicy::ResetOnSuccessfulApplication; //在应用时,触发并重置Period时间
	Effect->StackExpirationPolicy = EGameplayEffectStackingExpirationPolicy::ClearEntireStack; //GE时间到了默认清除所有层数,还有可以清除单层的设置
	//Effect->OverflowEffects.Add() //在叠加层数超出时,将触发此数组内的GE应用到角色
	// Effect->bDenyOverflowApplication = true; //设置为true时,叠加层数超出时,将不会刷新GE实例
	// Effect->bClearStackOnOverflow = true; //设置为true时,叠加层数超出时,将清除GE

	//在5.3版本修改为通过GEComponent来设置GE应用的标签,向目标Actor增加对应的标签
	UTargetTagsGameplayEffectComponent& TargetTagsGameplayEffectComponent = Effect->AddComponent<UTargetTagsGameplayEffectComponent>();
	FInheritedTagContainer InheritableOwnedTagsContainer = TargetTagsGameplayEffectComponent.GetConfiguredTargetTagChanges(); //获取到标签容器
	InheritableOwnedTagsContainer.AddTag(DeBuffType); //添加标签
	TargetTagsGameplayEffectComponent.SetAndApplyTargetTagChanges(InheritableOwnedTagsContainer); //应用并更新

	//设置属性修改
	const int32 Index = Effect->Modifiers.Num(); //获取当前修改属性的Modifiers的长度,也就是下一个添加的modify的下标索引
	Effect->Modifiers.Add(FGameplayModifierInfo()); //添加一个新的Modify
	FGameplayModifierInfo& ModifierInfo = Effect->Modifiers[Index]; //通过下标索引获取Modify

	ModifierInfo.ModifierMagnitude = FScalableFloat(DeBuffDamage); //设置应用的属性值
	ModifierInfo.ModifierOp = EGameplayModOp::Additive; //设置属性运算符号
	ModifierInfo.Attribute = URPGAttributeSet::GetIncomingDamageAttribute(); //设置修改的属性

	//创建GE实例,并添加伤害类型标签,应用GE
	FGameplayEffectContextHandle EffectContextHandle = Props.SourceASC->MakeEffectContext();
	EffectContextHandle.AddSourceObject(Props.SourceCharacter);
	if(const FGameplayEffectSpec* MutableSpec = new FGameplayEffectSpec(Effect, EffectContextHandle, 1.f))
	{
		FRPGGameplayEffectContext* RPGContext = static_cast<FRPGGameplayEffectContext*>(MutableSpec->GetContext().Get());
		const TSharedPtr<FGameplayTag> DeBuffDamageType = MakeShareable(new FGameplayTag(DeBuffType));
		RPGContext->SetDeBuffDamageType(DeBuffDamageType);
		
		Props.TargetASC->ApplyGameplayEffectSpecToSelf(*MutableSpec);
	}

}

我们就可以去项目中测试是否有bug。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2098661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【最新华为OD机试E卷】猜字迷(100分)-多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,…

计算机网络 数据链路层1

数据链路层&#xff1a; 服务&#xff1a;将来自网络层的数据传输到相邻节点的网络层 作用&#xff1a;加强物理层传输原始比特流的功能 封装成帧---组帧&#xff1a;将来自网络层的数据在首尾添加特定信息(帧定界&#xff1a;帧的起始&#xff0c;结束) 差错控制 CRC循环冗余…

Springboot快速创建的两种方法(简单易学)

方式一&#xff1a;使用网站https://start.spring.io/快速创建 直接在浏览器中输入以上网址&#xff0c;进入创建Springboot项目页面&#xff0c;根据需要勾选一些选项&#xff0c;然后下载到本地即可。 方式二&#xff1a;在IDEA中创建 步骤 创建Maven项目 导入spring-bo…

Spring boot整合接入Redis

Spring boot简单接入Redis 1.pom文件中引入redis <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 2.yml文件redis属性配置 spring:redis:host: 120.4…

小北使用Python和pyecharts对我校通信学院2024大数据专业就业情况进行中国地图可视化

引言 在数据分析领域&#xff0c;地图可视化是一种直观展示数据分布和趋势的有效方式。然而&#xff0c;当我们面对如“2020-2024届近5年通信就业数据”这样的数据集时&#xff0c;挑战也随之而来。这些数据通常包含就业单位名称和对应的学生信息&#xff0c;但缺乏直接的地理位…

MyBatis-SQL-语句执行流程

已查询为例 首先我们可以看到&#xff0c;在查询的时候Mapper对象已经是被代理过后的&#xff1a; 所以会执行invoke方法&#xff0c;其底层实现就是JDK的动态代理&#xff1a; 如下图所示&#xff0c;如果MethodCache里面存在方法&#xff0c;则判断这个方法是否为default方…

STM32:TIM中断配置应用(1)呼吸灯:库函数讲解笔记+代码讲解笔记

声明&#xff1a;本博客为哔哩哔哩up主江协科技 “STM32入门教程”的听课笔记&#xff0c;仅供学习、参考使用&#xff0c;不得用作其他用途&#xff0c;违者必究。如有版权问题&#xff0c;请联系作者修改。 目录 一、综述 二、TIM库&#xff08;有关输出比较的函数&#x…

【如何下载Landsat数据】

下载Landsat数据可以通过多种途径实现&#xff0c;主要包括使用官方网站、第三方平台和专门的软件库等。以下是一些常用的方法&#xff1a; 1. 使用USGS官方网站 EarthExplorer&#xff08;earthexplorer.usgs.gov&#xff09; 注册账号&#xff1a;首先&#xff0c;需要在…

10-python格式化字符串的四种方法(%,format,f-string,string template)

3 f-string (格式化字符串) in Python 自 Python 3.6 引入以来,f-string 提供了一种更加简洁和直观的方式来进行字符串格式化。其语法简单明了:只需在字符串前加上字母 f 或 F,并在字符串中使用 {} 来包裹需要插入的内容。 它相比于之前的%格式化和字符串format方法写起来更…

【R语言】基于Biomod2集成平台探究物种分布区的构建流程(SDMs)(持续更新中。。。。。。)

Species Distribution Models 1.写在前面2.物种分布模型介绍3.输入数据准备及预处理3.1.如何从GBIF网站上获取分布点数据&#xff08;基于rgbif包&#xff09;3.2.分布点稀疏处理&#xff08;基于spThin函数&#xff09;3.3.如何获取环境变量数据&#xff08;基于getData函数&a…

兴业月报|八月法拍房市场套均成交折扣降至6.9折

导读 8月北京法拍房成交房源233套&#xff0c;成交总金额18.2891亿元&#xff0c;套均成交价784.94万元&#xff0c;总参拍人数890人&#xff0c;套均参拍人数3.81人&#xff0c;套均成交折扣6.9折。 ——兴业数据中心 2024年八月北京法拍房市场详细数据报告 2024.03-2024.0…

计算机网络 第1章 概述

文章目录 计算机网络概念计算机网络的组成计算机网络的功能三种数据交换技术电路交换&#xff08;Circuit Switching&#xff09;报文交换&#xff08;message&#xff09;分组交换 三种交换方式性能对比计算机网络的分类计算机网络的性能指标性能指标1&#xff1a;速率性能指标…

【mysql】mysql查询机制 调优不止是索引调优

前言&#xff1a;说到mysql调优 我们第一反应都是想到索引调优 应该这是最基本的 也是至关重要的&#xff1b;一般工作个两年 索引调优都可以掌握的八九不离十&#xff0c;相关数据结构特点也都能说个一二出来&#xff0c;所以本文重点是讲述其它机制 整体架构 连接器&#xff…

【C++】手动实现String类的封装(分文件编译)

实现了String类的大部分封装&#xff0c;采用分文件编译 //mystring.h #ifndef MYSTRING_H #define MYSTRING_H#include <iostream> #include <cstring> using namespace std;class myString { private:char *str; //定义一个字符串int size; //记录字符串…

比亚迪方程豹携手华为乾崑智驾,加速中国智驾技术向前

近日&#xff0c;比亚迪方程豹与华为乾崑智驾在深圳签署合作协议&#xff0c;中国两大科技巨头强强联合&#xff0c;共同合作开发全球首个硬派专属智能驾驶方案&#xff0c;实现整车智驾深度融合&#xff0c;首发搭载在即将上市的方程豹豹8车型。 比亚迪智驾以自主研发和开放合…

MySQL之数据库基础

目录 一、数据库 1、基本概念 2、常见的数据库 3、MySQL数据库 连接MySQL服务器 数据逻辑存储 二、数据库和表的本质 三、SQL语句 四、服务器&#xff0c;数据库&#xff0c;表的关系 五、存储引擎 查看存储引擎 一、数据库 1、基本概念 一般来说&#xff0c;数据库…

【软件技巧】第33课,软件逆向安全工程师之如何快速的跑到某行代码EIP设置,每天5分钟学习逆向吧!

鼠标右键在此设置EIP EIP&#xff08;Extended Instruction Pointer&#xff09;是x86架构中一个重要的寄存器&#xff0c;它用于存储当前正在执行的指令的地址。EIP是程序计数器&#xff08;Program Counter&#xff09;的扩展版本&#xff0c;因为它是32位寄存器&#xff0c…

Centos安装node_exporter

使用以下命令下载最新版本的node_exporter, 地址: https://github.com/prometheus/node_exporter/releasesorter/releases&#xff1a; wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz或本地下载上传到服…

python 字典怎么提取value

打开编辑器&#xff0c;写上注释内容。 新建一个函数getvalue。 新建一个字典。 zidian{"country1":"america","country2":"australia","country3":"germany"} 利用values这个方法来获取字典中的所有Vlue值&…

浅析synchronized锁升级的原理与实现 1

目录 背景 锁的用法 底层实现 原理分析 锁的状态 锁升级 锁升级过程 锁升级简要步骤 锁升级细化流程 背景 在多线程编程中&#xff0c;线程同步是一个关键的概念&#xff0c;它确保了多个线程对共享资源的安全访问。Java中的synchronized关键字是一种常用的线程同步机制…