UE5多人MOBA+GAS 13、添加死亡、复活逻辑以及布娃娃含物理资产的修改调整


使用GE为角色添加定时的Tag控制死亡时间

1、添加死亡Tag

去Tag中添加新的标签

CRUNCH_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Stats_Dead)
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Stats_Dead, "Stats.Dead", "死亡")

2、创建死亡GE,并完成相关配置

添加新的GEGE_Death
在这里插入图片描述
使用拥有持续时间策略,后面幅度计算类型会采用属性基础,用等级来计算死亡时间,添加死亡标签到角色上
在这里插入图片描述

3、在AbilitySystemComponent中监听属性的变化,调用GE来添加Tag到角色上

到能力组件CAbilitySystemComponent中监听生命的变化,对生命值为0的时候添加死亡的GE

public:
	UCAbilitySystemComponent();
	// 添加GE
	void AuthApplyGameplayEffect(TSubclassOf<UGameplayEffect> GameplayEffect, int Level = 1);

private:
	void HealthUpdated(const FOnAttributeChangeData& ChangeData);
	
	// 死亡效果
	UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects")
	TSubclassOf<UGameplayEffect> DeathEffect;
	
	
UCAbilitySystemComponent::UCAbilitySystemComponent()
{
	GetGameplayAttributeValueChangeDelegate(UCAttributeSet::GetHealthAttribute()).AddUObject(this, &UCAbilitySystemComponent::HealthUpdated);
}

void UCAbilitySystemComponent::AuthApplyGameplayEffect(TSubclassOf<UGameplayEffect> GameplayEffect, int Level)
{
	if (GetOwner() && GetOwner()->HasAuthority())
	{
		FGameplayEffectSpecHandle EffectSpecHandle = MakeOutgoingSpec(GameplayEffect, Level, MakeEffectContext());
		ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
	}
}

void UCAbilitySystemComponent::HealthUpdated(const FOnAttributeChangeData& ChangeData)
{
	if (!GetOwner() || !GetOwner()->HasAuthority()) return;

	if (ChangeData.NewValue <= 0.0f)
	{
		// 角色死亡
		if (DeathEffect)
		{
			AuthApplyGameplayEffect(DeathEffect);
		}
	}
}

4、在角色中监听ASC传入的Tag以及Tag的层数,来响应不同的函数

在能力系统中使用了GE来获取Tag,然后来到角色中监听ASC获取到的Tag做出相应的响应,到CCharacter中:

#pragma region GAS组件相关
private:
	// 绑定GAS属性改变委托
	void BindGASChangeDelegates();
	// 死亡标签更新
	void DeathTagUpdated(const FGameplayTag Tag, int32 NewCount);

#pragma endregion

#pragma region 死亡和复活 (Death and Respawn)
private:
	void StartDeathSequence();
	void Respawn();
#pragma endregion

在角色构造函数中添加,绑定要在实现能力组件和属性初始化的下面。

ACCharacter::ACCharacter()
{
	PrimaryActorTick.bCanEverTick = true;
	// 禁用网格的碰撞功能
	GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	CAbilitySystemComponent = CreateDefaultSubobject<UCAbilitySystemComponent>(TEXT("CAbilitySystemComponent"));
	CAttributeSet = CreateDefaultSubobject<UCAttributeSet>(TEXT("CAttributeSet"));

	OverHeadWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("OverHeadWidgetComponent"));
	OverHeadWidgetComponent->SetupAttachment(GetRootComponent());

	// 绑定GAS属性改变委托
	BindGASChangeDelegates();
}

void ACCharacter::BindGASChangeDelegates()
{
	if (CAbilitySystemComponent)
	{
		CAbilitySystemComponent->RegisterGameplayTagEvent(TGameplayTags::Stats_Dead).AddUObject(this, &ACCharacter::DeathTagUpdated);
	}
}

void ACCharacter::DeathTagUpdated(const FGameplayTag Tag, int32 NewCount)
{
	// 标签数量不为0时,死亡。为0则复活。
	if (NewCount != 0)
	{
		StartDeathSequence();
	}else
	{
		Respawn();
	}
}

void ACCharacter::StartDeathSequence()
{
	UE_LOG(LogTemp, Warning, TEXT("%s:狗带"),*GetName())
}

void ACCharacter::Respawn()
{
	UE_LOG(LogTemp, Warning, TEXT("%s:复活"),*GetName())
}

