Delegates and Lambda Functions in Unreal Engine | 虚幻引擎 5.7 文档 | Epic Developer Community
委托 是一种泛型但类型安全的方式,可在C++对象上调用成员函数。可使用委托动态绑定到任意对象的成员函数,之后在该对象上调用函数,即使调用程序不知对象类型也可进行操作。复制委托对象很安全。
使用委托系统不需要知道和获取类的类型只需要绑定类的函数就可以调用,并像消息系统一样可以同时触发
实现TNT炸药类
首先需要一个可以引爆的tnt类继承与Actor
我们需要四个组件和对应的资源
StaticMeshComponent显示模型ParticleSystemComponent播放爆炸粒子特效AudioComponent播放音效RadialForceComponent释放径向爆炸的冲力,炸开周围物体
一个 isExplode变量存储是否已经爆炸,避免连续点击导致重复触发
一个 Explode()函数实现爆炸逻辑,一个 TryDistory()实现摧毁逻辑
// TNT.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TNT.generated.h"
UCLASS()
class MIXSTUDY_API ATNT : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATNT();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExplosionEffect")
TObjectPtr<class UStaticMesh> ExplosionMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExplosionEffect")
TObjectPtr<class UParticleSystem> ExplosionParticle;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ExplosionEffect")
TObjectPtr<class USoundBase> ExplosionAudio;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TObjectPtr<class UStaticMeshComponent> mesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite);
TObjectPtr<class UParticleSystemComponent> ExplosionParticleComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite);
TObjectPtr<class UAudioComponent> ExplosionAudioComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite);
TObjectPtr<class URadialForceComponent> ExplosionRadialForceComponent;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
bool isExplode = false;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION(BlueprintCallable)
void Explode();
UFUNCTION()
void TryDestroy();
};
// TNT.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TNT.h"
#include "Particles/ParticleSystemComponent.h"
#include "Components/AudioComponent.h"
#include "PhysicsEngine/RadialForceComponent.h"
// Sets default values
ATNT::ATNT()
{
// 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;
// 加载资源
ExplosionMesh = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("/Script/Engine.StaticMesh'/Game/Fab/Low-Poly_Tactical_Bomb_Device/bomb/StaticMeshes/bomb.bomb'")).Object;
ExplosionParticle = ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("/Script/Engine.ParticleSystem'/Game/StarterContent/Particles/P_Explosion.P_Explosion'")).Object;
ExplosionAudio = ConstructorHelpers::FObjectFinder<USoundBase>(TEXT("/Script/Engine.SoundWave'/Game/StarterContent/Audio/Explosion_Cue.Explosion_Cue'")).Object;
mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TNTMesh"));
RootComponent = mesh;
mesh->SetStaticMesh(ExplosionMesh);
mesh->SetRelativeScale3D(FVector(0.1f, 0.1f, 0.1f)); // 设置模型大小
mesh->SetSimulatePhysics(true); // 启用物理
ExplosionParticleComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ExplosionParticle"));
ExplosionParticleComponent->SetRelativeScale3D(FVector(10.f, 10.f, 10.f)); // 应为mesh是根组件,缩放会影响到子组件,所以将粒子放大
ExplosionParticleComponent->SetAutoActivate(false); // 禁用自动播放,不然关卡开启时就会自动播放爆炸特效
ExplosionParticleComponent->SetTemplate(ExplosionParticle);
ExplosionAudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("ExplosionAudio"));
ExplosionAudioComponent->SetupAttachment(RootComponent);
ExplosionAudioComponent->SetAutoActivate(false); // 禁用自动播放
ExplosionAudioComponent->SetSound(ExplosionAudio);
ExplosionRadialForceComponent = CreateDefaultSubobject<URadialForceComponent>(TEXT("ExplosionRadialComponent"));
ExplosionRadialForceComponent->SetupAttachment(RootComponent);
ExplosionRadialForceComponent->SetAutoActivate(false); // 禁用自动播放
ExplosionRadialForceComponent->ForceStrength = 0.f; // 关闭力场
//ExplosionRadialForceComponent->ImpulseStrength = 1000.f; // 保持默认
ExplosionRadialForceComponent->bImpulseVelChange = true; // 忽略物体重量对冲量的影响
ExplosionRadialForceComponent->Radius = 400.f; // 冲量半径
}
// Called when the game starts or when spawned
void ATNT::BeginPlay()
{
Super::BeginPlay();
// 在被生成时添加向前的冲量,实现抛出的效果
mesh->AddImpulse(GetActorForwardVector()*1000.f, NAME_None, true);
}
// Called every frame
void ATNT::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATNT::Explode()
{
if (isExplode) return;
isExplode = true;
mesh->SetSimulatePhysics(false);
mesh->SetVisibility(false); // 影藏模型
ExplosionRadialForceComponent->FireImpulse(); // 释放冲量
ExplosionParticleComponent->ActivateSystem(); // 播放特效和声音
ExplosionAudioComponent->Play();
ExplosionAudioComponent->OnAudioFinished.AddDynamic(this, &ATNT::TryDestroy); // 当音效播放完毕时调用摧毁的回调
// 可以发现这也是一个动态委托
}
void ATNT::TryDestroy()
{
// 直接摧毁,垃圾回收应该会帮我们完成剩下的事
GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Blue, FString("destory"));
UE_LOG(LogTemp, Warning, TEXT("Hello World"));
Destroy();
}
实现Character玩家类
这里不再讨论基本的移动控制部分代码
要实现远程同时引爆多个TNT可以使用多播委托
多播委托可以绑定多个函数,当委托触发时,将调用所有这些函数
首先创建一个多播委托
[!NOTE]
这个宏定义实际上会生成一个class定义,所以要放在Character类外
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnExplosion);
在Character类中声明此委托
[!NOTE]
这里一定要使用UPROPERTY()让UE建立反射不然会无法使用并报错
UPROPERTY(BlueprintAssignable)
FOnExplosion OnExplosion;
创建投掷与引爆函数
// .h
UFUNCTION(BlueprintCallable)
void LayTNT();
UFUNCTION(BlueprintCallable)
void ExplodeTNT();
// .cpp
void AMyCharacter::LayTNT()
{
// 导入TNT类的头文件
ATNT* tnt = GetWorld()->SpawnActor<ATNT>(GetActorLocation()+GetActorForwardVector()*50, GetActorRotation()); // 像角色面朝方向向前50生成TNT,以免生成时与玩家碰撞
OnExplosion.AddDynamic(tnt,&ATNT::Explode); // 将TNT的Explode帮绑定到委托
}
void AMyCharacter::ExplodeTNT()
{
OnExplosion.Broadcast(); // 委托调用所有已绑定的TNT类的Explode变量,实现远程引爆
}
绑定操作
最后将投掷与引爆绑定到玩家操作,这里使用增强输入,将左键右键映射到Axis1D(浮点)值
左键1右键-1
// .h
void uesItem(const struct FInputActionInstance& Instance);
// .cpp
void AMyCharacter::uesItem(const FInputActionInstance& Instance)
{
float value = Instance.GetValue().Get<float>();
if (value > 0) {
ExplodeTNT();
}
else
{
LayTNT();
}
}