目录
通过委托实现角色死亡
使用的是非动态多播
声明两个仅供C++使用的委托:委托要用F开头
//用于广播角色死亡 DECLARE_MULTICAST_DELEGATE(FOnDeath) //用于广播角色血量 DECLARE_MULTICAST_DELEGATE_OneParam(FOnHealthChange,float)
创建这两个委托变量
//创建委托变量 FOnDeath OnDeath; FOnHealthChange OnHealthChange;
在血量减少时执行委托:Broadcast()
void USTUHealthComponent::OnTakeAnyDamageComponent(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatedBy, AActor* DamageCauser) { if (Damage <= 0.0f || IsDead()) return; Health = FMath::Clamp(Health - Damage, 0.0f, MaxHealth); OnHealthChange.Broadcast(Health); if (IsDead()) { //执行委托,通知所有订阅这个委托的类 OnDeath.Broadcast(); } }
在角色类中订阅绑定这两个委托
void ASTUBaseCharacter::BeginPlay() { Super::BeginPlay(); check(HealthComponent); check(HealthTextComponent); check(GetCharacterMovement()); OnHealthChange(HealthComponent->GetHealth()); //订阅委托 动态多播用AddDynamic 非动态多播用AddUObject HealthComponent->OnDeath.AddUObject(this, &ASTUBaseCharacter::OnDeath); HealthComponent->OnHealthChange.AddUObject(this, &ASTUBaseCharacter::OnHealthChange); } void ASTUBaseCharacter::OnDeath() { UE_LOG(BaseCharacterLog, Display, TEXT("Player %s is dead"), *GetName()); PlayAnimMontage(DeathAnimMotage); //关闭移动 GetCharacterMovement()->DisableMovement(); //五秒后销毁 SetLifeSpan(5.0f); } void ASTUBaseCharacter::OnHealthChange(float Health) { HealthTextComponent->SetText(FText::FromString(FString::Printf(TEXT("Hp %.0f"), Health))); }
创建蒙太奇动画:UAnimMontage
UPROPERTY(EditDefaultsOnly, Category = "Animation") UAnimMontage* DeathAnimMotage;
播放蒙太奇:PlayAnimMontage()
PlayAnimMontage(DeathAnimMotage);
关闭蒙太奇的自动混出,不然播放结束后会回到初始状态
角色死亡后切换到旁观者类
Controller->ChangeState():切换到旁观者 NAME_Spectating,NAME_Playing
GetCapsuleComponent()->SetCollisionResponseToAllChannels():设置碰撞体类型
#include "GameFramework/Controller.h" void ASTUBaseCharacter::OnDeath() { UE_LOG(BaseCharacterLog, Display, TEXT("Player %s is dead"), *GetName()); PlayAnimMontage(DeathAnimMotage); //关闭移动 GetCharacterMovement()->DisableMovement(); //五秒后销毁 SetLifeSpan(LifeSpanOnDeath); //切换到旁观者类 if (Controller) { Controller->ChangeState(NAME_Spectating); } //关闭胶囊体碰撞 GetCapsuleComponent()->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); }
角色从高处掉落扣血
要使用到Character中的Landed,这里可以选择重载Landed函数或者使用LandedDelegate委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLandedSignature, const FHitResult&, Hit);
选择使用委托:
创建两个变量用于处理不同高度的伤害
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Movement") FVector2D LandedDamageVelocity = FVector2D(900.0f, 1200.0f); UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Movement") FVector2D LandedDamage = FVector2D(10.0f, 100.0f); UFUNCTION(BlueprintCallable) void OnGroundLanded(const FHitResult& Hit);
使用AddDynamic:动态多播
FMath::Abs():取绝对值
FMath::GetMappedRangeValueClamped(),就是蓝图的MapRangeClamped
TakeDamage:ApplyDamage的底层实现
GetVelocity(),GetCharacterMovement()->Velocity:获取速度
LandedDelegate.AddDynamic(this, &ASTUBaseCharacter::OnGroundLanded); void ASTUBaseCharacter::OnGroundLanded(const FHitResult& Hit) { //也可以直接用GetVelocity().Z const float FallVelocityZ = FMath::Abs(GetCharacterMovement()->Velocity.Z); UE_LOG(BaseCharacterLog, Display, TEXT("Velocity %f "), FallVelocityZ); if (FallVelocityZ < LandedDamageVelocity.X) { return; } const float FinalDamage = FMath::GetMappedRangeValueClamped(LandedDamageVelocity, LandedDamage, FallVelocityZ); TakeDamage(FinalDamage, FDamageEvent{}, nullptr, nullptr); UE_LOG(BaseCharacterLog, Display, TEXT("Damage %f "), FinalDamage); }