目录
1. 创建粒子类
2. 实现传送
3. 动画设置
????????这节将会完成课程设置的第二次作业之一 —— 实现角色的粒子位移技能,即发射出一个特殊的粒子,经过一定时间后,该粒子会爆炸并将角色传送到爆炸位置。
1. 创建粒子类
? ? ? ? 考虑到后面制作的粒子都需要实现爆炸效果,所以在SurProjectileBase中添加实现爆炸的函数和OnActorHit事件函数,爆炸函数仅包括最基本的爆炸后销毁自身的功能,方便子类可以根据自己的需求来进行重写。绑定事件的方法已经在第6、12节的爆炸桶内容出现过,重写函数的内容已经在第7节打开宝箱的文章中出现过。
// SurProjectileBase.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()
{
// IsPendingKill确保对象有效,防止重复destroy
if (ensure(IsPendingKill()))
{
// 触发爆炸时的特效,特效样式在UE中设置ImpactVFX变量
UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());
Destroy();
}
}
????????由于这个技能需要实现额外的传送角色代码,因此我们要在UE中创建一个新的粒子类SurDashProjectile,在创建时选择“显示所有类”,并通过搜索选择,从上一节创建的基类SurProjectileBase派生出来。
? ? ? ? 课程中在DashProjectile中设置两个时间,经过DetonateDelay后生成爆炸特效,然后再经过TeleportDelay后传送角色。将控制爆炸的TimerHandle_DelayedDetonate声明为类的保护成员,当粒子提前遇到障碍物爆炸调用Explode函数时,我们可以手动清除这个Timer,从而防止触发两次。相对的,控制传送延迟的Timer不涉及这个问题,因此定义为局部变量即可:
#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;
};
2. 实现传送
????????实现角色的传送,可以直接使用UE的TeleportTo函数接口。这部分需要注意的是,一进入Explode就要清除控制爆炸的Timer,防止提前触发Hit事件爆炸后、原有的计时器结束再次触发。
#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();
}
????????接下来就是常规的绑定按键操作,在UE中创建蓝图类,然后设置发射的粒子为该蓝图类,这已经在之前的内容出现过很多次,故不再赘述。
3. 动画设置
????????最后,在创建的蓝图类中设置粒子的动画。一共需要设置两个动画:一是发射出去粒子的样子,对应ProjectileBase中的Effect Comp;二是爆炸时的动画,对应刚刚声明的ImpactVFX。
? ? ? ? 我设置的特效分别为P_Gideon_Burden_Projectile和P_Gideon_Burden_DoT2_End,注意ImpactVFX要设置名字带有End或Destroy等会自动结束的动画,否则爆炸后的粒子会一直停留在世界中,大家可以根据喜好任意选择粒子特效。
? ? ? ? 在C++中我们将两个延迟使用UPROPERTY暴露到了UE中,所以此时可以很方便地调节数据。我将爆炸延迟设置为1,传送延迟设置为0.2,即正常情况下粒子飞行1s后再经过0.2s,角色会传送到粒子最终位置。但如果粒子遇到障碍物,则代码中清除Timer的语句就会发挥作用,在0.2s后直接传送角色,效果如下:
????????最后我想说的是,课程的作业部分由于没有足够的指引,复现的难度确实比课程内容更大,但同时也给了我很多迫使自己摸索解决问题的机会。所以也十分建议大家可以先自行探索尝试实现作业内容,这对加深对UE的理解十分有用。
|