斯坦福UE4 C++课学习补充20:位移技能

文章目录

一、创建粒子类

  1. 考虑到粒子类都有爆炸效果,可以在基类中实现,继承后再实现具体功能的重写。在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()。
  1. 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;

}; 

二、实现传送

  1. 实现角色的传送,可以直接使用UE的TeleportTo函数接口。需要注意的是,一进入Explode就要清除控制爆炸的Timer,防止提前触发Hit事件(基类ProjectileBaseOnActorHit函数)爆炸后、原有的计时器结束再次触发
  2. 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();
} 

三、动画设置

  1. 在创建的蓝图类中设置粒子的动画。一共需要设置两个动画:一是发射出去 粒子的样子,对应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中进行管理。
相关推荐
奋斗的小花生37 分钟前
c++ 多态性
开发语言·c++
Nu11PointerException41 分钟前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
闲晨42 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
UestcXiye2 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
霁月风3 小时前
设计模式——适配器模式
c++·适配器模式
jrrz08284 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i4 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1074 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客4 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生5 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法