83. UE5 RPG 实现属性值的设置

85 篇文章 11 订阅

在前面,我们实现了角色升级相关的功能,在PlayerState上记录了角色的等级和经验值,并在变动时,通过委托广播的形式向外广播,然后在UI上,通过监听委托的变动,进行修改等级和经验值。
在这一篇里,我们实现一下对属性值的操作,原理和等的差不多,但是编写死不一样。属性值是在属性面板上的属性,它有一个专用的属性面板控制器。
我们的控制器的基类为RPGWidgetController.h,从基类分了两个子类,OverlayWidgetController.h一直显示在战斗界面的覆层的控制器,用于更新血量蓝量,技能,经验等级等。AttributeMenuWidgetController.h为专门为属性面板使用的控制器,我们在属性里设置了一个数组,用于对应属性标签和属性的对应,并在属性修改后,对其进行广播,用于属性面板的更新,虽然此内容与本篇无关,作为记录。
我们接下来,会在此控制器增加属性点变动的委托。

在PlayerState增加属性点记录

和等级相同,我们先增加对属性点的参数。

	UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_AttributePoints)
	int32 AttributePoints = 0;

以及属性点的变动委托

FOnPlayerStateChanged OnAttributePointsChangedDelegate; //属性点数变动委托

从服务器到客户端的同步函数

	UFUNCTION()
	void OnRep_AttributePoints(int32 OldAttributePoints) const; //服务器出现更改自动同步到本地函数 属性点

实现

void ARPGPlayerState::OnRep_AttributePoints(int32 OldAttributePoints) const
{
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

接着增加属性点的相关函数

	//属性点
	FORCEINLINE int32 GetAttributePoints() const {return AttributePoints;} //获取角色当前属性点
	void AddToAttributePoints(int32 InAttributePoints); //增加属性点
	void SetAttributePoints(int32 InAttributePoints); //设置当前属性点

在设置后广播委托

void ARPGPlayerState::AddToAttributePoints(const int32 InAttributePoints)
{
	AttributePoints += InAttributePoints;
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

void ARPGPlayerState::SetAttributePoints(const int32 InAttributePoints)
{
	AttributePoints = InAttributePoints;
	OnAttributePointsChangedDelegate.Broadcast(AttributePoints);
}

控制器添加委托

为了方便增加玩家状态的委托,我们将之前放在OverlayWidgetController.h覆层控制器里面的委托,移到基类RPGWidgetController.h,这样它的子类都可以使用这个委托类型,防止重复定义。

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerStateChangedSignature, int32, NewValue); //当玩家状态该表回调类型

接下来,我们就可以在属性面板控制器AttributeMenuWidgetController.h里面声明属性点委托

	UPROPERTY(BlueprintAssignable, Category="GAS|Attributes")
	FOnPlayerStateChangedSignature AttributePointsChangedDelegate; //监听属性点的变化委托

在绑定委托这里,我们实现对PlayerState里的委托绑定匿名函数,用于UI使用

	ARPGPlayerState* RPGPlayerState = CastChecked<ARPGPlayerState>(PlayerState);
	//绑定PlayerState的属性点委托
	RPGPlayerState->OnAttributePointsChangedDelegate.AddLambda([this](const int32 Points)
	{
		AttributePointsChangedDelegate.Broadcast(Points);
	});

在初始化这里,我们也需要广播一次,在面板打开时,显示正确的属性点

	const ARPGPlayerState* RPGPlayerState = CastChecked<ARPGPlayerState>(PlayerState);
	AttributePointsChangedDelegate.Broadcast(RPGPlayerState->GetAttributePoints());

修改一次升多个等级

我们还未对一次提升两级或者三级时对角色的属性点奖励和技能点奖励进行设置,实现起来简单,我们只需要对提升的等级进行遍历,将所有的奖励应用即可。
在AS里面,我们在升级时增加遍历,将已经升到的等级的奖励应用

//获取获得经验后的新等级
			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());
			}

在UI实现对属性点的监听

我们之前创建的属性面板,以及对应的部件无法实现当前功能
在这里插入图片描述
所以,我们要为属性点创建一个专用的控制面板
在这里插入图片描述
在控件里面,我们需要通过蓝图修改属性名称,直接写上即可
在这里插入图片描述
然后修改监听的委托,修改为监听属性点的变动委托
在这里插入图片描述
现在我们有了属性点的值,接着要在玩家角色类里,将之前未完成的函数编写完成

