【UEC++游戏案例】向上的小松饼

发布时间 2023-11-04 16:42:46作者: 哟吼--小文文公主

一. 效果与资源准备

1.1 游戏演示效果

 

 

1.2 游戏资产素材与源码

 

 

1.3 前期准备

  1. 创建无初学者内容的空项目

  2. 将素材文件拷贝到项目 Content 文件夹下

 

  1. 搭建场景(用 3D 场景模拟 2D 游戏效果)

 

 

二. 创建角色类与全局相机

2.1 创建角色类

  1. 创建C++类(Character),重命名 Cake(Public)

 

  1. 创建继承自 Cake 的蓝图(实例化),重命名 BP_Cake,并存放在 Blueprints 文件夹下

 

  1. 设置 BP_Cake 模型资产,并调整胶囊体覆盖模型

 

  1. 创建 BP_GameModeBase 游戏模式(GameModeBase),并和 BP_Cake 一起挂载到项目设置,那么在场景中放置玩家出生点 Player Start 后,运行时会自动生成 BP_Cake 的玩家出生点位置

 

 

2.2 创建全局相机

  1. 创建C++类(Actor),重命名 Camera(Public)

 

  1. 创建继承自 Camera 的蓝图(实例化),重命名 BP_Camera,并存放在 Blueprints 文件夹下

 

  1. 指定默认相机

 

  1. 调整相机正交模式

 

 

三. 移动逻辑

3.1 角色水平方向跟随鼠标移动

  1. 获取 PlayerController,运行时显示鼠标

 

  1. 角色随鼠标移动

 

  1. 防止角色出画,添加阻挡体积

 

  1. 运行效果:

 

 

3.2 角色垂直方向跳跃

  1. 角色垂直方向跳跃

 

  1. 绑定操作映射

 

  1. 运行效果:

 

 

3.4 设置角色速度

  1. 设置角色在空中/落地 2种状态下的不同速度

 

  1. 设置角色 BP_Cake 蓝图物理细节参数

 

 

3.4 全局相机垂直跟随

  1. 相机左右并不移动,只需调整 Z 轴跟随

 

  1. 设置背景板和阻挡体积为可移动,随摄像机移动

 

  1. 运行效果:

 

 

 

 

四. 创建云朵类

4.1 创建云朵类

  1. 创建C++类(Actor),重命名 Cloud(Public)

 

  1. 创建继承自 Cloud 的蓝图(实例化),重命名 BP_Cloud,并存放在 Blueprints 文件夹下

 

  1. 添加盒体检测组件和云朵组件

 

4.2 随机云贴图生成

  1. 通过修改材质中的,随机生成云显示

 

  1. 设置云朵的显示,并添加材质数组

 

  1. 运行效果(拖放多个 BP_Cloud 到场景中运行测试):

 

  1. 调整修改云朵的大小

 

 

 

 

4.3 角色触碰云朵则触发弹跳

  1. 重写碰撞函数,检测到角色则调用弹跳函数

 

  1. 运行效果(小球触碰到云朵就起飞):

 

 

4.4 云朵随机下雨

  1. 添加云朵雨组件并绑定,随机显示云朵雨

 

  1. 设置云朵雨的位置,并添加材质

 

  1. 运行效果(随机生成云朵雨):

 

 

五. 云朵生成器

5.1 创建云朵生成器

  1. 创建C++类(Actor),重命名 SpawnCloud(Public)

 

  1. 创建继承自 SpawnCloud 的蓝图(实例化),重命名 BP_SpawnCloud,存放在 Blueprints 文件夹下

 

  1. 云朵生成器的原理:在高处设置一块云朵生成区 SpawnArea,低处设置一块碰撞检测区 TriggerArea,当角色跳跃碰撞到检测区 TriggerArea,则整体上移,并在生成区 SpawnArea 随机位置生成云朵

 

 

5.2 随机位置生成云朵

  1. 添加云朵生成区域组件和检测生成区域组件

 

  1. 随机位置生成云朵

 

 

5.3 碰撞检测与初始化上升

  1. 检测是否与角色产生碰撞

 

  1. 云朵生成器的初始化与上升

 

  1. 在 BP_SpawnCloud 中调整参数

 

 

六. 计分系统与云朵销毁

6.1 计分系统

  1. 在角色中添加分数和计分方法

 

  1. 在云朵中添加分数文本,并显示分数

 

  1. 调整分数文本的字体位置大小

 

 

