😊

【UE5 BP&C++】ランダムに地形を爆発させる

2024/09/17に公開

はじめに

今回からUnreal Engine5の記録・開発にあたっての備忘録を記録したいと思います.
ほとんどが,ほかの方の記事や動画をベースに改良を加えたような記事になると思います.
最終的には本を出せればいいと思います.(個人感想)

対象者

  • Unreal Engine5 をインストールしてある
  • Unreal Engine5 をほとんど触れたことのない人(初学者)

免責事項

間違っている個所やエラーが出てしまう箇所が出てくると思います.訂正しますので,コメント欄にてお知らせください.
また,削除依頼がありましたら,お知らせください.(すぐに対応いたします)

本題

今回はランダムに爆発させるギミックを作成したいと思います.

参考資料

  • 秀和システム sig著 Unreal Engine4 アクションゲームブループリント入門
    ISBN 978-4-7980-5590-9
  • Qiita UE4パーティクルシステムについてのメモ
  • zer0から始めるプログラミング生活

プロジェクトの作成

まず,以下の赤く囲っている箇所をクリック(または緑の箇所)

プロジェクト名の作成

以下の通りにテンプレートの設定,プロジェクトの保存場所,プロジェクト名を変更してみましょう.③はC++も使うためc++を選択しております.

プロジェクトの立ち上げ

時間がかかると思いますが(特にC++の場合),立ち上がったら以下の画像になると思います.
長くなるので,ビューポートの解説はしません.(スミマセン...)
本にするときに解説できればいいと思います.....

<BPを選択した場合>



<C++を選択した場合>

スターターコンテンツを入れ忘れた場合..

間違えてスターターコンテンツにチェックを入れなかった場合後からプロジェクトに適応させる方法があります.(作成したときにやらかしました...)

  1. +追加 > 機能またはコンテンツパックを追加をクリック

  2. コンテンツ>スターターコンテンツ(Starter Content)>プロジェクトに追加をします.

BPからの説明

ブループリント(Blueprint)とは

ブループリントとは,Unreal Engine独自のビジュアルでコードが作れる(ノーコード)の言語です.利点としては,非エンジニアの人でも開発することができるため,簡易実装として作業効率を上げることができます.Blueprint とかいてプループリントと読みます.ブループリントって何回もいうのはつらいので,略してBPと呼びます.

とりあえず動かしてみましょう

上のプレイボタンを押すかまたはAlt+Pを押してキャラクターを動かしてみましょう.

実行結果

card

Unreal Engineのいいところ

Unreal Engineのいいところは,テンプレートが最初から用意されていて,面倒なキャラクター操作などをやらなくても最低限の操作ができてしまうところだと思います.

いざ実践(BP)

ではいよいよ本題に入って,ダメージ処理アクターを使ってキャラクターにダメージを与えたいと思います!

アクターは,ゲーム画面上に置かれるオブジェクトのことを指します.舞台で劇をする俳優みたいな...

アクターの作成

  1. 左下のコンテンツドロワーをクリック(またはControlキー+Spaceバー)
  2. 空いている個所を右クリック>ブループリントクラスを選ぶ
  3. アクターを選択する
  4. 名前を「BP_DamageFloor」にする.(BPはブループリントであることを示します)

フォルダーの作成

  1. 左下のコンテンツドロワーをクリック(またはControlキー+Spaceバー)
  2. 空いている個所を右クリック>新規フォルダを選択
  3. 名前を「Blueprints」にする.
  4. その中に先ほど作ったアクターをドラック&ドロップ
  5. ここに移動をクリック
  6. 「Blueprints」フォルダをダブルクリック
  7. 「BP_DamageFloor」をダブルクリック

プログラムの作成

長らくお待たせしました.続いてプログラムを作成します.
以下の画像が開かれたと思います.

ダメージの床を作成

以下の画像の丸の部分をクリックしてイベントグラフに移動します。
イベントグラフでプログラムを実際に書いていきます.

以下のグラフをコピー&ペーストしてください
<iframe src="https://blueprintue.com/render/20gqucec/" scrolling="no" allowfullscreen></iframe>
見えない場合は以下の画像通りに作成してください.

当たり判定の追加

以下の画像のとおりにコンポーネント(パーツ)を追加してください.

コリジョン(当たり判定)を設定する

コリジョン(当たり判定)を以下のように
これでポーン(操作できるアクター)以外の当たり判定はなくなりました.

ビューポートにおいてみましょう

プレイボタンを押して確認してみましょう

一瞬爆発すると思います.

当たったときの処理

次に当たったときの処理を作成します.
BP_DamageFloorのイベントグラフを開いてください

Boxを選択>イベント>OnComponentBeginOverlapの+をクリックしてください

