【UE4C++-ActionRougelike-08】黑洞攻击&闪现效果
1、创建投射物基类
创建投射物基类MyProjectileBase.h。
UCLASS(ABSTRACT) //声明为抽象基类,防止在游戏过程中创建该类的实例
class ACTIONROGUELIKE_API AMyProjectileBase : public AActor
{
GENERATED_BODY()
protected:
//粒子系统指针,声明为编辑器默认可编辑属性(EditDefaultsOnly),用于指定抛体在碰撞时播放的粒子效果
UPROPERTY(EditDefaultsOnly, Category = "Effects")
UParticleSystem* ImpactVFX;
//球体组件用于判断碰撞
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USphereComponent* SphereComp;
//添加抛体组件用于运动
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UProjectileMovementComponent* MoveComp;
//添加粒子系统组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UParticleSystemComponent* EffectComp;
//声明了一个用于处理 Actor 碰撞事件的函数
UFUNCTION()
virtual void OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
//声明了一个蓝图可调用的、Native 实现的事件函数,用于处理爆炸效果。
UFUNCTION(BluePrintCallable, BlueprintNativeEvent)
void Explode();
//类的组件初始化完成后被调用的函数
virtual void PostInitializeComponents() override;
public:
// Sets default values for this actor's properties
AMyProjectileBase();
};
在MyProjectileBase.cpp中实现投射物的基础功能。
#include "MyProjectileBase.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
AMyProjectileBase::AMyProjectileBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//创建球体组件
SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");
//设置碰撞体的collision profile
SphereComp->SetCollisionProfileName("Projectile");
SphereComp->OnComponentHit.AddDynamic(this, &AMyProjectileBase::OnActorHit);
RootComponent = SphereComp;
//创建抛体组件
MoveComp = CreateDefaultSubobject<UProjectileMovementComponent>("MoveComp");
MoveComp->bRotationFollowsVelocity = true; //随速度方向更新旋转
MoveComp->bInitialVelocityInLocalSpace = true; //控制初始速度在本地坐标空间(角色)进行计算
MoveComp->ProjectileGravityScale = 0.0f; //表示抛体不受重力影响
MoveComp->InitialSpeed = 8000; //抛体组件初速度
EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");
EffectComp->SetupAttachment(RootComponent);
}
void AMyProjectileBase::OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
Explode();
}
/*
当一个虚函数在基类中声明为 UFUNCTION(BlueprintNativeEvent) 时,派生类可以通过在其实现中添加 _Implementation 后缀来实现该虚函数的具体逻辑。这样的虚函数被称为蓝图本地事件(Blueprint Native Event),允许在蓝图中覆盖该函数并添加自定义的蓝图逻辑。可以在蓝图中覆盖 MyFunction 并添加自定义的蓝图逻辑。在运行时,当调用 MyFunction 时,如果派生类没有实现_Implementation,则基类中的空实现将被调用。如果派生类实现了 _Implementation,则派生类中的实现将被调用*/
void AMyProjectileBase::Explode_Implementation()
{
if (ensure(!IsPendingKill()))
{
UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());
Destroy();
}
}
void AMyProjectileBase::PostInitializeComponents()
{
Super::PostInitializeComponents();
}
2、修改角色发射投射物逻辑
由于角色进行魔法攻击、黑洞攻击和闪现效果都是发射一种投射物,所以可以共用生成抛射物代码,创建SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)方法,角色具体进行某一类攻击时只需调用该方法填入对应投射物类型作为参数
void AMyCharacter::SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)
{
if (ensureAlways(ClassToSpawn)) {
//获取骨骼体手部的插槽作为投射物类的生成点
FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
//参数设置
FActorSpawnParameters SpawnParams;
//设置为:总是生成;粒子在角色手部插槽生成,可能会与角色发生碰撞
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
//设置抛体的发起者为玩家自身
SpawnParams.Instigator = this;
//用于碰撞检测
FCollisionShape Shape;
Shape.SetSphere(20.0f);
//碰撞查询参数,并设置当前角色对象this忽略
FCollisionQueryParams Params;
Params.AddIgnoredActor(this);
//碰撞对象查询参数,并添加需要查询的对象类型
FCollisionObjectQueryParams ObjParams;
ObjParams.AddObjectTypesToQuery(ECC_WorldDynamic);
ObjParams.AddObjectTypesToQuery(ECC_WorldStatic);
ObjParams.AddObjectTypesToQuery(ECC_Pawn);
//通过 CameraComp(相机组件)获取相机位置作为射线起点:TraceStart
FVector TraceStart = CameraComp->GetComponentLocation();
//通过角色的控制旋转获取射线方向,并乘以距离因子5000,得到射线终点:TraceEnd。
FVector TraceEnd = CameraComp->GetComponentLocation() + (GetControlRotation().Vector() * 5000);
FHitResult Hit;
//进行射线检测,如果有碰撞,则更新TraceEnd为碰撞点的位置
if (GetWorld()->SweepSingleByObjectType(Hit, TraceStart, TraceEnd, FQuat::Identity, ObjParams, Shape, Params))
{
TraceEnd = Hit.ImpactPoint;
}
//获取生成物的选择角度
FRotator ProjRotation = FRotationMatrix::MakeFromX(TraceEnd - HandLocation).Rotator();
//根据生成物的生成位置和旋转角度获得生成物变换矩阵
FTransform spawnTM = FTransform(ProjRotation, HandLocation);
//生成指定类ClassToSpawn的抛体对象,并传入生成的位置、旋转(spawnTM)和生成参数 SpawnParams 进行生成
GetWorld()->SpawnActor<AActor>(ClassToSpawn, spawnTM, SpawnParams);
}
}
3、黑洞攻击
3.1 创建黑洞攻击类蓝图
以投射物基类MyProjectileBase为父类创建BlackHoleProjectile_BP蓝图类。添加RadialForce组件来实现黑洞的吸引力,设定RadialForce组件半径和力的大小来实现不同效果的黑洞攻击,并移除Pawn被RadialForce影响,修改Initial Life Span来设定黑洞攻击的持续时间。
3.2 绑定角色技能按键
在MyCharacter中添加BlackHoleAttack()方法,并绑定按键事件。
void AMyCharacter::BlackHoleAttack()
{
//播放攻击动画
PlayAnimMontage(AttackAnim);
//设置定时器,延迟生成攻击特效,0.2s后调用PrimaryAttack_TimeElapsed()
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &AMyCharacter::BlackHoleAttack_TimeElapsed, 0.2f);
}
void AMyCharacter::BlackHoleAttack_TimeElapsed()
{
SpawnProjectile(BlackHoleProjectileclass);
}
最终效果如下:
4、闪现效果
4.1 创建闪现投射物子类
创建闪现投射物子类MyDashProjectile.h
UCLASS()
class ACTIONROGUELIKE_API AMyDashProjectile : public AMyProjectileBase
{
GENERATED_BODY()
protected:
//用于设置传送延迟的时间
UPROPERTY(EditDefaultsOnly, Category = "Teleport")
float TeleportDelay;
//用于设置引爆延迟的时间
UPROPERTY(EditDefaultsOnly, Category = "Teleport")
float DetonateDelay;
//计时器用于触发闪现
FTimerHandle TimerHandle_DelayedDetonate;
//闪现效果
virtual void Explode_Implementation() override;
void TeleportInstigator();
virtual void BeginPlay() override;
public:
AMyDashProjectile();
};
在MyDashProjectile.cpp中实现投射物的基础功能。
AMyDashProjectile::AMyDashProjectile()
{
TeleportDelay = 0.2f;
DetonateDelay = 0.2f;
MoveComp->InitialSpeed = 6000.0f;
}
//实现闪现效果
void AMyDashProjectile::Explode_Implementation()
{
//清除了之前设置的定时器TimerHandle_DelayedDetonate
GetWorldTimerManager().ClearTimer(TimerHandle_DelayedDetonate);
//在当前位置生成粒子效果
UGameplayStatics::SpawnEmitterAtLocation(this, ImpactVFX, GetActorLocation(), GetActorRotation());
//调用EffectComp->DeactivateSystem()方法停止粒子效果的播放
EffectComp->DeactivateSystem();
//调用MoveComp->StopMovementImmediately()方法停止移动
MoveComp->StopMovementImmediately();
//调用SetActorEnableCollision(false) 函数禁用了碰撞
SetActorEnableCollision(false);
FTimerHandle TimerHandle_DelayedTeleport;
//调用GetWorldTimerManager().SetTimer()设置了一个延迟执行的定时器TimerHandle_DelayedTeleport:
//在指定的延迟时间后调用了AMyDashProjectile::TeleportInstigator()方法
GetWorldTimerManager().SetTimer(TimerHandle_DelayedTeleport, this, &AMyDashProjectile::TeleportInstigator, TeleportDelay);
}
//定时器触发时所执行的传送逻辑
void AMyDashProjectile::TeleportInstigator()
{
//获取了投射物的Instigator(发射者)
AActor* ActorToTeleport = GetInstigator();
if (ensure(ActorToTeleport))
{
//将一个游戏角色或物体瞬间传送到指定位置
ActorToTeleport->TeleportTo(GetActorLocation(), ActorToTeleport->GetActorRotation(), false, false);
}
}
void AMyDashProjectile::BeginPlay()
{
Super::BeginPlay();
//设置一个延迟执行的定时器TimerHandle_DelayedDetonate,如果没有发生碰撞,则会在指定的延迟时间后调用AMyDashProjectile::Explode_Implementation()函数,从而触发闪现效果
GetWorldTimerManager().SetTimer(TimerHandle_DelayedDetonate, this, &AMyDashProjectile::Explode_Implementation, DetonateDelay);
}
4.2 创建闪现投射物蓝图
创建蓝图DashProjectile_BP,并设置相应粒子效果
4.3 绑定角色技能按键
在MyCharacter中添加Dash()方法,并绑定按键事件
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
PlayerInputComponent->BindAction(TEXT("Dash"), IE_Pressed, this, &AMyCharacter::Dash);
}
void AMyCharacter::Dash()
{
//播放攻击动画
PlayAnimMontage(AttackAnim);
//设置定时器,延迟生成攻击特效,0.2s后调用PrimaryAttack_TimeElapsed()
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &AMyCharacter::Dash_TimeElapsed, 0.2f);
}
void AMyCharacter::Dash_TimeElapsed()
{
SpawnProjectile(DashProjectileclass);
}
在Edit->Project Settings->Input->Action Mapping中添加闪现的按键绑定
4.4 效果演示
总结
① FHitResult
FHitResult
是 Unreal Engine 中的一个结构体,用于表示射线、碰撞等查询操作的结果。它包含了关于碰撞结果的详细信息,例如碰撞点、法线、击中的物体等。
FHitResult
的定义如下:
cppCopy codestruct FHitResult
{
float Time; // 碰撞发生的时间
AActor* Actor; // 被击中的物体的指针
UPrimitiveComponent* Component; // 被击中的物体的 Primitive 组件指针
FVector Location; // 碰撞点的世界坐标
FVector Normal; // 碰撞点的法线
FVector ImpactPoint; // 碰撞点的物体相对坐标
FVector ImpactNormal; // 碰撞点的物体相对法线
FVector TraceStart; // 射线起点的世界坐标
FVector TraceEnd; // 射线终点的世界坐标
};
FHitResult
可以在 Unreal Engine 中广泛应用于进行射线查询、碰撞检测、击中反馈等操作。它是 Unreal Engine 中常用的碰撞结果表示方式之一。
② SweepSingleByObjectType
SweepSingleByObjectType
是 Unreal Engine 中的一个碰撞检测函数,用于检测一个物体是否与指定的物体类型发生碰撞。
c++Copy codebool UWorld::SweepSingleByObjectType(
FHitResult& OutHit,
const FVector& Start,
const FVector& End,
const FQuat& Rot,
const FCollisionObjectQueryParams& ObjectQueryParams,
const FCollisionShape& CollisionShape,
const FCollisionQueryParams& Params /* = FCollisionQueryParams::DefaultQueryParam */
) const
其中参数的含义如下:
OutHit
: 用于返回碰撞检测结果的FHitResult
结构体。Start
和End
: 碰撞检测的起始点和终止点。Rot
: 碰撞检测时的旋转。ObjectQueryParams
: 碰撞检测使用的物体查询参数,用于指定要检测的物体类型。CollisionShape
: 碰撞检测使用的碰撞形状。Params
: 碰撞检测使用的查询参数,如 TraceType、Ignore Actors 等。可选参数,默认值为FCollisionQueryParams::DefaultQueryParam
。
UWorld::SweepSingleByObjectType
函数会对起始点和终止点之间的线段进行碰撞检测,检测过程中使用指定的碰撞形状和查询参数,并根据指定的物体查询参数进行碰撞判定。如果发生碰撞,结果将保存在 OutHit
参数中,并返回 true
;否则,返回 false
。
在使用 UWorld::SweepSingleByObjectType
函数时,需要注意传递正确的参数值,包括起始点和终止点、碰撞形状、查询参数和物体查询参数,以确保碰撞检测的准确性和预期的碰撞行为。
③ TeleportTo()方法
用于将一个游戏角色或物体瞬间传送到指定位置,需确保调用对象是一个 AActor
的派生类的实例
virtual void TeleportTo(const FVector& DestLocation, const FRotator& DestRotation, bool bIsAbsoluteRotation = false);
参数:
DestLocation
:FVector
类型,目标位置的三维向量。表示要传送到的目标位置在游戏世界中的位置。DestRotation
:FRotator
类型,目标旋转的旋转角度。表示在传送到目标位置后,角色或物体应该面朝的方向。bIsATest
:bool
类型,指示是否将传送视为测试。如果设置为true
,则传送将被视为测试,不会对角色或物体进行实际传送;如果设置为false
,则传送将是实际传送。bNoCheck
:bool
类型,指示是否要进行位置检查。如果设置为true
,则在传送过程中将不会进行位置检查;如果设置为false
,则会进行位置检查。
返回值:
bool
类型,表示传送是否成功。如果传送成功,返回true
;如果传送失败,返回false
。
功能:
TeleportTo()
函数将游戏角色或物体瞬间传送到指定位置,并根据提供的旋转角度重新定向角色或物体的朝向。传送后,角色或物体将立即出现在目标位置,并在目标旋转角度下朝向正确的方向。