void ARPGHero::AddToAttributePoints_Implementation(int32 InAttributePoints)
{
	ARPGPlayerState* PlayerStateBase = GetPlayerState<ARPGPlayerState>();
	check(PlayerStateBase); //检测是否有效,无限会暂停游戏
	PlayerStateBase->AddToAttributePoints(InAttributePoints);
}

测试,在角色等级提升时,角色属性点是否会自动修改。
在这里插入图片描述

设置加点按钮的开启关闭

接下来,我们实现一下对加点按钮的开启和关闭的设置。我们想在有可分配的属性点时,加点按钮可以点击,而在可分配属性点数为0时,我们将其设置为不可点击。
我们在WBP_TextValueButtonRow里增加一个函数,用于设置按钮点击是否开启
在这里插入图片描述
然后在WBP_AttributeMenu里增加一个函数,传入当前可分配的属性点数,根据属性点数设置是个按钮是否需要开启
在这里插入图片描述
接着,我们重写事件,将获取到的将控制器存储到变量,并监听属性点变动回调,调用设置按钮函数,并在最后初始化。
在这里插入图片描述
这个获取方式是我们写的蓝图函数库的方法,它们设置的位置是我们自定义的HUD类上,并存储一份单例
在这里插入图片描述
接着运行测试,在可分配属性点为0时,无法点击
在这里插入图片描述
当拥有了可分配属性点,增加按钮变为可点击状态
在这里插入图片描述

实现属性加点功能

有了可分配属性,接下来我们将实现属性加点的功能。
我们将通过点击按钮触发事件,然后调用控制器内的函数,将加点属性的标签传递,接着在ASC里,通过发送事件,将需要增加的属性和之前增长经验的方式增加,并在内部处理可分配的属性值。
首先,我们在AttributeMenuWidgetController.h增加一个函数,用于增加事件触发后的调用函数

	UFUNCTION(BlueprintCallable, Category="GAS|Attributes")
	void UpgradeAttribute(const FGameplayTag& AttributeTag); //升级属性

然后在自定义的ASC中增加两个函数,它们其实是为了实现一个功能,但是对于属性操作只需要在服务器去做修改即可,所以,我们额外增加了一个只有服务器运行的函数

	void UpgradeAttribute(const FGameplayTag& AttributeTag); //升级属性

	UFUNCTION(Server, Reliable)
	void ServerUpgradeAttribute(const FGameplayTag& AttributeTag); //服务器升级属性函数

我们需要能够在ASC里面去获取当前角色是否拥有可分配的属性点数,在角色接口里增加一个获取的函数

	UFUNCTION(BlueprintNativeEvent)
	int32 GetAttributePoints() const; //获取可分配属性点数

在角色基类里覆写它

virtual int32 GetSpellPoints_Implementation() const override;

实现此函数,从PlayerState里获取即可

int32 ARPGHero::GetAttributePoints_Implementation() const
{
	const ARPGPlayerState* PlayerStateBase = GetPlayerState<ARPGPlayerState>();
	check(PlayerStateBase); //检测是否有效,无限会暂停游戏
	return PlayerStateBase->GetAttributePoints();
}

有了属性点数的获取,我们就可以实现ASC中创建的升级属性的函数,如果角色拥有可分配的点数,那么我们将让服务器执行发送事件,我们之前创建了被动技能接收属性相关的标签事件,我们将需要提升的属性标签和提升数量传递过去,并通过接口,减少相应的数量。

void URPGAbilitySystemComponent::UpgradeAttribute(const FGameplayTag& AttributeTag)
{
	//判断Avatar是否基础角色接口
	if(GetAvatarActor()->Implements<UPlayerInterface>())
	{
		//判断是否用于可分配点数
		if(IPlayerInterface::Execute_GetAttributePoints(GetAvatarActor()) > 0)
		{
			ServerUpgradeAttribute(AttributeTag); //调用服务器升级属性
		}
	}
}

void URPGAbilitySystemComponent::ServerUpgradeAttribute_Implementation(const FGameplayTag& AttributeTag)
{
	FGameplayEventData Payload; //创建一个事件数据
	Payload.EventTag = AttributeTag;
	Payload.EventMagnitude = 1.f;

	//向自身发送事件,通过被动技能接收属性加点
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(GetAvatarActor(), AttributeTag, Payload);

	//判断Avatar是否基础角色接口
	if(GetAvatarActor()->Implements<UPlayerInterface>())
	{
		IPlayerInterface::Execute_AddToAttributePoints(GetAvatarActor(), -1); //减少一点可分配属性点
	}
}