6.2 TimeLine 云朵销毁

  1. 结合蓝图中 Timeline 实现云朵淡入淡出效果

 

  1. 销毁相机视角外的多余云朵

 

  1. 在场景中调整销毁碰撞检测区域的位置

 

  1. 运行效果(销毁时显示计数并变化淡出,落下区域无云朵):

 

 

七. 游戏结束与重开

7.1 游戏结束的判定

  1. 结合蓝图中 Timeline (计数 2s )判断角色是否持续下落,如果持续下落则直接降落到地面

 

  1. 游戏结束的判定

 

 

7.2 游戏重开按钮

  1. 创建 UMG 蓝图,直接设计游戏重开按钮 UI(不建议在C++里面用代码创建UI按钮,是一件非常费力不讨好的事情)

 

  1. 设置 UI 动画

 

  1. 结合蓝图,在游戏结束后显示游戏重开按钮

 

 

7.3 点击事件游戏重开

  1. 添加按钮点击事件

 

  1. 重开则重新生成云朵

 

  1. 点击重开后,移除UI,调用 C++ 中 GameReset 函数

 

 

 

 

 

八. 游戏音乐

8.1 背景音乐

  1. 将音乐文件拖入场景,设置循环播放

 

 

8.2 角色接触云朵音乐

  1. 按云朵按位置播放音乐

 

  1. 设置音乐

 

 

8.3 云朵雨音乐

  1. 添加云朵雨的声音组件,并播放

 

  1. 创建雨声音频的 Cue 文件并打开,设置雨声衰减

 

  1. 在蓝图中设置云朵雨的音效

 

 

附录:C++文件提要

 

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Cake.generated.h"

UCLASS()
class PROJECT_UPUPCAKE_API ACake : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	ACake();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	//声明一个玩家控制器
	APlayerController* PlayerController;
	
	//角色在空中的速度
	UPROPERTY(EditAnywhere,Category="Speed")
	float AirSpeed;

	//角色落地的速度
	UPROPERTY(EditAnywhere,Category="Speed")
	float GroundSpeed;
	
	//计分
	int Score;

	//判断是否死亡
	bool bDeath;

	//判断游戏是否开始
	bool bGamestart;

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

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	//声明角色跟随鼠标移动的函数
	void FollowMouseCursorMoving();
	
	//声明跳跃,游戏启动时跳跃
	void BeginJump();

	//声明跳跃,碰到云彩时跳跃
	void Jump();

	//判断角色所在的场景,设置速度
	void SetSpeed();

	//加分
	void IncreaseScore();

	//获取分数
	int GetScore() const;

	//判断角色是否持续下落
	void IsFalling();

	//刷新时间轴
	UFUNCTION(BlueprintImplementableEvent)
	void UpdateTimer();

	//重置时间轴
	UFUNCTION(BlueprintImplementableEvent)
	void ResetTimer();

	//游戏结束
	UFUNCTION(BlueprintCallable)
	void GameOver();

	//游戏重开
	UFUNCTION(BlueprintCallable)
	void GameReset();
	
	//显示重开按钮
	UFUNCTION(BlueprintImplementableEvent)
	void ResetUI();
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "Cake.h"
#include "GameFramework/CharacterMovementComponent.h" //判断角色是否运动

// Sets default values
ACake::ACake()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//速度初始化
	AirSpeed = 3500.0f;
	GroundSpeed = 300.0f;

	//分数初始化
	Score = 0;

	//判断是否死亡(初始化)
	bDeath = false;
	//判断游戏是否开始(初始化)
	bGamestart = false;
}

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

	//获取 PlayerController(因为继承自 Character,可以直接 GetController()获取)
	PlayerController = Cast<APlayerController>(GetController());

	//获取鼠标显示
	PlayerController->bShowMouseCursor = true;

	//调用游戏重开
	GameReset();
}

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

	if(!bDeath)
	{
		FollowMouseCursorMoving();  //每帧判断角色朝鼠标方向移动
	
		SetSpeed();  //每帧判断角色所在的场景,设置速度
	
		IsFalling();  //每帧判断角色是否持续下落
	}
}

// Called to bind functionality to input
void ACake::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//跳跃绑定
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACake::BeginJump);
}

void ACake::FollowMouseCursorMoving()
{
	//获取鼠标在世界坐标下的位置(方向暂用不到)
	FVector MouseCursorLocation;
	FVector MouseCursorDirection;
	PlayerController->DeprojectMousePositionToWorld(MouseCursorLocation, MouseCursorDirection);

	//角色朝鼠标方向移动
	//获取 Y方向(左右方向)的偏移:鼠标位置-小球位置,用 Clamp()函数限定范围为 -1到 1
	float YDirection = FMath::Clamp(MouseCursorLocation.Y - GetActorLocation().Y, -1.0f ,1.0f);
	FVector Direction = FVector(0,YDirection,0);  //转换为向量
	AddMovementInput(Direction);
	
}