在角色蓝图中给角色添加死亡GE
在这里插入图片描述
在这里插入图片描述

添加死亡、复活的逻辑

1、在CAbilitySystemComponent中,添加回血回蓝函数

	// 回满血、满蓝效果
	void ApplyFullStatEffect();
	
	// 满血、满蓝效果
	UPROPERTY(EditDefaultsOnly, Category = "Gameplay Effects")
	TSubclassOf<UGameplayEffect> FullStatEffect;
void UCAbilitySystemComponent::ApplyFullStatEffect()
{
	AuthApplyGameplayEffect(FullStatEffect);
}

2、在CCharacter中添加动画蒙太奇,添加一些逻辑

#pragma region UI
	/**
	 * @brief 设置头顶状态条的启用状态\n
	 * 启用或禁用头顶UI组件的显示。\n
	 * @param bIsEnabled 是否启用头顶UI
	 */
	void SetStatusGaugeEnabled(bool bIsEnabled);
#pragma endregion
#pragma region 死亡和复活 (Death and Respawn)
private:
	// 死亡蒙太奇
	UPROPERTY(EditDefaultsOnly, Category = "Death")
	TObjectPtr<UAnimMontage> DeathMontage;
	
	// 播放死亡动画
	void PlayDeathAnimation();
	
	// 死亡
	void StartDeathSequence();
	// 复活
	void Respawn();

	// 子类中实现
	virtual void OnDead();
	virtual void OnRespawn();
#pragma endregion
void ACCharacter::SetStatusGaugeEnabled(bool bIsEnabled)
{
	// 清除定时器
	GetWorldTimerManager().ClearTimer(HeadStatGaugeVisibilityUpdateTimerHandle);
	if (bIsEnabled)
	{
		// 启动头顶血条
		ConfigureOverHeadStatusWidget();
	}else
	{
		// 关闭头顶血条
		OverHeadWidgetComponent->SetHiddenInGame(true);
	}
}

void ACCharacter::PlayDeathAnimation()
{
	if (DeathMontage)
	{
		PlayAnimMontage(DeathMontage);
	}
}

void ACCharacter::StartDeathSequence()
{
	OnDead();
	// 播放死亡动画
	PlayDeathAnimation();
	// 关闭头顶血条
	SetStatusGaugeEnabled(false);
	
	// 禁用移动
	GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
	// 禁用碰撞
	GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	UE_LOG(LogTemp, Warning, TEXT("%s:狗带"),*GetName())
}

void ACCharacter::Respawn()
{
	OnRespawn();
	SetStatusGaugeEnabled(true);
	// 开启移动
	GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
	// 开启碰撞
	GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
	GetMesh()->GetAnimInstance()->StopAllMontages(0.f);
	if (CAbilitySystemComponent)
	{
		CAbilitySystemComponent->ApplyFullStatEffect();
	}
	UE_LOG(LogTemp, Warning, TEXT("%s:复活"),*GetName())
}

void ACCharacter::OnDead()
{
}

void ACCharacter::OnRespawn()
{
}

3、到子类CPlayerCharacter中覆盖OnDeadOnRespawn

#pragma region Input
	// 输入开关
	void SetInputEnabledFromPlayerController(bool bEnabled);
#pragma endregion
#pragma region 死亡和复活 (Death and Respawn)
	virtual void OnDead() override;
	virtual void OnRespawn() override;
#pragma endregion
void ACPlayerCharacter::SetInputEnabledFromPlayerController(bool bEnabled)
{
	// 获取玩家控制器
	APlayerController* PlayerController = GetController<APlayerController>();
	// 如果玩家控制器为空,则返回
	if (!PlayerController)
	{
		return;
	}

	if (bEnabled)
	{
		// 启用玩家控制器输入
		EnableInput(PlayerController);
	}
	else
	{
		// 禁用玩家控制器输入
		DisableInput(PlayerController);
	}
}

void ACPlayerCharacter::OnDead()
{
	// 死亡,禁用玩家控制器输入
	SetInputEnabledFromPlayerController(false);
}

void ACPlayerCharacter::OnRespawn()
{
	// 复活,启用玩家控制器输入
	SetInputEnabledFromPlayerController(true);
}

创建一个死亡蒙太奇,设置新的插槽,关闭自动混出
在这里插入图片描述

