【UE开发】2D吃豆人Demo

1.Demo介绍

        1.游戏:玩家以第三人称俯视角控制吃豆人收集地图中所有的道具豆,同时躲避随机巡逻的敌人。

        2.附件:

                1.工程源码:GitHub - CNEarly/PacmanDemo: 使用虚幻引擎C++开发2D吃豆人Demo

                2.演示视频:PacmanDemo演示_演示

        3.内容:

                1.移动:使用WSAD键控制吃豆人上下左右移动

                2.巡逻:敌人随机巡逻

                3.道具:触碰普通道具豆玩家获得分数,触碰超级道具豆玩家获得技能

                4.阵亡:玩家普通状态触碰敌人损失生命,生命消耗完毕游戏结束

                5.获胜:玩家触碰获取所有道具豆游戏获胜

2.地图实现

        1.材料:①敌人BP②几何体③豆BP④音效⑤摄像机类⑥导航网格体边界体积⑦玩家出生地⑧导航调整

        2.搭建:

3.吃豆人实现

        1.C++头文件:

//预处理
#pragma once//编译一次
#include "CoreMinimal.h"//核心编程环境
#include "GameFramework/Character.h"//Character源代码
#include "PacmanPlayer.generated.h"//反射机制
//吃豆类
UCLASS()
class PACMAN_API APacmanPlayer : public ACharacter
{
	GENERATED_BODY()
public:
	//构造函数声明
	APacmanPlayer();
	//网格体组件声明
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* PacmanDisplayMesh;
	//生命数变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int Lifes;
	//无敌时间变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float InvincibleTime;
	//正常速度变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float NormalSpeed;
protected:
	//游戏开始函数声明
	virtual void BeginPlay() override;
public:	
	//游戏每帧函数声明
	virtual void Tick(float DeltaTime) override;
	//玩家输入函数声明
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	//受伤函数声明
	void Injured();
	//复位函数声明
	void Resetting();
	//设置移动函数声明
	void SetMovement(bool bCanMove);
	//开始游戏函数声明
	void StartGame();
	//暂停游戏函数声明
	void PauseGame();
	//重启游戏函数声明
	void RestartGame();
	//左右移动函数声明
	void MoveX(float Value);
	//上下移动函数声明
	void MoveY(float Value);
	//碰撞函数声明
	UFUNCTION()
	void OnPacmanBeginOverlay(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
private:
	//游戏开始位置变量
	FVector StartLocation;
	//移动向量变量
	FVector Velocity;
	//游戏模式类
	class APacmanGameModeBase* GameModeRef;
	//复位定时器
	FTimerHandle ResettingTimerHandle;
};

