文章目录
使用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中覆盖OnDead和OnRespawn
#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、添加约束(不然胶囊体之间会断开)

对约束增加一些限制

使用镜像创造对称的胶囊体(可以偷懒),对称的约束需要手动重新调制

给两个小腿也添加胶囊


3413

被折叠的 条评论
为什么被折叠?



