// YanWei.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "AuraProjectile.generated.h"
class UNiagaraSystem;
class UProjectileMovementComponent;
class USphereComponent;
UCLASS()
class AURA_API AAuraProjectile : public AActor
{
GENERATED_BODY()
public:
AAuraProjectile();
//设置为公共public 因为之后可能会用它改变projectile的参数
UPROPERTY(VisibleAnywhere)
TObjectPtr<UProjectileMovementComponent> ProjectileMovement;
protected:
virtual void BeginPlay() override;
virtual void Destroyed() override;
/*// 声明一个 Server RPC,用于在服务器上销毁对象
UFUNCTION(Server, Reliable, WithValidation)
void ServerDestroyProjectile();*/
//TODO:: Projectile Overlap
UFUNCTION()
void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,UPrimitiveComponent* OtherComp,int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
private:
UPROPERTY(VisibleAnywhere)
TObjectPtr<USphereComponent> Sphere;
UPROPERTY(EditAnywhere)
TObjectPtr<UNiagaraSystem> ImpactEffect;
UPROPERTY(EditAnywhere)
TObjectPtr<USoundBase> ImpactSound;
bool bHit = false; //用于判断当前火球是否和其它物体产生了碰撞
};
// YanWei.
#include "Actor/AuraProjectile.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h"
AAuraProjectile::AAuraProjectile()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true; //此类在服务器运行,然后复制到每个客户端
Sphere = CreateDefaultSubobject<USphereComponent>("Sphere");
SetRootComponent(Sphere);
Sphere->SetCollisionEnabled(ECollisionEnabled::QueryOnly); //设置其只用作查询使用
Sphere->SetCollisionResponseToChannels(ECR_Ignore); //设置其忽略所有碰撞检测
Sphere->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap); //设置其与世界动态物体产生重叠事件
Sphere->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Overlap); //设置其与世界静态物体产生重叠事件
Sphere->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap); //设置其与Pawn类型物体产生重叠事件
//创建发射组件
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMovement");
ProjectileMovement->InitialSpeed = 550.f; //设置初始速度
ProjectileMovement->MaxSpeed = 550.f; //设置最大速度
ProjectileMovement->ProjectileGravityScale = 0.f; //设置重力影响因子,0为不受影响
}
void AAuraProjectile::BeginPlay()
{
Super::BeginPlay();
Sphere->OnComponentBeginOverlap.AddDynamic(this,&AAuraProjectile::OnSphereOverlap);
}
void AAuraProjectile::Destroyed()
{
//如果没有权威性且未触发重叠事件时, 在销毁之前播放击中特效
if (!bHit && !HasAuthority())
{
UGameplayStatics::PlaySoundAtLocation(this, ImpactSound, GetActorLocation(),FRotator::ZeroRotator);
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, ImpactEffect, GetActorLocation());
}
Super::Destroyed(); // 调用父类的销毁逻辑
}
/*
void AAuraProjectile::ServerDestroyProjectile_Implementation()
{
// 确保在服务器上销毁
Destroy();
}
bool AAuraProjectile::ServerDestroyProjectile_Validate()
{
// 可以在这里添加验证逻辑,检查销毁请求是否合理
return true;
}*/
void AAuraProjectile::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// 播放 Sound 和 Niagara 特效
UGameplayStatics::PlaySoundAtLocation(this, ImpactSound, GetActorLocation(),FRotator::ZeroRotator);
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, ImpactEffect, GetActorLocation());
//如果拥有权威性,在触发重叠后,销毁它,如果没有,将bHit设置为true,代表它已经触发了重叠事件,并且已经播放了几种特效,
//但是没有对火球的控制权,无法自身直接销毁。 需添加销毁函数
if (HasAuthority())
{
//拥有权限时销毁(服务器端)
Destroy(); // 服务器端控制,直接销毁
}else
{
//无权威时销毁(客户端)
bHit = true; // 客户端设置 bHit 为 true,等待服务器处理
//ServerDestroyProjectile();// 客户端请求服务器
}
}
AActor::Destroyed()
是 Unreal Engine 中 AActor
类的一个成员函数,用于在对象销毁时执行一些清理和回调操作。这个函数主要用于处理对象销毁前的一些必要步骤。接下来,我会详细解析它的工作流程,并说明如何通过 RPC 来同步销毁对象。
AActor::Destroyed()
详细解析
void AActor::Destroyed()
{
RouteEndPlay(EEndPlayReason::Destroyed); // 通知游戏框架该 Actor 正在销毁
ReceiveDestroyed(); // 调用接收销毁事件的虚拟函数
OnDestroyed.Broadcast(this); // 广播 OnDestroyed 委托
}
1. RouteEndPlay(EEndPlayReason::Destroyed)
- 该函数用于通知游戏框架对象即将销毁,并开始执行销毁过程。
EEndPlayReason::Destroyed
是枚举值,表示该对象是由于销毁事件而结束生命周期。它会触发AActor::EndPlay
函数中的相关清理工作,例如注销事件、移除对象等。
2. ReceiveDestroyed()
- 这是一个虚拟函数,可以在子类中重写。
Destroyed()
会在销毁时调用它,允许子类在销毁前进行一些自定义的清理工作。 - 如果你需要在销毁对象时做一些额外处理(例如,释放资源、停止计时器等),你可以在子类中重写这个函数。
- 示例:
void AMyActor::ReceiveDestroyed() { Super::ReceiveDestroyed(); // 在这里添加销毁前的清理逻辑,比如关闭文件句柄或清理自定义资源 }
3. OnDestroyed.Broadcast(this)
- 这是一个委托(Delegate),用于在对象销毁时广播通知,其他订阅该事件的对象可以做出相应的处理。
- 你可以订阅
OnDestroyed
委托,以便在对象销毁时触发某些操作,例如更新 UI 或执行某些动画。 - 示例:
然后你可以在MyActor->OnDestroyed.AddDynamic(this, &AMyGameMode::OnActorDestroyed);
AMyGameMode
类中定义处理销毁的函数:void AMyGameMode::OnActorDestroyed(AActor* DestroyedActor) { // 处理销毁的 Actor UE_LOG(LogTemp, Warning, TEXT("Actor destroyed: %s"), *DestroyedActor->GetName()); }
通过 RPC 同步销毁
如果你希望通过 RPC 来同步销毁对象,可以结合 Destroyed()
方法和 RPC 来确保对象在服务器和所有客户端上都能正确销毁。
假设你希望通过 RPC 从客户端向服务器请求销毁一个 Actor,可以按照以下步骤操作。
定义服务器端 RPC 方法
你需要定义一个服务器端的 RPC 方法,来在服务器上销毁对象。
UFUNCTION(Server, Reliable, WithValidation)
void ServerDestroy();
实现 RPC
void AMyActor::ServerDestroy_Implementation()
{
Destroy(); // 销毁对象
}
bool AMyActor::ServerDestroy_Validate()
{
return true; // 可选验证函数,检查客户端是否有权限进行销毁
}
客户端调用 RPC
当客户端需要销毁一个对象时,调用该 RPC 方法:
MyActor->ServerDestroy(); // 客户端请求服务器销毁对象
通过这种方式,客户端通过 RPC 向服务器请求销毁对象,服务器接收到请求后执行 AActor::Destroyed()
,触发销毁相关的清理和委托操作。这样可以保证销毁操作在服务器和客户端同步进行。