        2.C++源文件:

//预处理
#include "PacmanPlayer.h"//吃豆人类
#include "Components/StaticMeshComponent.h"//网格体
#include "Components/CapsuleComponent.h"//碰撞
#include "Components/InputComponent.h"//输入
#include "Pacdot.h"//豆类
#include "Kismet/GameplayStatics.h"//游戏状态
#include "PacmanGameModeBase.h"//游戏模式类
#include "Engine/Public/TimerManager.h"//定时器
#include "GameFramework/CharacterMovementComponent.h"//移动
//构造函数实现
APacmanPlayer::APacmanPlayer()
{
	PrimaryActorTick.bCanEverTick = true;//开启tick函数

	Lifes = 3;//生命数为3
	InvincibleTime = 3.0f;//无敌时间为3s
	NormalSpeed = 600.0f;//正常速度为600
	Velocity = FVector(0, 0, 0);//移动向量为(0,0,0)

	PacmanDisplayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PacmanDisplayMesh"));//实例化网格体组件
	PacmanDisplayMesh->SetupAttachment(GetCapsuleComponent());//网格体组件绑定胶囊体组件
}
//游戏开始函数实现
void APacmanPlayer::BeginPlay()
{
	Super::BeginPlay();
	
	StartLocation = GetActorLocation();//设置当前位置为起始位置
	GameModeRef = Cast<APacmanGameModeBase>(UGameplayStatics::GetGameMode(this));//类型检测获取当前游戏模式

	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &APacmanPlayer::OnPacmanBeginOverlay);//胶囊体重叠事件绑定碰撞函数
	SetMovement(true);//开启移动
}
//游戏每帧函数实现
void APacmanPlayer::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}
//玩家输入函数实现
void APacmanPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAction("StartGame", IE_Pressed, this, &APacmanPlayer::StartGame);//开始游戏操作映射
	PlayerInputComponent->BindAction("PauseGame", IE_Pressed, this, &APacmanPlayer::PauseGame);//暂停游戏操作映射
	PlayerInputComponent->BindAction("RestartGame", IE_Pressed, this, &APacmanPlayer::RestartGame);//重启游戏操作映射

	PlayerInputComponent->BindAxis("MoveX", this, &APacmanPlayer::MoveX);//左右移动轴映射
	PlayerInputComponent->BindAxis("MoveY", this, &APacmanPlayer::MoveY);//上下移动轴映射
}
//受伤函数实现
void APacmanPlayer::Injured()
{
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Invincible"));

	Lifes--;//生命数减一
	if (Lifes == 0)//生命数为0时
	{
		GameModeRef->SetCurrentState(EGameState::EGameOver);//游戏模式设置为游戏结束
	}
	else//生命数不为0时
	{
		SetActorLocation(StartLocation);//吃豆人位置设置为初始位置
		GetWorldTimerManager().SetTimer(ResettingTimerHandle, this, &APacmanPlayer::Resetting, InvincibleTime, false);//启动复位定时器
	}
}
//复位函数实现
void APacmanPlayer::Resetting()
{
	GetWorldTimerManager().ClearTimer(ResettingTimerHandle);//清除复位定时器
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Pawn"));//设置碰撞预设为pawn
}
//设置移动函数实现
void APacmanPlayer::SetMovement(bool bCanMove)
{
	if (bCanMove)//玩家可以移动时最大速度为正常速度
	{
		GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;
	}
	else//玩家不可以移动时最大速度为0
	{
		GetCharacterMovement()->MaxWalkSpeed = 0;
	}
}
//开始游戏函数实现
void APacmanPlayer::StartGame()
{
	GameModeRef->StartGame();//调用游戏模式类开始游戏函数
}
//暂停游戏函数实现
void APacmanPlayer::PauseGame()
{
	GameModeRef->PauseGame();//调用游戏模式类暂停游戏函数
}
//重启游戏函数实现
void APacmanPlayer::RestartGame()
{
	GameModeRef->RestartGame();//调用游戏模式类重启游戏函数
}
//左右移动函数实现
void APacmanPlayer::MoveX(float Value)
{
	if (GameModeRef->GetCurrentState() == EGameState::EPlay)//游戏模式为游戏游玩时
	{
		Velocity.X = Value;//移动向量X轴变量为传参
		Velocity.Y = 0;//移动向量Y轴变量为0

		if (Value > 0)//X轴变量大于0
		{
			SetActorRotation(FRotator(0, 0, 0));//设置旋转为0度朝向
		}
		else if (Value < 0)//X轴变量小于0
		{
			SetActorRotation(FRotator(0, -180, 0)); //设置旋转为-180度朝向
		}

		AddMovementInput(Velocity);//将移动向量传入移动输入函数
	}
}
//上下移动函数实现
void APacmanPlayer::MoveY(float Value)
{
	if (GameModeRef->GetCurrentState() == EGameState::EPlay)//游戏模式为游戏游玩时
	{
		Velocity.X = 0;//移动向量X轴变量为0
		Velocity.Y = Value;//移动向量Y轴变量为传参

		if (Value > 0)//Y轴变量大于0
		{
			SetActorRotation(FRotator(0, 90, 0));//设置旋转为90度朝向
		}
		else if (Value < 0)//Y轴变量小于0
		{
			SetActorRotation(FRotator(0, -90, 0));//设置旋转为-90度朝向
		}

		AddMovementInput(Velocity);//将移动向量传入移动输入函数
	}
}
//碰撞函数实现
void APacmanPlayer::OnPacmanBeginOverlay(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (GameModeRef->GetCurrentState() == EGameState::EPlay)//游戏模式为游戏游玩时
	{
		if (OtherActor->IsA(APacdot::StaticClass()))//当前碰撞到的是豆
		{
			APacdot* CurrentPacdot = Cast<APacdot>(OtherActor);//保存当前碰撞的豆
			if (CurrentPacdot->bIsSuperPacdot)//碰撞超级豆
			{
				GameModeRef->SetEnemiesVulnerable();//游戏模式调用敌人设置虚弱状态函数
			}
			OtherActor->Destroy();//销毁碰撞的豆
			GameModeRef->SetPacdotNum(GameModeRef->GetPacdotNum() - 1);//游戏模式记录豆数-1
		}
	}
}

        3.虚幻蓝图类:①创建蓝图类继承吃豆人C++类

                                 ②蓝图类组件添加素材

        4.类设置:①Pawn细节关闭使用控制器旋转Yaw       

        5.总结:吃豆人类作为玩家控制的对象需要根据玩家的输入来触发对应的函数,当吃豆人在游戏中触发不同事件时会调用不同的函数