void ACake::BeginJump()
{
	if(!bGamestart && GetActorLocation().Z != 0)
	{
		bGamestart = true;  //游戏开始
		Jump();
	}
}

void ACake::Jump()
{
	//LaunchCharacter(发射方向,false:XY方向,true:Z方向),弹跳函数只对角色有效
	//false/true:是否替换原发射速度(true一直保持 1500)
	LaunchCharacter(FVector(0,0,1500),false, true);	
}

void ACake::SetSpeed()
{
	//判断角色是否下落
	if(GetCharacterMovement()->IsFalling())
	{
		GetCharacterMovement()->MaxWalkSpeed = AirSpeed;  //在空中
	}
	else
	{
		if(bGamestart)
		{
			GameOver();
		}
		GetCharacterMovement()->MaxWalkSpeed = GroundSpeed;  //在地面
	}
}

void ACake::IncreaseScore()
{
	Score++;
}

int ACake::GetScore() const
{
	return Score;
}

void ACake::IsFalling()
{
	//如果 Z方向小于0,则表示准备开始下落了
	if(GetCharacterMovement()->Velocity.Z < 0)
	{
		//持续性下落,刷新时间轴
		UpdateTimer();
	}
	else
	{
		//短暂性下落,重置时间轴
		ResetTimer();

	}
}

void ACake::GameOver()
{
	bDeath = true;
	SetActorRotation(FRotator::ZeroRotator);  //旋转归零
	EnableInput(PlayerController);  //启用输入

	//显示重开按钮
	UE_LOG(LogTemp,Warning, TEXT("GameOver"));
	
	ResetUI();

}

void ACake::GameReset()
{
	Score = 0;
	bDeath = false;
	bGamestart = false;
}

 

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

#pragma once

#include "CoreMinimal.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Actor.h"
#include "Cake.h"  //角色
#include "Components/BoxComponent.h"  //盒体检测
#include "Camera.generated.h"

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

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

	//声明一个相机组件
	UPROPERTY(VisibleAnywhere)
	UCameraComponent* CameraComponent;

	//声明一个玩家控制器
	APlayerController* PlayerController;

	//声明角色
	ACake* Cake;

	//销毁相机视角外的多余云朵
	UPROPERTY(VisibleAnywhere, Category = "Component")
	UBoxComponent* DestroyArea;

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

	//全局相机垂直跟随
	void CameraMoving();

	//检测销毁区是否与多余云朵产生碰撞
	virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;  //重写碰撞函数
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "Camera.h"
#include "Camera/CameraComponent.h"  //相机组件
#include "Kismet/GameplayStatics.h"  //玩家控制器
#include "Cloud.h"  //销毁相机视角外的多余云朵

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

	//定义一个相机组件
	CameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
	//将相机组件设置为默认根组件
	CameraComponent->SetupAttachment(RootComponent);

	//销毁相机视角外的多余云朵
	DestroyArea = CreateDefaultSubobject<UBoxComponent>(TEXT("DestroyArea"));
	//绑定到根组件
	DestroyArea->SetupAttachment(RootComponent);

}

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

	//获取玩家控制器
	PlayerController = UGameplayStatics::GetPlayerController(this, 0);
	//切换视角(立刻对齐)
	PlayerController->SetViewTargetWithBlend(this, 0);

	//获取角色
	Cake = Cast<ACake>(UGameplayStatics::GetPlayerPawn(this,0));
	
}

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

	CameraMoving();  //每帧调用全局相机垂直跟随
}

void ACamera::CameraMoving()
{
	//(相机的 X,相机的 Y,角色的 Z)组成跟随坐标
	FVector TargetLoaction = FVector(GetActorLocation().X, GetActorLocation().Y, Cake->GetActorLocation().Z);
	//设置相机当前位置
	SetActorLocation(TargetLoaction);
}

void ACamera::NotifyActorBeginOverlap(AActor* OtherActor)
{
	Super::NotifyActorBeginOverlap(OtherActor);

	//类型转换
	ACloud* Cloud = Cast<ACloud>(OtherActor);
	if(Cloud)
	{
		//检测到多余云朵则调用销毁
		Cloud->Destroy();
	}
}

 

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