在动画蓝图中添加这个新的插槽
在这里插入图片描述
到角色中添加死亡蒙太奇
在这里插入图片描述
添加补状态的GE,这个GE在初始化属性的时候创建了
在这里插入图片描述
打死后又会复活:
在这里插入图片描述
在这里插入图片描述

添加布娃娃

#pragma region 死亡和复活 (Death and Respawn)
private:
	// 相对于网格的变换
	FTransform MeshRelativeTransform;
	
	// 死亡蒙太奇完成时间偏移
	UPROPERTY(EditDefaultsOnly, Category = "Death")
	float DeathMontageFinishTimeShift = -0.8f;
	
	// 死亡蒙太奇
	UPROPERTY(EditDefaultsOnly, Category = "Death")
	TObjectPtr<UAnimMontage> DeathMontage;

	// 死亡蒙太奇计时器句柄
	FTimerHandle DeathMontageTimerHandle;
	// 死亡蒙太奇完成处理
	void DeathMontageFinished();
	// 启用/禁用 布娃娃系统
	void SetRagdollEnabled(bool bIsEnabled);
	
	// 播放死亡动画
	void PlayDeathAnimation();
	
	// 死亡
	void StartDeathSequence();
	// 复活
	void Respawn();

	virtual void OnDead();
	virtual void OnRespawn();
#pragma endregion
void ACCharacter::BeginPlay()
{
	Super::BeginPlay();
	ConfigureOverHeadStatusWidget();

	MeshRelativeTransform = GetMesh()->GetRelativeTransform();
	//UE_LOG(LogTemp, Warning, TEXT("ACCharacter::BeginPlay,%hhd"),GetIsReplicated());
	//UE_LOG(LogTemp, Warning, TEXT("True是:,%hhd"),true);
}

void ACCharacter::DeathMontageFinished()
{
	SetRagdollEnabled(true);
}

void ACCharacter::SetRagdollEnabled(bool bIsEnabled)
{
	if (bIsEnabled)
	{
		GetMesh()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform); // 从父组件分离网格,但保持世界变换不变
		GetMesh()->SetSimulatePhysics(true); // 启用物理模拟
		GetMesh()->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly); // 仅启用物理碰撞
	}
	else
	{
		GetMesh()->SetSimulatePhysics(false); // 禁用物理模拟
		GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision); // 禁用碰撞
		GetMesh()->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); // 将网格重新附加到根组件,保持相对变换
		GetMesh()->SetRelativeTransform(MeshRelativeTransform); // 设置网格的相对变换
	}
}

void ACCharacter::PlayDeathAnimation()
{
	if (DeathMontage)
	{
		// 获取死亡蒙太奇的持续时间
		float MontageDuration = PlayAnimMontage(DeathMontage);
		GetWorldTimerManager().SetTimer(DeathMontageTimerHandle, this, &ACCharacter::DeathMontageFinished, MontageDuration + DeathMontageFinishTimeShift);
	}
}

void ACCharacter::StartDeathSequence()
{
	OnDead();
	// 播放死亡动画
	PlayDeathAnimation();
	// 关闭头顶血条
	SetStatusGaugeEnabled(false);
	// 禁用移动
	GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
	// 禁用碰撞
	GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	UE_LOG(LogTemp, Warning, TEXT("%s:狗带"),*GetName())
}

void ACCharacter::Respawn()
{
	OnRespawn();
	// 关闭布娃娃
	SetRagdollEnabled(false);
	// 开启血条
	SetStatusGaugeEnabled(true);
	// 开启移动
	GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_Walking);
	// 开启碰撞
	GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
	// 停止所有蒙太奇
	GetMesh()->GetAnimInstance()->StopAllMontages(0.f);
	// 应用全属性
	if (CAbilitySystemComponent)
	{
		CAbilitySystemComponent->ApplyFullStatEffect();
	}
	UE_LOG(LogTemp, Warning, TEXT("%s:复活"),*GetName())
}

用上了布娃娃系统后
在这里插入图片描述

对物理资产的修改调整

在这里插入图片描述
删除手上两个胶囊体
在这里插入图片描述
可以显示所有骨骼
在这里插入图片描述

1、添加胶囊体

在这里插入图片描述

2、添加约束(不然胶囊体之间会断开)

在这里插入图片描述
对约束增加一些限制
在这里插入图片描述
使用镜像创造对称的胶囊体(可以偷懒),对称的约束需要手动重新调制
在这里插入图片描述
给两个小腿也添加胶囊
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值