4.豆实现

        1.C++头文件:

//预处理
#pragma once//编译一次
#include "CoreMinimal.h"//核心编程环境
#include "GameFramework/Actor.h"//Actor源代码
#include "Pacdot.generated.h"//反射机制
//豆类
UCLASS(Blueprintable)
class PACMAN_API APacdot : public AActor
{
	GENERATED_BODY()
public:	
	//构造函数声明
	APacdot();
	//球体碰撞组件变量
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class USphereComponent* PacdotCollision;
	//网格体组件变量
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* PacdotDisplayMesh;
	//超级豆属性变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool bIsSuperPacdot = false;
protected:
	//游戏开始函数声明
	virtual void BeginPlay() override;
public:	
	//游戏每帧函数声明
	virtual void Tick(float DeltaTime) override;
};

        2.C++源文件:

//预处理
#include "Pacdot.h"//豆类
#include "Components/StaticMeshComponent.h"//网格体
#include "Components/SphereComponent.h"//碰撞
//构造函数实现
APacdot::APacdot()
{
	PrimaryActorTick.bCanEverTick = false;//开启tick函数

	PacdotCollision = CreateDefaultSubobject<USphereComponent>(TEXT("PacdotCollision"));//实例化球体碰撞
	PacdotDisplayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PacdotDisplayMesh"));//实例化网格体

	PacdotDisplayMesh->SetupAttachment(PacdotCollision);//网格体绑定球体碰撞
}
//游戏开始函数实现
void APacdot::BeginPlay()
{
	Super::BeginPlay();
	
}
//游戏每帧函数实现
void APacdot::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

        3.虚幻蓝图类:①创建蓝图类继承豆C++类

                                 ②蓝图类组件添加素材

        4.总结:作为场景中的道具当被玩家触碰到时会触发销毁,吃豆人会根据豆的不同来触发不同的状态和函数

5.敌人实现

        1.C++头文件:

//预处理
#pragma once//编译一次
#include "CoreMinimal.h"//核心编程环境
#include "GameFramework/Character.h"//Character源代码
#include "PacmanEnemy.generated.h"//反射机制
//敌人类
UCLASS()
class PACMAN_API APacmanEnemy : public ACharacter
{
	GENERATED_BODY()
public:
	//构造函数声明
	APacmanEnemy();
	//网格体组件声明
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	class UStaticMeshComponent* EnemyDisplayMesh;
	//阵亡变量
	bool bIsDead;
	//虚弱变量
	bool bIsVulnerable;
	//阵亡定时器变量
	FTimerHandle DeadTimerHandle;
	//虚弱定时器变量
	FTimerHandle VulnerableTimerHandle;
	//阵亡时间变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float DeadTime;
	//虚弱时间变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float VulnerableTime;
	//正常速度变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float NormalSpeed;
	//虚弱速度变量
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float VulnerableSpeed;
protected:
	//游戏开始函数声明
	virtual void BeginPlay() override;
public:	
	//游戏每帧函数声明
	virtual void Tick(float DeltaTime) override;
	//玩家输入函数声明
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	//设置阵亡函数声明
	void SetDead(bool bRestoreTimer = false, float Time = 0.0f);
	//设置虚弱函数声明
	void SetVulnerable(bool bRestoreTimer = false, float Time = 0.0f);
	//设置正常函数声明
	void SetNormal();
	//设置移动函数声明
	void SetMovement(bool bCanMove);
	//碰撞函数声明
	UFUNCTION()
	void OnEnemyBeginOverlay(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
private:
	//起始位置变量
	FVector StartLocation;
};