#pragma once

#include "CoreMinimal.h"
#include "Components/BoxComponent.h"
#include "GameFramework/Actor.h"
#include "Cake.h"  //角色
#include "Components/TextRenderComponent.h"  //文本
#include "Sound/SoundWave.h"  //播放音乐
#include "Cloud.generated.h"

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

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

	//声明盒体检测组件
	UPROPERTY(VisibleAnywhere, Category = "Collision")
	UBoxComponent* BoxComponent;

	//声明云朵组件
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Show")
	UStaticMeshComponent* CloudPlane;

	//声明云朵雨组件
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Show")
	UStaticMeshComponent* RainPlane;

	//云朵库
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Show")
	TArray<UTexture*> CloudTextures;

	//动态材质实例
	UPROPERTY(BlueprintReadOnly, Category = "Show")
	UMaterialInstanceDynamic* MaterialInstanceDynamic;
	
	UMaterialInterface* MaterialInterface;

	//分数文本
	UPROPERTY(EditAnywhere, Category = "Show")
	UTextRenderComponent* ScoreText;

	//检测是否与角色产生碰撞
	ACake* Cake;

	//角色接触云朵音乐
	UPROPERTY(EditAnywhere, Category = "Sound")
	USoundWave* CloudSound;

	//云朵雨音乐
	UPROPERTY(VisibleAnywhere, Category = "Sound")
	UAudioComponent* RainSound;
	
public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	
	//随机云朵生成
	void SetRandomCloudTextures();

	//显示分数
	void DisplayScore();

	//检测是否与角色产生碰撞
	virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;  //重写碰撞函数
	
	//云朵淡出(蓝图实现)
	UFUNCTION(BlueprintImplementableEvent)
	void FadeOut();
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "Cloud.h"
#include "Kismet/GameplayStatics.h"  //播放音乐
#include "Components/AudioComponent.h"  //云朵雨音乐组件

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

	//定义盒体检测
	BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollision"));
	//设置为默认根组件
	RootComponent = BoxComponent;

	//定义云朵
	CloudPlane = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("CloudMesh"));
	//绑定到根组件(盒体检测)
	CloudPlane->SetupAttachment(RootComponent);

	//定义云朵雨
	RainPlane = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("RainMesh"));
	//绑定到云朵组件
	RainPlane->SetupAttachment(CloudPlane);

	//分数文本
	ScoreText = CreateDefaultSubobject<UTextRenderComponent>(TEXT("ScoreText"));
	//绑定到根组件
	ScoreText->SetupAttachment(RootComponent);

	//云朵雨音乐
	RainSound = CreateDefaultSubobject<UAudioComponent>(TEXT("RainSound"));
	//绑定到云朵组件
	RainSound->SetupAttachment(CloudPlane);
}

// Called when the game starts or when spawned
void ACloud::BeginPlay()
{
	Super::BeginPlay();
	
	SetRandomCloudTextures();  //调用随机云朵生成

	//随机显示云朵雨
	if(FMath::RandRange(0,2))
	{
		RainPlane->SetVisibility(true);

		//播放云朵雨音乐
		RainSound->Activate(true);
	}
}

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

}

void ACloud::SetRandomCloudTextures()
{
	//获取 CloudPlane上的材质
	MaterialInterface = CloudPlane->GetMaterial(0);
	
	//通过创建动态材质实例来获取指定材质参数的随机材质
	MaterialInstanceDynamic = CloudPlane->CreateDynamicMaterialInstance(0,MaterialInterface);
	
	//随机数
	int index = FMath::RandRange(0,2);
	
	if(CloudTextures[index])
	{
		//设置材质参数值(参数节点名Texture,随机云彩库)
		MaterialInstanceDynamic->SetTextureParameterValue(FName(TEXT("Texture")), CloudTextures[index]);
		//将材质实例赋给模型
		CloudPlane->SetMaterial(0, MaterialInstanceDynamic);
	}
}

void ACloud::NotifyActorBeginOverlap(AActor* OtherActor)
{
	Super::NotifyActorBeginOverlap(OtherActor);

	//类型转换
	Cake = Cast<ACake>(OtherActor);
	if(Cake)
	{
		//按位置播放音乐
		UGameplayStatics::PlaySoundAtLocation(this, CloudSound, GetActorLocation());
		//检测到角色则调用弹跳
		Cake->Jump();
		//显示分数
		DisplayScore();

		//云朵淡出(蓝图实现)
		FadeOut();
	}
}