以下のURLのノードをコピー&ペーストしよう
<iframe src="https://blueprintue.com/render/nrwzkpoo/" scrolling="no" allowfullscreen></iframe>
できない場合は、以下の画像のとおりに作ってみよう

コンパイル!

以下のように保存とコンパイルをしておきましょう.
コンパイルとは,人間の言葉を機械の言葉へと変換する手続きだと思っていればOK

スポーナーを作る

ランダムにスポーンする範囲を別に決めてあげましょう.
ブループリントを作成して,名前を「BP_DamageSpawner」とします.

以下のノードをコピー&ペーストします.
できない場合は,画像のとおりに検索ノードを作成していきます.

コンパイルをしましょう

ビューポートへ配置しましょう.

右にあるアクタの配置タブをクリックしてください.

Navmeshboundsvolumeを選択して,ビューポートへドラッグ&ドロップしましょう

スケール値を変更します.

プレイしてみましょう!

ではプレイしてみましょう!
プレイボタンを押してください

忘れた人

ダメージが当たったか確認してみる

このままではダメージが当たっているかわかりません.
とりあえず,ダメージが当たったら「Damage」と表示させてみましょう

コンテンツドロワー>ThridPerson>Blueprints>BP_ThirdPersonCharacterをクリックして開いてください.

以下のように作成します.(コピー&ペースト)
<iframe src="https://blueprintue.com/render/oeory_h7/" scrolling="no" allowfullscreen></iframe>
できない場合は画像のとおりに作成してください

プレイボタンでプレイ!

では最後にプレイを押してみましょう
左上にDamageの文字が出ていれば成功です.

C++で作成

次はUnrealC++で作成してみましょう.

ライブコーディングについて

ライブコーディングとは,エディタが立ち上がっていてもC++の変更を修正してくれる機能です.
ただ,ライブコーディングは閉じてしまうと変更が元に戻ってしまうため再度コンパイルする必要があります.
詳しくはこちらの本が便利です.

めんどくさいので,エディタを閉じるときなどはライブコーディングを消しておきましょう.

ビルドボタンの隣にある三点リーダをクリックします.

ライブコーディングの有効化のチェックを外してください.

Visual studio 2022(Visual studio 2019)の操作
Visual studioを開きます.

ツールバーのBuild(ビルド)>プロジェクト名をビルドをクリックします.
Ctrl+Bでもビルドができます.

終了です.

アクタの作成

BPとは異なり,アクターは以下の通りに作成します.
ツールバーのTool(ツール)>新規C++クラスを選択

親クラスを選択タブからアクタを選択し次へを押します.

クラスの作成タブで以下の画像のように選択します.

初期は以下の2つのファイルが開くと思います.

ACPP_DamageFloor.h
ACPP_DamageFloor
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CPP_DamageFloor.generated.h"

UCLASS()
class PRACTICE1_API ACPP_DamageFloor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACPP_DamageFloor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

ACPP_DamageFloor.cpp
ACPP_DamageFloor.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "CPP_DamageFloor.h"

// Sets default values
ACPP_DamageFloor::ACPP_DamageFloor()
{
 	// 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;

}

// Called when the game starts or when spawned
void ACPP_DamageFloor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ACPP_DamageFloor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}


コンポーネントの追加

Componentを追加します.
CPP_DamageFloor.hに移動します.

CPP_DamageFloor.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CPP_DamageFloor.generated.h"

class UBoxComponent;   // 追加してください.

UCLASS()
class PRACTICE1_API ACPP_DamageFloor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACPP_DamageFloor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

// 以下追加してください
protected:
    UPROPERTY(EditAnywhere,BlueprintReadWrite)
    TObjectPtr<UBoxComponent> BoxCmp;

public:
	UFUNCTION()
	void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

};

.cppに移動してください.
以下の通りに書いてみましょう.

CPP_DamageFloor.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "CPP_DamageFloor.h"
#include "Components/BoxComponent.h" // 追加してください
#include "Kismet/GameplayStatics.h"  // 追加してください

// Sets default values
ACPP_DamageFloor::ACPP_DamageFloor()
{
 	// 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;

    // 以下を追加してください
    // コンポーネントの作成
    BoxCmp = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
    RootComponent = BoxCmp;

    // 範囲を設定する
    BoxCmp->SetBoxExtent(FVector(300,300,300));
    // コリジョンの種類を変える
	BoxCmp->SetCollisionProfileName(TEXT("Trigger"));
    BoxCmp->OnComponentBeginOverlap.AddDynamic(this,&ACPP_DamageFloor::OnBeginOverlap);

    // 以上
}

