http://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Delegates/
主要就是在角色死亡之后 向全局广播 我死了 这个事件
然后因为Controller对Tank绑定了OnDeath事件(死后就执行OnControlledTankDeath())
h:(玩家控制的角色)(Tank.h)
在include下面写:
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FTankDelegate); //以F开头的代理类型 FTankDelegate OnDeath; //创建了一个全局的事件(代理),代理的变量cpp:(同上)
在TakeDamage(之前写的)中 生命值为负数时广播 死亡信息
float ATank::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) { int32 DamagePoint = FPlatformMath::RoundToInt(DamageAmount); //将伤害(float)通过四舍五入转为int,强制转型会向下取整 int32 DamageToApply = FMath::Clamp<int>(DamagePoint, 0, CurrentHp); //防止把血打到负数 CurrentHp -= DamageToApply; if (CurrentHp <= 0) { //Tank Dead UE_LOG(LogTemp, Warning, TEXT("Tank Dead,Tank Name:%s"), *GetName()); OnDeath.Broadcast(); //触发事件 向全局广播说 我死了 } return DamageToApply; }接着让AIController或者和PlayerController接受到死亡的信息
一般来说 是可以在 BeginPlay()里面 绑定上死亡的事件
但虚幻里面 Controller的生成一般会比Actor(Pawn)的生成要快(Controller生成了 Actor还没出来)所以可能会绑定不到事件
所以要用新的方法才能绑定到我们控制的Actor(Pawn)
virtual void SetPawn(APawn* InPawn) override; //真正有一个被我们控制的Pawn,保证可以拿到物体
h:(Controller)
virtual void SetPawn(APawn* InPawn) override; UFUNCTION() void OnControlledTankDeath(); //当控制的角色(Tank)死亡的时候cpp:(Controller)
void ATankAIController::SetPawn(APawn* InPawn) { Super::SetPawn(InPawn); if (InPawn) { auto PossessedTank = Cast<ATank>(InPawn); //转成Tank类才能绑定在Tank类写的OnDeath if (!PossessedTank)return; //绑定上死亡事件(引入死后做什么事的函数) PossessedTank->OnDeath.AddUniqueDynamic(this, &ATankAIController::OnControlledTankDeath); } } void ATankAIController::OnControlledTankDeath() { //死后做什么事 if (GetControlledTank()) { GetControlledTank()->DetachFromControllerPendingDestroy(); //死亡之后就不动了 } }而对于玩家(PlayerController)可以用(也要重写SetPawn方法)
但是用此方法要注意 是否在其他方法内 会获取错误的Pawn ,进入观察者模式之后 Pawn就会变成空指针,会导致UE4崩溃
void ATankPlayerController::OnControlledTankDeath() { //死后做什么事 StartSpectatingOnly(); //玩家死了之后进入观察者模式 }玩家死亡之后的重生:
h:(PlayerController)
#include "GameFramework/PlayerState.h" #include "TimerManager.h" #include "GameFramework/PlayerStart.h" #include "Kismet/GameplayStatics.h" UPROPERTY(EditAnywhere) float RespawnTime = 5.0f; FTimerHandle TankRespawnHandle; ATank* MyTank = nullptr;cpp:(PlayerController)
将上述的方法OnControlledTankDeath改为
void ATankPlayerController::OnControlledTankDeath() { //死后做什么事 MyTank = GetControlledTank(); //先获取准备死掉的Tank 这样之后重生的时候可以直接使用这个死掉的Tank,还有一种方法是重新设置一个新的Tank,只不过设置要重新调所以可能会麻烦一点 PlayerState->bIsSpectator = true; //PlayerState.h中自带的设置玩家状态 死了之后设置成Spectator ChangeState(NAME_Spectating); //改为Spectating观战中 GetWorldTimerManager().SetTimer( //TimerManager.h延迟之后执行TankRespawn用于复活 TankRespawnHandle, //返回一个FTimerHandle值 this, //谁用这个东西 &ATankPlayerController::TankRespawn, //需要调用的函数 RespawnTime, //延迟的时间 false //是否持续调用函数(true就持续复活???) ); } void ATankPlayerController::TankRespawn() { GetWorldTimerManager().ClearTimer(TankRespawnHandle); //为了安全 避免重复调用 TArray<AActor*> PlayerStarts; //GameFramework/PlayerStart.h 用于找出生点,下述方法会传出一个所有PlayerStarts的数组 UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerStart::StaticClass(), PlayerStarts); //Kismet/GameplayStatics.h AActor* SpawnTarget = PlayerStarts[0]; //只有一个PlayerStart 所以是第0位 if (MyTank == nullptr)return; MyTank->ResetTankHealth(); //在Tank中设置血量设置回满血的方法 MyTank->SetActorTransform(SpawnTarget->GetTransform()); Possess(MyTank); PlayerState->bIsSpectator = false; ChangeState(NAME_Playing); }