        2.C++源文件:

//预处理
#include "PacmanEnemy.h"//敌人类
#include "Components/StaticMeshComponent.h"//网格体
#include "Components/CapsuleComponent.h"//碰撞
#include "PacmanPlayer.h"//吃豆人类
#include "GameFramework/CharacterMovementComponent.h"//移动
#include "Engine/Public/TimerManager.h"//定时器
//构造函数实现
APacmanEnemy::APacmanEnemy()
{
	PrimaryActorTick.bCanEverTick = true;//开启tick函数

	bIsDead = false;//阵亡为假
	bIsVulnerable = false;//虚弱为假

	DeadTime = 5.0f;//阵亡时间为5s
	VulnerableTime = 5.0f;//虚弱时间为5s

	NormalSpeed = 300.0f;//正常移动速度为300
	VulnerableSpeed = 100.0f;//虚弱移动速度为100

	EnemyDisplayMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("EnemyDisplayMesh"));//实例化网格体组件
	EnemyDisplayMesh->SetupAttachment(GetCapsuleComponent());//网格体组件绑定碰撞组件
}
//游戏开始函数实现
void APacmanEnemy::BeginPlay()
{
	Super::BeginPlay();
	
	StartLocation = GetActorLocation();//设置初始位置为当前位置

	GetCapsuleComponent()->OnComponentBeginOverlap.AddDynamic(this, &APacmanEnemy::OnEnemyBeginOverlay);//胶囊体重叠事件绑定碰撞函数
	SetMovement(true);//开启移动
}
//游戏每帧函数实现
void APacmanEnemy::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}
//玩家输入函数实现
void APacmanEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
}
//设置阵亡函数实现
void APacmanEnemy::SetDead(bool bRestoreTimer, float Time)
{
	if (bRestoreTimer && bIsDead)//恢复定时器为真且阵亡为真
	{
		GetWorldTimerManager().SetTimer(DeadTimerHandle, this, &APacmanEnemy::SetNormal, Time, false);//阵亡定时器继续
		return;//返回
	}

	if (bIsDead)//阵亡为真
	{
		return;//返回
	}

	bIsDead = true;//阵亡设置为真
	SetMovement(true);//移动设置为真
	SetActorLocation(StartLocation);//位置设置为初始位置

	GetWorldTimerManager().ClearTimer(VulnerableTimerHandle);//清除虚弱定时器
	GetWorldTimerManager().SetTimer(DeadTimerHandle, this, &APacmanEnemy::SetNormal, DeadTime, false);//阵亡定时器开启
}
//设置虚弱函数实现
void APacmanEnemy::SetVulnerable(bool bRestoreTimer, float Time)
{
	if (bRestoreTimer)//恢复定时器为真
	{
		GetWorldTimerManager().SetTimer(VulnerableTimerHandle, this, &APacmanEnemy::SetNormal, Time, false);//虚弱定时器继续
	}
	else//恢复定时器未开启
	{
		GetWorldTimerManager().SetTimer(VulnerableTimerHandle, this, &APacmanEnemy::SetNormal, VulnerableTime, false);//虚弱定时器开启
	}

	if (bIsVulnerable)//虚弱为真
	{
		return;//返回
	}

	bIsVulnerable = true;//虚弱设置为真
	SetMovement(true);//移动设置为真
}
//设置正常函数实现
void APacmanEnemy::SetNormal()
{
	GetWorldTimerManager().ClearTimer(DeadTimerHandle);//清除阵亡定时器
	GetWorldTimerManager().ClearTimer(VulnerableTimerHandle);//清除虚弱定时器

	bIsDead = false;//阵亡设置为假
	bIsVulnerable = false;//虚弱设置为假

	SetMovement(true);//设置移动为真
}
//设置移动函数实现
void APacmanEnemy::SetMovement(bool bCanMove)
{
	if (bCanMove)//可移动变量为真
	{
		if (bIsVulnerable == false && bIsDead == false)//虚弱为假且阵亡为假
		{
			GetCharacterMovement()->MaxWalkSpeed = NormalSpeed;//最大速度设置为正常速度
		}
		if (bIsVulnerable == true && bIsDead == false)//虚弱为真且阵亡为假
		{
			GetCharacterMovement()->MaxWalkSpeed = VulnerableSpeed;//最大速度设置为虚弱状态
		}
		if (bIsDead == true)//阵亡为真
		{
			GetCharacterMovement()->MaxWalkSpeed = 0;//最大速度设置为0
		}
	}
	else//可移动变量为假
	{
		GetCharacterMovement()->MaxWalkSpeed = 0;//最大速度设置为0
	}
}
//碰撞函数实现
void APacmanEnemy::OnEnemyBeginOverlay(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor->IsA(APacmanPlayer::StaticClass()))//当前碰撞到的是吃豆人
	{
		if (bIsDead)//当敌人是阵亡状态
		{
			return;//忽略
		}

		if (bIsVulnerable)//当敌人是虚弱状态
		{
			SetDead();//阵亡
		}
		else//当敌人是正常状态
		{
			Cast<APacmanPlayer>(OtherActor)->Injured();//吃豆人阵亡
		}
	}
}

        3.虚幻蓝图类:①创建蓝图类继承敌人C++类

                                 ②蓝图类组件添加素材

        4.类设置:①Pawn细节使用敌人控制器        

        5.总结:与战机类的基本设置相同,只是作为npc需要使用控制器来控制敌人的行为,敌人也会通过事件的触发来调用不同的函数