void ACloud::DisplayScore()
{
	Cake->IncreaseScore();
	
	//设置文本(类型转换 int - FString - FText)
	ScoreText->SetText(FText::FromString(FString::FromInt(Cake->GetScore())));
}

 

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

#pragma once

#include "CoreMinimal.h"
#include "Cloud.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"  //盒体检测
#include "SpawnCloud.generated.h"

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

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

	//云朵生成区域
	UPROPERTY(VisibleAnywhere, Category = "Component")
	UBoxComponent* SpawnArea;

	//根据角色位置,检测云朵是否可以继续生成
	UPROPERTY(VisibleAnywhere, Category = "Component")
	UBoxComponent* TriggerArea;

	//场景根组件
	USceneComponent* DefaultRootComponent;

	//TSubclassOf:限定为 ACloud或其子类
	UPROPERTY(EditAnywhere, Category = "Cloud")
	TSubclassOf<ACloud> Cloud;

	//检测是否与角色产生碰撞
	ACake* Cake;
	
	//云朵初始化
	UPROPERTY(EditAnywhere, Category = "Cloud")
	int32 InitialCloud;

	//云朵生成器每次上升距离
	UPROPERTY(EditAnywhere, Category = "Cloud")
	float SpawnSpacing;

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

	//随机生成云朵
	void SpawnCloud();

	//检测是否与角色产生碰撞
	virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;  //重写碰撞函数
	
	//重开则重新生成云朵
	UFUNCTION(BlueprintCallable)
	void ResetCloud();

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

#include "SpawnCloud.h"
#include "Kismet/KismetMathLibrary.h"  //随机生成点
#include "Cloud.h"  //生成云朵
#include "Kismet/GameplayStatics.h"  //重开销毁之前的所有云朵

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

	//场景根组件
	DefaultRootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("DefaultRootComponent"));
	RootComponent = DefaultRootComponent;
	
	//云朵生成区域
	SpawnArea = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnArea"));
	//绑定到场景根组件
	SpawnArea->SetupAttachment(RootComponent);

	//根据角色位置,检测云朵是否可以继续生成
	TriggerArea = CreateDefaultSubobject<UBoxComponent>(TEXT("TriggerArea"));
	//绑定到场景根组件
	TriggerArea->SetupAttachment(RootComponent);

	//云朵初始化
	InitialCloud = 6;
	//云朵生成器每次上升距离
	SpawnSpacing = 300.0f;
}

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

	ResetCloud();
}

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

}

void ASpawnCloud::SpawnCloud()
{
	FVector SpawnOrigin = SpawnArea->Bounds.Origin;  //生成中心点
	FVector SpawnExtent = SpawnArea->Bounds.BoxExtent;  //生成范围

	//生成的位置
	//提供圆心和范围,Y轴随机生成点
	float YLocation = UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent).Y;
	FVector SpawnLocation = FVector(SpawnArea->GetComponentLocation().X, YLocation, SpawnArea->GetComponentLocation().Z);

	//生成(生成云朵,生成位置,生成旋转)
	GetWorld()->SpawnActor<ACloud>(Cloud, SpawnLocation, FRotator::ZeroRotator);

	//云朵生成器上升(0,0,300)
	AddActorWorldOffset(FVector(0,0,SpawnSpacing));
}

void ASpawnCloud::NotifyActorBeginOverlap(AActor* OtherActor)
{
	Super::NotifyActorBeginOverlap(OtherActor);

	//类型转换
	Cake = Cast<ACake>(OtherActor);
	if(Cake)
	{
		//检测到角色则调用随机生成云朵
		SpawnCloud();
	}
}

void ASpawnCloud::ResetCloud()
{
	//云朵初始化
	InitialCloud = 6;

	//刷新位置为(0,0,0)
	SetActorLocation(FVector::ZeroVector);

	//重开销毁之前的所有云朵
	TArray<AActor*>ResetClouds;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), ACloud::StaticClass(), ResetClouds);
	for(AActor* TActor : ResetClouds)
	{
		ACloud* FoundClouds = Cast<ACloud>(TActor);  //类型转换
		if(FoundClouds!= nullptr)
		{
			FoundClouds->Destroy();
		}
	}
	
	//云朵初始化随机生成
	while(InitialCloud > 0)
	{
		SpawnCloud();
		InitialCloud--;
	}
}

 


参考资料:

【SiKi学院Unreal视频教程】UE4/5 虚幻4/5初级课程-向上的小松饼_哔哩哔哩_bilibili