// Called when the game starts or when spawned
void ACPP_DamageFloor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ACPP_DamageFloor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}


void ACPP_DamageFloor::OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor == UGameplayStatics::GetPlayerPawn(GetWorld(), 0))
	{
		TSubclassOf<UDamageType> damagetype = UDamageType::StaticClass();
		AController* control = UGameplayStatics::GetPlayerController(GetWorld(), 0);
		UGameplayStatics::ApplyDamage(OtherActor, 50.0f, control, this, damagetype);
	}
}

次に,ダメージを受ける側(プレイヤー)を実装します.
(プロジェクト名)Character.cpp と ヘッダーファイルを開いて下さい.

practice1Character.h
Apractice1Character.h
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "practice1Character.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
struct FInputActionValue;

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

UCLASS(config=Game)
class Apractice1Character : public ACharacter
{
	GENERATED_BODY()

	/** Camera boom positioning the camera behind the character */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* CameraBoom;

	/** Follow camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FollowCamera;
	
	/** MappingContext */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputMappingContext* DefaultMappingContext;

	/** Jump Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* JumpAction;

	/** Move Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* MoveAction;

	/** Look Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* LookAction;

public:
	Apractice1Character();
	

protected:

	/** Called for movement input */
	void Move(const FInputActionValue& Value);

	/** Called for looking input */
	void Look(const FInputActionValue& Value);
			

protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	
	// To add mapping context
	virtual void BeginPlay();

public:
	/** Returns CameraBoom subobject **/
	FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
	/** Returns FollowCamera subobject **/
	FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }


};

practice1Character.cpp
practice1Character.cpp
// Copyright Epic Games, Inc. All Rights Reserved.

#include "practice1Character.h"
#include "Engine/LocalPlayer.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"

DEFINE_LOG_CATEGORY(LogTemplateCharacter);

//////////////////////////////////////////////////////////////////////////
// Apractice1Character

Apractice1Character::Apractice1Character()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
		
	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...	
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate

	// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
	// instead of recompiling to adjust them
	GetCharacterMovement()->JumpZVelocity = 700.f;
	GetCharacterMovement()->AirControl = 0.35f;
	GetCharacterMovement()->MaxWalkSpeed = 500.f;
	GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;
	GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;
	GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;

	// Create a camera boom (pulls in towards the player if there is a collision)
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(RootComponent);
	CameraBoom->TargetArmLength = 400.0f; // The camera follows at this distance behind the character	
	CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller

	// Create a follow camera
	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
	FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm

	// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
	// are set in the derived blueprint asset named ThirdPersonCharacter (to avoid direct content references in C++)
}

void Apractice1Character::BeginPlay()
{
	// Call the base class  
	Super::BeginPlay();
}

//////////////////////////////////////////////////////////////////////////
// Input

void Apractice1Character::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	// Add Input Mapping Context
	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(DefaultMappingContext, 0);
		}
	}
	
	// Set up action bindings
	if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
		
		// Jumping
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

		// Moving
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &Apractice1Character::Move);

		// Looking
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &Apractice1Character::Look);
	}
	else
	{
		UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
	}
}

void Apractice1Character::Move(const FInputActionValue& Value)
{
	// input is a Vector2D
	FVector2D MovementVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		// find out which way is forward
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	
		// get right vector 
		const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

		// add movement 
		AddMovementInput(ForwardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}

void Apractice1Character::Look(const FInputActionValue& Value)
{
	// input is a Vector2D
	FVector2D LookAxisVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		// add yaw and pitch input to controller
		AddControllerYawInput(LookAxisVector.X);
		AddControllerPitchInput(LookAxisVector.Y);
	}
}

ヘッダーファイルに以下を追加します.

practice1Character.h
public:
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCausor) override;

.cppファイルに関数を書いてみましょう.

practice1Character.cpp
float Apractice1Character::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCausor)
{
	if (Cast<ACPP_DamageFloor>(DamageCausor))
	{
		UKismetSystemLibrary::PrintString(this, TEXT("Damage!!"), true, true, FColor::Cyan, 10.0f, NAME_None);
        return DamageAmount;
	}
    return 0.0f;
}

スポーナーの作成

それでは発射装置であるすぽーなーを生成します.
ツールバー>ツール>新規のC++クラス...を選択します.

適切なクラスの名前を決めます.
できたら「クラスを生成」をクリックします.

CPP_Spawner.h
ACPP_Spawner.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CPP_Spawner.generated.h"

UCLASS()
class PRACTICE1_API ACPP_Spawner : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACPP_Spawner();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};



CPP_Spawner.cpp
ACPP_Spawner.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "CPP_Spawner.h"

// Sets default values
ACPP_Spawner::ACPP_Spawner()
{
 	// 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;

}