6.敌人控制器实现

        1.C++头文件:

//预处理
#pragma once//编译一次
#include "CoreMinimal.h"//核心编程环境
#include "AIController.h"//AIController源代码
#include "EnemyController.generated.h"//反射机制
//敌人控制器类
UCLASS()
class PACMAN_API AEnemyController : public AAIController
{
	GENERATED_BODY()
public:
	//占有函数声明
	void OnPossess(class APawn* PawnToPossess) override;
	//移动完成函数声明
	virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result) override;
	//移动至新目的地函数声明
	void GoToNewDestination();
private:
	//控制敌人变量
	class APacmanEnemy* ControlledEnemy;
};

        2.C++源文件:

//预处理
#include "EnemyController.h"//敌人控制器类
#include "PacmanEnemy.h"//敌人类
#include "NavigationSystem.h"//导航系统
//占有函数实现
void AEnemyController::OnPossess(APawn* PawnToPossess)
{
	Super::OnPossess(PawnToPossess);

	ControlledEnemy = Cast<APacmanEnemy>(PawnToPossess);//控制器占有敌人类
}
//移动完成函数实现
void AEnemyController::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
	Super::OnMoveCompleted(RequestID, Result);

	GoToNewDestination();//调用移动至新目的地函数
}
//移动至新目的地函数实现
void AEnemyController::GoToNewDestination()
{
	UNavigationSystemV1* NavMesh = UNavigationSystemV1::GetCurrent(this);//获取场景中导航网格的指针
	if (NavMesh)//指针为真
	{
		const float SearchRadius = 10000.0f;//设置巡逻半径
		FNavLocation RandomLocation;//随机位置变量
		const bool bNewDestinationFound = NavMesh->GetRandomReachablePointInRadius(ControlledEnemy->GetActorLocation(), SearchRadius, RandomLocation);//在可到达半径范围内得到一个随机点
		if (bNewDestinationFound)//随机点为真
		{
			MoveToLocation(RandomLocation.Location, 50.0f);//移动至新目的地
		}
	}
}

        3.总结:负责获取需要控制的敌人以及控制敌人进行移动到达新的位置

7.模式实现

        1.C++头文件:

