文章目录
一、创建粒子类
- 考虑到粒子类都有爆炸效果,可以在基类中实现,继承后再实现具体功能的重写。在
SProjectileBase
中添加实现爆炸的函数和OnActorHit
事件函数,爆炸函数仅包括最基本的爆炸后销毁自身的功能。
cpp
// SProjectileBase.h
UPROPERTY(EditDefaultsOnly, Category = "Effects")
UParticleSystem* ImpactVFX;//VFX指Visual effects, 即视觉特效,负责爆炸时的特效
UFUNCTION()
virtual void OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
void Explode(); // 爆炸函数
// SurProjectileBase.cpp
void ASurProjectileBase::OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
Explode();
}
// BlueprintNativeEvent需要加上后缀_Implementation
void ASurProjectileBase::Explode_Implementation()
{
// 判断对象是否已经被标记为需要销毁的函数
if (ensure(!IsPendingKill()))
{
// 触发爆炸时的特效,特效样式在UE中设置ImpactVFX变量
UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());
Destroy();
}
}
为什么在上面这段代码中,
.h
中声明的是Explode
,而.cpp
中是Explode_Implementation()
?
- 因为
Explode()
函数被声明为BlueprintNativeEvent
,这意味着它既可以在C++中实现,也可以在蓝图中重载。对于这样的函数UE提供了一种特殊的机制来处理它们的实现,这就是在C++代码中实现函数时,需要使用_Implementation
后缀。BlueprintNativeEvent
的作用:
UE会自动生成两个版本的函数声明:
①纯虚函数(即头文件中声明的Explode()),可以在蓝图中重载。
②C++中实际的函数实现,命名为Explode_Implementation()。
- 从
SProjectileBase
派生出DashProjectile
中,设置两个时间,经过DetonateDelay
后生成爆炸特效,然后再经过TeleportDelay
后传送角色。控制爆炸的TimerHandle_DelayedDetonate
,当粒子提前遇到障碍物爆炸调用Explode
函数时,我们可以手动清除 这个Timer
(要在多个地方访问,设置为类变量),从而防止触发两次。相对的,控制传送延迟的Timer
不涉及这个问题,因此定义为局部变量即可。
cpp
#pragma once
#include "CoreMinimal.h"
#include "SurProjectileBase.h"
#include "SurDashProjectile.generated.h"
UCLASS()
class SURKEAUE_API ASurDashProjectile : public ASurProjectileBase
{
GENERATED_BODY()
public:
ASurDashProjectile();
protected:
UPROPERTY(EditDefaultsOnly, Category = "Teleport")
float TeleportDelay; // 传送延迟
UPROPERTY(EditDefaultsOnly, Category = "Teleport")
float DetonateDelay; // 爆炸延迟
// hit后清除计时器
FTimerHandle TimerHandle_DelayedDetonate;
virtual void Explode_Implementation() override;
void TeleportInstigator();
virtual void BeginPlay() override;
};
二、实现传送
- 实现角色的传送,可以直接使用UE的
TeleportTo
函数接口。需要注意的是,一进入Explode
就要清除控制爆炸的Timer
,防止提前触发Hit事件(基类ProjectileBase
的OnActorHit
函数)爆炸后、原有的计时器结束再次触发。 TeleportTo
函数是UE中用于将Actor
移动到指定位置和旋转方向的函数。函数原型如下:
cpp
bool TeleportTo(const FVector& DestLocation, const FRotator& DestRotation, bool bIsATest = false, bool bNoCheck = false);
- 参数:
①目标位置
②目标旋转
③如果为true
,则函数仅模拟 传送过程,不实际移动Actor
。这可以用于测试 某个位置是否可以安全地传送(即是否会导致碰撞等问题)。false
正常传送。
④如果为true
,则忽略碰撞检测 ,直接将Actor
传送到目标位置和旋转方向。如果为false
(默认值),则在传送之前会进行碰撞检测,以确保传送不会将Actor置于一个非法 或不安全的位置。
cpp
#include "SurDashProjectile.h"
#include "Kismet/GameplayStatics.h"
#include "Particles/ParticleSystemComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
ASurDashProjectile::ASurDashProjectile()
{
TeleportDelay = 0.2f;
DetonateDelay = 0.2f;
MoveComp->InitialSpeed = 6000.f;
}
void ASurDashProjectile::BeginPlay()
{
Super::BeginPlay();
// 到爆炸时间后,触发Explode()
GetWorldTimerManager().SetTimer(TimerHandle_DelayedDetonate, this, &ASurDashProjectile::Explode, DetonateDelay);
}
void ASurDashProjectile::Explode_Implementation()
{
// 可能有碰到障碍物提前爆炸的情况,因此要清除Timer防止被调用两次
GetWorldTimerManager().ClearTimer(TimerHandle_DelayedDetonate);
// 爆炸特效
UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());
// 粒子爆炸后关闭系统、停止移动、停止碰撞
EffectComp->DeactivateSystem();
MoveComp->StopMovementImmediately();
SetActorEnableCollision(false);
// 到传送时间后,触发传送
FTimerHandle TimerHandle_DelayedTeleport;
GetWorldTimerManager().SetTimer(TimerHandle_DelayedTeleport, this, &ASurDashProjectile::TeleportInstigator, TeleportDelay);
// 不调用base中的Explode中的destroy自身,因为传送时间计时器需要一点时间
//Super::Explode_Implementation();
}
void ASurDashProjectile::TeleportInstigator()
{
AActor* ActorToTeleport = GetInstigator();
if (ensure(ActorToTeleport))
{
// 传送时保持角色原始Rotation
ActorToTeleport->TeleportTo(GetActorLocation(), ActorToTeleport->GetActorRotation(), false, false);
}
// 之前没有调用,现在销毁
Destroy();
}
三、动画设置
- 在创建的蓝图类中设置粒子的动画。一共需要设置两个动画:一是发射出去 粒子的样子,对应
ProjectileBase
中的Effect Comp
;二是爆炸时 的动画,对应ImpactVFX
。
cpp
//SProjectileBase.h
UPROPERTY(EditDefaultsOnly, Category = "Effect")
UParticleSystem* ImpactVFX;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UParticleSystemComponent* EffectComp;
(1)UParticleSystem
是指向一个粒子系统资源 的指针。主要用于定义一个粒子系统的类型,即它是什么样的效果,包括每个粒子的发射行为、运动轨迹、生命周期、材质动画、光效等。这只是对粒子系统的一种静态描述,类似于一个蓝图模板。它本身不会在游戏世界中渲染出任何东西。
- 常用于在代码中指定某个粒子系统资源,之后可以通过其他函数(如
UGameplayStatics::SpawnEmitterAtLocation
)在游戏世界中实例化 并播放这个粒子系统。 - 当使用
ParticleSystem
来指定某个动画效果时,实际上是在选择一个已经设计好的动画模板。比如ImpactVFX
指向一个包含爆炸效果的粒子系统,这个系统已经配置了所有爆炸时需要的动画。
(2)UParticleSystemComponent
是指向一个粒子系统组件 的指针。用于在游戏世界中动态播放 和控制粒子效果 。UParticleSystemComponent
实际上是 UParticleSystem
的一个运行时实例,还管理粒子系统的生命周期 、位置 、旋转 、缩放 以及其他动态属性 。可以通过这个组件来启动 、停止 或者调整粒子系统的参数。
- 当需要在
Actor
中创建并直接管理一个粒子效果时,通常会使用UParticleSystemComponent
。例如,角色身上持续播放的火焰效果、或物体爆炸后残留的烟雾效果。
(3)使用场景对比: - 如果只需要指定一种特效,并且打算在游戏运行时通过某种方法(例如在特定事件发生时)动态生成这个特效,通常使用
UParticleSystem
,然后通过函数如UGameplayStatics::SpawnEmitterAtLocation
来生成和播放特效。 - 如果需要一个长期存在于
Actor
中的特效(例如火焰、烟雾),或者需要频繁控制特效的行为(如停止、重启、改变参数),则应使用UParticleSystemComponent
,并将其附加到Actor
中进行管理。