// Called when the game starts or when spawned
void ACPP_Spawner::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void ACPP_Spawner::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}



ではここに追加していきましょう.
まずはコンポーネントを追加します.

CPP_Spawner.h
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CPP_Spawner.generated.h"

class UBoxComponent;
class UParticleSystem;

UCLASS()
class PRACTICE1_API ACPP_Spawner : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ACPP_Spawner();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

private:

	UPROPERTY(EditAnywhere,BlueprintReadWrite,meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UBoxComponent> BoxCmp;
// 続く

次にタイマー用の関数を定義します.

CPP_Spawner.h
// 続き
	// Sets default values for this actor's properties
	ACPP_Spawner();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

private:
    // タイマー関数
    void ExecuteTimer();

private:
	UPROPERTY(EditAnywhere,BlueprintReadWrite,meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UBoxComponent> BoxCmp;
// 続く

次にスポーンするクラスとBoxの大きさを定義します.

CPP_Spawner.h
// 続き
private:

	UPROPERTY(EditAnywhere,BlueprintReadWrite,meta = (AllowPrivateAccess = "true"))
	TObjectPtr<UBoxComponent> BoxCmp;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CPP|Particle", meta = (AllowPrivateAccess = "true"))
	FVector Extents = FVector(500.0f,500.0f,500.0f);
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CPP|Particle", meta = (AllowPrivateAccess = "true"))
	TSubclassOf<ACPP_DamageFloor> SpawnClass;


private:
	// タイマー関数
	void ExecuteTimer();
};

次にコンポーネントを取得します.

CPP_Spawner.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "CPP_Spawner.h"
#include "Components/BoxComponent.h"
#include "Particles/ParticleSystem.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"

// Sets default values
ACPP_Spawner::ACPP_Spawner()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;  // 変更
	BoxCmp = CreateDefaultSubobject<UBoxComponent>(TEXT("Box"));
	RootComponent = BoxCmp;

	BoxCmp->SetBoxExtent(Extents);
}

タイマーを設定します.

CPP_Spawner.cpp

// Called when the game starts or when spawned
void ACPP_Spawner::BeginPlay()
{
	Super::BeginPlay();
	FTimerHandle handle;
	GetWorld()->GetTimerManager().SetTimer(handle, this, &ACPP_Spawner::ExecuteTimer,0.2f,true);
	
	FString path = TEXT("/Script/Engine.Blueprint'/Game/Blueprints/CPPBP_DamageFloor.CPPBP_DamageFloor_C'");
	SpawnClass = TSoftClassPtr<ACPP_DamageFloor>(FSoftObjectPath(*path)).LoadSynchronous();
}

最後にタイマー関数を実装します

CPP_Spawner.cpp
void ACPP_Spawner::ExecuteTimer()
{
	FVector Location = BoxCmp->GetComponentLocation();
	FVector Point = UKismetMathLibrary::RandomPointInBoundingBox(Location, Extents);
	FTransform PointTransform = FTransform(FRotator::ZeroRotator, Point, FVector::ZeroVector);

	TObjectPtr<ACPP_DamageFloor> damage = GetWorld()->SpawnActor<ACPP_DamageFloor>(SpawnClass);

	if (damage)
	{
		damage->SetActorLocation(Point);
	}

}

パーティクルを表示する

パーティクルを表示させます.
CPP_DamageFloorに移動します.

CPP_DamagerFloor.h
// 続き
protected:
	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	TObjectPtr<UBoxComponent> BoxCmp;

	UPROPERTY(EditAnywhere,BlueprintReadWrite)
	TObjectPtr<UParticleSystem> Particles;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<UParticleSystemComponent> ParticleCmp;



public:
	UFUNCTION()
	void OnBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

private:
	void DestroyActor();
};
CPP_DamageFloor.cpp
// 続き
// Called when the game starts or when spawned
void ACPP_DamageFloor::BeginPlay()
{
	Super::BeginPlay();
	// パーティクルをスポーンする
	if (Particles)
	{
		ParticleCmp->Activate(); // 有効化
		// パーティクルを設定する
		ParticleCmp->SetTemplate(Particles);
		// エミッターを設定する
		ParticleCmp->SetEmitterEnable(NAME_None, true);

	}

	FTimerHandle handle;
	GetWorld()->GetTimerManager().SetTimer(handle, this, &ACPP_DamageFloor::DestroyActor, 0.4f, false);
}

void ACPP_DamageFloor::DestroyActor()
{
	Destroy(true);
}

結果

爆発がきちんと確認ができると思います.

質問や感想は

質問や感想は以下のDiscordにて承ります.
https://discord.gg/wFEs7qBCwz

Discussion