最后,我们在控制器里,调用ASC对应的函数

void UAttributeMenuWidgetController::UpgradeAttribute(const FGameplayTag& AttributeTag)
{
	URPGAbilitySystemComponent* ASC = CastChecked<URPGAbilitySystemComponent>(AbilitySystemComponent);
	ASC->UpgradeAttribute(AttributeTag);
}

接着编译代码,打开被动技能应用的GE,我们之前在里面增加了经验的设置
在这里插入图片描述
由于设置对应的属性是通过标签设置的,我们接着增加四个主要属性的修改,如果没有设置对应的属性,那么传递的值将默认为0
在这里插入图片描述
在这里插入图片描述
只要事件传递过来,带有标签和数值,GE会通过SetbyCaller被设置。

实现按钮的点击事件

最后,我们想给点击事件绑定回调,在回调里调用控制器的升级属性函数,并将属性标签传递,我们在里面已经监听对应标签的属性变动的回调了,标签是设置在UI上的,已经有了标签,后续就是实现点击回调调用函数即可。
在这里插入图片描述
我们首先在WBP_TextValueRow蓝图,将控制器切换并设置为变量,这样方便后续和子蓝图的使用
在这里插入图片描述
然后在WBP_AttributePointsRow里,对按钮的点击回调进行绑定,调用属性面板控制器的升级属性
在这里插入图片描述
这样,我们实现了整个逻辑,现在就可以去测试

在这里插入图片描述

修复无法满血的bug

出现这个bug的原因是因为在我们设置满血的时候,最大血量或者最大蓝量的属性还没有应用。解决这个bug需要在属性变动的回调里面修改,我们可以根据比例将属性值进行提升,或者设置一个变量,在属性变动时,进行修改。
如果按比例修改,我们可以按照官方的RPG进行修改,这里设置一下设置变量修改。
我们创建两个变量,在升级时设置将其设置为true

//将血量和蓝量填充满, 我们将设置变量
bFillHealth = true;
bFillMana = true;

覆写PostAttributeChange,它会在属性变动时触发

virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override; //属性变动后回调函数

在里面如果最大血量或者蓝量变动,并且变量为true,那么我们将蓝量或者血量填满

void URPGAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
{
	Super::PostAttributeChange(Attribute, OldValue, NewValue);

	if(Attribute == GetMaxHealthAttribute() && bFillHealth)
	{
		SetHealth(GetMaxHealth());
		bFillHealth = false;
	}

	if(Attribute == GetMaxManaAttribute() && bFillMana)
	{
		SetMana(GetMaxMana());
		bFillMana = false;
	}
}
  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE5的载具(Vehicle)是一个非常强大的工具,可以用于创建各种类型的车辆,包括汽车、卡车、摩托车等。为了获得最佳的载具属性设置,建议遵循以下几个步骤: 1. 设置载具类型 在创建载具时,需要设置其类型。可以选择车辆类型(Vehicle Type)为四轮驱动(FourWheeledVehicle)、两轮驱动(TwoWheeledVehicle)或者其他类型。选择正确的载具类型可以为后续的属性设置提供更好的基础。 2. 设置车轮属性 在载具中,车轮(Wheels)是最重要的组成部分之一。需要设置车轮的大小、位置、碰撞形状等属性。在设置碰撞形状时,需要根据车轮的形状和大小进行调整,以确保碰撞检测的准确性。 3. 设置车辆属性设置车辆属性时,需要考虑到载具的重心、质量分布、悬挂系统等因素。重心的位置会影响车辆的稳定性和转向性能,质量分布会影响车辆的加速和刹车性能,悬挂系统则会影响车辆的行驶舒适性和稳定性。 4. 设置驱动类型 在设置驱动类型时,可以选择前驱、后驱或四驱等不同的驱动方式。需要根据载具的类型和用途来选择适当的驱动类型,以获得最佳的驱动性能和稳定性。 5. 调整模拟参数 最后,可以根据实际需求来调整模拟参数,比如车速、加速度、转向速度、刹车力度等。需要进行充分的测试和调整,以确保载具在模拟中表现出最佳的性能和稳定性。 以上是一些建议,具体的属性设置应根据实际情况和需求进行调整。需要注意的是,载具属性设置是一个相对复杂的过程,需要进行充分的测试和调整,以确保获得最佳的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值