//预处理
#pragma once//编译一次
#include "CoreMinimal.h"//核心编程环境
#include "GameFramework/GameModeBase.h"//GameModeBase源代码
#include "PacmanGameModeBase.generated.h"//反射机制
//游戏状态变量
UENUM(BlueprintType)
enum class EGameState : uint8
{
	EMenu,//菜单状态
	EPlay,//游玩状态
	EPause,//暂停状态
	EWin,//胜利状态
	EGameOver//战败状态
};
//游戏模式类
UCLASS()
class PACMAN_API APacmanGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
public:
	//构造函数声明
	APacmanGameModeBase();
protected:
	//游戏开始函数声明
	virtual void BeginPlay() override;
public:
	//开始游戏函数声明
	void StartGame();
	//暂停游戏函数声明
	void PauseGame();
	//重启游戏函数声明
	void RestartGame();
	//设置敌人虚弱函数声明
	void SetEnemiesVulnerable();
	//记录敌人定时器函数声明
	void RecordEnemiesTimer();
	//恢复敌人定时器函数声明
	void RestoreEnemiesTimer();
	//获取豆数量函数声明
	int GetPacdotNum() const;
	//设置豆数量函数声明
	void SetPacdotNum(int Value);
	//豆数量变量
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	int PacdotNum;
	//设置当前游戏状态函数声明
	EGameState GetCurrentState() const;
	void SetCurrentState(EGameState Value);
	//游戏状态变量
	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	EGameState CurrentState;
private:
	//敌人数组
	TArray<class APacmanEnemy*> Enemies;
	//定时器数组
	TArray<float> EnemiesTimerRemaining;
};
//获取豆数量函数实现
FORCEINLINE int APacmanGameModeBase::GetPacdotNum() const
{
	return PacdotNum;//返回豆数量
}
//获取当前游戏状态函数实现
FORCEINLINE EGameState APacmanGameModeBase::GetCurrentState() const
{
	return CurrentState;//返回当前游戏状态
}

        2.C++源文件:

//预处理
#include "PacmanGameModeBase.h"//游戏模式类
#include "Pacdot.h"//豆类
#include "PacmanPlayer.h"//吃豆人类
#include "PacmanEnemy.h"//敌人类
#include "EnemyController.h"//敌人控制器类
#include "Engine/Public/EngineUtils.h"//虚幻工具
//构造函数实现
APacmanGameModeBase::APacmanGameModeBase()
{
	CurrentState = EGameState::EMenu;//设置菜单状态为当前游戏状态
}
//游戏开始函数实现
void APacmanGameModeBase::BeginPlay()
{
	Super::BeginPlay();

	for (TActorIterator<APacdot> PacdotItr(GetWorld()); PacdotItr; ++PacdotItr)//遍历所有豆子
	{
		PacdotNum++;//记录豆数
	}

	for (TActorIterator<APacmanEnemy> EnemyItr(GetWorld()); EnemyItr; ++EnemyItr)//遍历所有敌人
	{
		Enemies.Add(Cast<APacmanEnemy>(*EnemyItr));//存储敌人
	}
}
//开始游戏函数实现
void APacmanGameModeBase::StartGame()
{
	if (CurrentState == EGameState::EMenu)//当前游戏状态为菜单
	{
		SetCurrentState(EGameState::EPlay);//设置当前游戏状态为游玩
		for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人控制器
		{
			Cast<AEnemyController>((*Iter)->GetController())->GoToNewDestination();//敌人进行巡逻
		}
	}
}
//暂停游戏函数实现
void APacmanGameModeBase::PauseGame()
{
	if (GetCurrentState() == EGameState::EPlay)//当前游戏状态为游玩
	{
		RecordEnemiesTimer();//调用记录敌人定时器函数
		SetCurrentState(EGameState::EPause);//设置当前游戏状态为暂停
	}
	else if (GetCurrentState() == EGameState::EPause)//当前游戏状态为暂停
	{
		RestoreEnemiesTimer();//调用恢复敌人定时器函数
		SetCurrentState(EGameState::EPlay);//设置当前游戏状态为游玩
	}
}
//重启游戏函数实现
void APacmanGameModeBase::RestartGame()
{
	GetWorld()->GetFirstPlayerController()->ConsoleCommand(TEXT("RestartLevel"));//获取玩家控制器重启关卡
}
//设置敌人虚弱函数实现
void APacmanGameModeBase::SetEnemiesVulnerable()
{
	for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人控制器
	{
		(*Iter)->SetVulnerable();//敌人设置为虚弱
	}
}
//记录敌人定时器函数实现
void APacmanGameModeBase::RecordEnemiesTimer()
{
	EnemiesTimerRemaining.Empty();//剩余定时器数组清空

	for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历敌人
	{
		if ((*Iter)->bIsVulnerable == false && (*Iter)->bIsDead == false)//敌人未虚弱且未阵亡
		{
			EnemiesTimerRemaining.Add(-1);
		}
		if ((*Iter)->bIsVulnerable == true && (*Iter)->bIsDead == false)//敌人虚弱且未阵亡
		{
			float VulnerableTimerRemaining = GetWorldTimerManager().GetTimerRemaining((*Iter)->VulnerableTimerHandle);//定义一个新虚弱定时器记录原虚弱定时器
			EnemiesTimerRemaining.Add(VulnerableTimerRemaining);//定时器数组添加新虚弱定时器
		}
		if ((*Iter)->bIsDead == true)//敌人阵亡
		{
			float DeadTimerRemaining = GetWorldTimerManager().GetTimerRemaining((*Iter)->DeadTimerHandle);//定义一个新阵亡定时器记录原阵亡定时器
			EnemiesTimerRemaining.Add(DeadTimerRemaining);//定时器数组添加新阵亡定时器
		}
	}
}
//恢复敌人定时器函数实现
void APacmanGameModeBase::RestoreEnemiesTimer()
{
	for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人
	{
		if ((*Iter)->bIsVulnerable == true && (*Iter)->bIsDead == false)//敌人虚弱且未阵亡
		{
			(*Iter)->SetVulnerable(true, EnemiesTimerRemaining[Iter.GetIndex()]);//设置敌人虚弱为真且恢复定时器
		}

		if ((*Iter)->bIsDead == true)//敌人阵亡
		{
			(*Iter)->SetDead(true, EnemiesTimerRemaining[Iter.GetIndex()]);//设置敌人阵亡为真且恢复定时器
		}
	}
}
//设置豆数量函数实现
void APacmanGameModeBase::SetPacdotNum(int Value)
{
	PacdotNum = Value;//设置豆数

	if (PacdotNum == 0)//当豆数为0
	{
		SetCurrentState(EGameState::EWin);//设置当前状态为获胜状态
	}
}
//设置当前游戏状态函数实现
void APacmanGameModeBase::SetCurrentState(EGameState Value)
{
	CurrentState = Value;//设置游戏状态

	switch (CurrentState)//获取到的游戏状态为真
	{
		case EGameState::EPlay://游戏状态为游玩
			for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人
			{
				(*Iter)->SetMovement(true);//设置敌人移动
			}
			Cast<APacmanPlayer>(GetWorld()->GetFirstPlayerController()->GetPawn())->SetMovement(true);//设置玩家移动
			break;
		case EGameState::EWin://游戏状态为胜利
			for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人
			{
				(*Iter)->Destroy();//敌人销毁
			}
			break;
		case EGameState::EPause://游戏状态为暂停
		case EGameState::EGameOver://游戏状态为战败
			for (auto Iter(Enemies.CreateIterator()); Iter; ++Iter)//遍历所有敌人
			{
				(*Iter)->SetMovement(false);//设置敌人不移动
				GetWorldTimerManager().ClearAllTimersForObject(*Iter);//清除敌人所有定时器
			}
			Cast<APacmanPlayer>(GetWorld()->GetFirstPlayerController()->GetPawn())->SetMovement(false);//设置玩家移动
			break;
		default:
			break;
	}
}

        3.虚幻蓝图类:①创建蓝图类继承游戏模式C++类

                                 ②蓝图类事件图表调用界面UI到窗口

        4.类设置:①类细节默认Pawn类设置为吃豆人类       

        5.总结:负责设置当前的游戏状态,以及管理游戏中豆的数量和敌人的定时器

8.编辑器实现

        1.设置:①将游戏模式添加到编辑器默认游戏模式

                      ②将地图添加到编辑器游戏默认地图

                      ③关卡蓝图使用混合设置视图目标将场景中放置的摄像机类为玩家控制器所使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值