捡电池案例
- 创建第三人称模版
创建Actor基类
- 创建一个Actor类用来作为可拾取物品基类
- 需求:我们让这个基类有静态网格可识别,然后得有一个状态就是是否被拾取的状态,那就得拥有改变状态的函数与返回状态的函数,然后和返回这个实体的函数
CollectActor.h
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CollectActor.generated.h"UCLASS()
class BATTERYRETRIEVER_API ACollectActor : public AActor
{GENERATED_BODY()public: // Sets default values for this actor's propertiesACollectActor();//静态网格UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Properties")class UStaticMeshComponent* StaticMesh;protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;//是否被拾取bool bIsActive;
public: // Called every framevirtual void Tick(float DeltaTime) override;//返回拾取的物体FORCEINLINE class UStaticMeshComponent* GetMesh() { return StaticMesh; }//返回拾取的状态UFUNCTION(BlueprintPure, Category = "Pickup")bool IsActive();//改变拾取的状态UFUNCTION(BlueprintCallable, Category = "Pickup")void SetActive(bool NewPickupState);
};
CollectActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.#include "CollectActor.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
ACollectActor::ACollectActor()
{// 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;StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));RootComponent = StaticMesh;
}// Called when the game starts or when spawned
void ACollectActor::BeginPlay()
{Super::BeginPlay();}// Called every frame
void ACollectActor::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}bool ACollectActor::IsActive()
{return bIsActive;
}void ACollectActor::SetActive(bool NewPickupState)
{bIsActive = NewPickupState;
}
设置电池的物体状态
- 创建CollectActor的子类用于创建电池子类
- SetSimulatePhysics:设置物理效果
- BatteryCollect.h
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "CollectActor.h"
#include "BatteryCollectActor.generated.h"/*** */
UCLASS()
class BATTERYRETRIEVER_API ABatteryCollectActor : public ACollectActor
{GENERATED_BODY()public:ABatteryCollectActor();protected:};
- BatteryCollect.cpp
// Fill out your copyright notice in the Description page of Project Settings.#include "BatteryCollectActor.h"ABatteryCollectActor::ABatteryCollectActor()
{//设置物理状态GetMesh()->SetSimulatePhysics(true);}
- 创建一下碰撞
创建生成器
- 创建一个Actor用来当做生成器
- 创建一个Box的组件作为生成空间,定义电池的生成变量,获取随机点生成的点处理函数与生成电池的处理函数,我们要使用定时器来进行几秒就生成一次,还需要定义时间句柄与最大最小时间范围与真实的时间
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
class UBoxComponent* SpawnBox;
//定义电池生成变量
UPROPERTY(EditAnywhere, Category = "Spawn Volume")
TSubclassOf<class ACollectActor>SpawnClass;
FTimerHandle TimerHandle;
//最小延迟
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawn Volume")
float MinDelayTimer;
//最大延迟
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawn Volume")
float MaxDelayTimer;
//真实的延迟
float SpawnDelay;//获取随机点UFUNCTION(BlueprintPure, Category = "Spawn Volume")FVector GetSpawnRandomLocation();//管理一个新的触发拾取void SpawnPickup();
- 获取随机点生成的点处理函数
- SpawnBox->Bounds.Origin:保存边界框和球体的原点
- SpawnBox->Bounds.BoxExtent:保存边界框的范围
FVector ASpawnVolume::GetSpawnRandomLocation()
{FVector SpawnOrigin = SpawnBox->Bounds.Origin;FVector SpawnExtent = SpawnBox->Bounds.BoxExtent;return UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent);
}
- 也可以这样,调用函数来获取随机点
- 生成电池的处理函数
void ASpawnVolume::SpawnPickup()
{if (SpawnClass != NULL){UWorld* World = GetWorld();if (World){//设置触发参数FActorSpawnParameters SpawnParams;SpawnParams.Owner = this;SpawnParams.Instigator = GetInstigator();//获取触发的随机位置FVector SpawnLocation = GetSpawnRandomLocation();//获取触发物体的随机旋转FRotator SpawnRotation;SpawnRotation.Yaw = FMath::FRand() * 360.f;SpawnRotation.Pitch = FMath::FRand() * 360.f;SpawnRotation.Roll = FMath::FRand() * 360.f;//生成电池ACollectActor* SpawnPickup = World->SpawnActor<ACollectActor>(SpawnClass, SpawnLocation,SpawnRotation, SpawnParams);//真实延迟时间范围SpawnDelay = FMath::FRandRange(MinDelayTimer, MaxDelayTimer);//定时器生成电池GetWorld()->GetTimerManager().SetTimer(TimerHandle,this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);}}
}
- 然后在BeginPlay中定时器生成我们的电池
// Called when the game starts or when spawned
void ASpawnVolume::BeginPlay()
{Super::BeginPlay();//真实延迟时间范围SpawnDelay = FMath::FRandRange(MinDelayTimer, MaxDelayTimer);//定时器生成电池GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);}
SpawnVolume.h
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SpawnVolume.generated.h"UCLASS()
class BATTERYRETRIEVER_API ASpawnVolume : public AActor
{GENERATED_BODY()public: // Sets default values for this actor's propertiesASpawnVolume();UPROPERTY(VisibleAnywhere, BlueprintReadOnly)class UBoxComponent* SpawnBox;//定义电池生成变量UPROPERTY(EditAnywhere, Category = "Spawn Volume")TSubclassOf<class ACollectActor>SpawnClass;FTimerHandle TimerHandle;//最小延迟UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawn Volume")float MinDelayTimer;//最大延迟UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Spawn Volume")float MaxDelayTimer;//真实的延迟float SpawnDelay;
protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;
public: // Called every framevirtual void Tick(float DeltaTime) override;//获取随机点UFUNCTION(BlueprintPure, Category = "Spawn Volume")FVector GetSpawnRandomLocation();//管理一个新的触发拾取void SpawnPickup();
};
SpawnVolume.cpp
// Fill out your copyright notice in the Description page of Project Settings.#include "SpawnVolume.h"
#include "Components/BoxComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include "../MyActors/CollectActor.h"
#include "Engine/Engine.h"
// Sets default values
ASpawnVolume::ASpawnVolume()
{// 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;SpawnBox = CreateDefaultSubobject<UBoxComponent>(TEXT("SpawnBox"));RootComponent = SpawnBox;//延迟区间MinDelayTimer = 1.f;MaxDelayTimer = 4.f;
}// Called when the game starts or when spawned
void ASpawnVolume::BeginPlay()
{Super::BeginPlay();//真实延迟时间范围SpawnDelay = FMath::FRandRange(MinDelayTimer, MaxDelayTimer);//定时器生成电池GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);}// Called every frame
void ASpawnVolume::Tick(float DeltaTime)
{Super::Tick(DeltaTime);}FVector ASpawnVolume::GetSpawnRandomLocation()
{FVector SpawnOrigin = SpawnBox->Bounds.Origin;FVector SpawnExtent = SpawnBox->Bounds.BoxExtent;FVector OriginPoint = SpawnBox->GetComponentLocation();return UKismetMathLibrary::RandomPointInBoundingBox(SpawnOrigin, SpawnExtent);
}void ASpawnVolume::SpawnPickup()
{if (SpawnClass!=NULL){UWorld* World = GetWorld();if (World){//设置触发参数FActorSpawnParameters SpawnParams;SpawnParams.Owner = this;SpawnParams.Instigator = GetInstigator();//获取触发的随机位置FVector SpawnLocation = GetSpawnRandomLocation();//获取触发物体的随机旋转FRotator SpawnRotation;SpawnRotation.Yaw = FMath::FRand() * 360.f;SpawnRotation.Pitch = FMath::FRand() * 360.f;SpawnRotation.Roll = FMath::FRand() * 360.f;//生成电池ACollectActor* const SpawnPickup = World->SpawnActor<ACollectActor>(SpawnClass, SpawnLocation,SpawnRotation, SpawnParams);//真实延迟时间范围SpawnDelay = FMath::FRandRange(MinDelayTimer, MaxDelayTimer);//定时器生成电池GetWorld()->GetTimerManager().SetTimer(TimerHandle,this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);}}
}
测试生成器
- 将我们的SpawnVolume类创建为蓝图拖入场景进行测试
拾取类收集状态重写
- 在CollectActor基类中添加一个收集方法,这个方法带BlueprintNativeEvent属性可以在C++中和蓝图中调用,用virtual修饰,让子类可以继承
//当物体被收集时调用此方法UFUNCTION(BlueprintNativeEvent,BlueprintCallable)void WasCollected();virtual void WasCollected_Implementation();
void ACollectActor::WasCollected_Implementation()
{FString PickupDebug = GetName();UE_LOG(LogTemp, Warning, TEXT("你收集了%s"), *PickupDebug);
}
- BatteryCollectActor中继承此方法
void ABatteryCollectActor::WasCollected_Implementation()
{Super::WasCollected_Implementation();//销毁电池Destroy();
}
角色控制器的按键收集
逻辑需求
- 我们给第三人称模版添加一个球形检测组件,用于来检测电池是否可以收集,然后创建一个收集电池的处理函数
//球形检测UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Collection")class USphereComponent* CollectionSphere;//收集电池处理函数
UFUNCTION(BlueprintCallable, Category = "Pickups")
void CollectPickups();
- 初始化球形组件
//创建球形组件CollectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("CollectionSphere"));CollectionSphere->SetupAttachment(GetRootComponent());CollectionSphere->SetSphereRadius(200.f);
- 收集电池处理函数逻辑:
将所有与角色重叠的组件存入数组,遍历数组,数组中的组件转换为电池基类
,成功并且电池没有被销毁也没有被拾取就调用电池被收集的处理方法,并将电池标记为已失去状态 GetOverlappingActors
:TArray中的函数用于返回该组件的参与重叠者列表
void ABatteryRetrieverCharacter::CollectPickups()
{TArray<AActor*> CollectionActors;//返回该组件的参与重叠者列表CollectionSphere->GetOverlappingActors(CollectionActors);float CollectPower = 0.f;//能力值//遍历所有区域的电池,并存入数组for (int32 i = 0; i < CollectionActors.Num(); i++){ACollectActor* Collect = Cast<ACollectActor>(CollectionActors[i]);//转换成功,电池没有被销毁,电池没有被拾取if (Collect && !Collect->IsPendingKill() && Collect->IsActive()){Collect->WasCollected();//调用电池被收集的方法Collect->SetActive(false);//电池被拾取}}}
- 记得将CollectActor类中的bIsActive赋初值为true,一开始就是可拾取状态
bIsActive = true;
增强输入系统绑定
- 我们绑定C键为捡取电池,在模版角色中添加一个增强输入行为
/** PicktupC Input Action */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))class UInputAction* PicktupC;
- 在SetupPlayerInputComponent()中绑定这个输入行为
//Pickup
EnhancedInputComponent->BindAction(PicktupC, ETriggerEvent::Triggered, this, &ABatteryRetrieverCharacter::CollectPickups);
- 在UE中创建这个输入行为,因为是简单的拾取用bool值类型即可
- 在设备映射集合中添加这个行为,绑定按键C
- 最后在模版蓝图中添加这个输入行为
- 运行结果
角色能量值定义
- 首先电池类中,我们要定义电池能恢复多少能力的初值
UCLASS()
class BATTERYRETRIEVER_API ABatteryCollectActor : public ACollectActor
{GENERATED_BODY()public:ABatteryCollectActor();void WasCollected_Implementation()override;//获取能量float GetPower();
protected:UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float BatteryPower;
};
// Fill out your copyright notice in the Description page of Project Settings.#include "BatteryCollectActor.h"ABatteryCollectActor::ABatteryCollectActor()
{//设置物理状态GetMesh()->SetSimulatePhysics(true);//电池能力值BatteryPower = 200.f;
}void ABatteryCollectActor::WasCollected_Implementation()
{Super::WasCollected_Implementation();//销毁电池Destroy();
}//返回能量值
float ABatteryCollectActor::GetPower()
{return BatteryPower;
}
- 角色类中也是要定义初始能量值与角色最大能量值
//初始能量值与角色能量值UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float InitalPower;UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float CharacterPower;
//------------------------------------------------------------------
InitalPower = 2000.f;
CharacterPower = InitalPower;
获取能量值
- 在角色模版中定义几个方法,分别是获取初始能量、当前能量、更新设置能量,前两个处理函数都要在蓝图中进行调用,所以要加上
BlueprintPure
参数宏,第三个方法没有返回值,就不能加上BlueprintPure
宏,用BlueprintCallable
宏
UFUNCTION(BlueprintPure,Category="Power")float GetInitialPower();UFUNCTION(BlueprintPure, Category = "Power")float GetCurrentPower();UFUNCTION(BlueprintCallable, Category = "Power")void UpdataPower(float PowerChange);
- 处理函数方法
float ABatteryRetrieverCharacter::GetInitialPower()
{return InitalPower;
}float ABatteryRetrieverCharacter::GetCurrentPower()
{return CharacterPower;
}void ABatteryRetrieverCharacter::UpdataPower(float PowerChange)
{CharacterPower += PowerChange;
}
- 在收集电池处理函数中继续编写捡起电池的逻辑
void ABatteryRetrieverCharacter::CollectPickups()
{TArray<AActor*> CollectionActors;//返回该组件的参与重叠者列表CollectionSphere->GetOverlappingActors(CollectionActors);float CollectPower = 0.f;//能力值//遍历所有区域的电池,并存入数组for (int32 i = 0; i < CollectionActors.Num(); i++){ACollectActor* Collect = Cast<ACollectActor>(CollectionActors[i]);//转换成功,电池没有被销毁,电池没有被拾取if (Collect && !Collect->IsPendingKill() && Collect->IsActive()){Collect->WasCollected();//调用电池被收集的方法//检查CollectActor是否是BatteryCollect电池类ABatteryCollectActor* Battery = Cast<ABatteryCollectActor>(Collect);if (Battery){CollectPower += Battery->GetPower();//如果是当前电池,就增加能量值}Collect->SetActive(false);//电池被拾取}}if (CollectPower > 0.f){UpdataPower(CollectPower);//更新能量值}}
角色能量衰减
- 在GameMode里面编写角色每秒衰减能量值的逻辑
- 添加一个衰减的速率变量,然后重写Tick逻辑
BatteryRetrieverGameMode.h
// Copyright Epic Games, Inc. All Rights Reserved.#pragma once#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "BatteryRetrieverGameMode.generated.h"UCLASS(minimalapi)
class ABatteryRetrieverGameMode : public AGameModeBase
{GENERATED_BODY()public:ABatteryRetrieverGameMode();//衰减速率UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Power")float DecayRate;protected://重写Tickvirtual void Tick(float DeltaTime) override;
};
BatteryRetrieverGameMode.cpp
// Copyright Epic Games, Inc. All Rights Reserved.#include "BatteryRetrieverGameMode.h"
#include "BatteryRetrieverCharacter.h"
#include "UObject/ConstructorHelpers.h"
#include "Kismet/GameplayStatics.h"
ABatteryRetrieverGameMode::ABatteryRetrieverGameMode()
{//开启TickPrimaryActorTick.bCanEverTick = true;// set default pawn class to our Blueprinted characterstatic ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/ThirdPerson/Blueprints/BP_ThirdPersonCharacter"));if (PlayerPawnBPClass.Class != NULL){DefaultPawnClass = PlayerPawnBPClass.Class;}DecayRate = 0.01f;
}void ABatteryRetrieverGameMode::Tick(float DeltaTime)
{Super::Tick(DeltaTime);ABatteryRetrieverCharacter* MyCharacter = Cast<ABatteryRetrieverCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));if (MyCharacter){if (MyCharacter->GetCurrentPower() > 0.f){//每帧进行衰减能量MyCharacter->UpdataPower(-(DeltaTime * DecayRate * MyCharacter->GetInitialPower()));}}}
修改玩家速度
- 当能量到达某个状态的时候控制的玩家应该需要被减速
- 我们新建一个玩家基础速度变量与一个速度因子用来控制玩家的速度和一个在蓝图中调用的处理能量值变化的函数方法
//速度因子UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float SpeedFactor;//初始速度UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float BaseSpeed;UFUNCTION(BlueprintImplementableEvent, BlueprintCallable, Category = "Power")void PowerChangeEffect();
- 根据能量更新玩家速度
SpeedFactor = 0.75f;
BaseSpeed = 10.f;void ABatteryRetrieverCharacter::UpdataPower(float PowerChange)
{CharacterPower += PowerChange;//根据Power改变玩家速度GetCharacterMovement()->MaxWalkSpeed = BaseSpeed + SpeedFactor * CharacterPower;}
拾取电池的粒子效果
- 在电池蓝图中去进行添加粒子效果的设置,使用之前在C++编写好的处理函数,进行粒子添加
设置玩家的材质变化
-
在第三人称蓝图脚本中创建指定的材质实例
-
然后在Tick中去实现颜色的变化,注意
UE5中的BlueprintImplementableEvent宏参数的函数,必须要调用了,UE4中使用的时候是不用调用的
-
Tint是第三人称模版材质中的颜色变换
游戏通关条件设定
- 游戏通关状态我们在GameMode中进行判断
- 重写一下BeginPlay函数用于编写通关逻辑,新建一个可以通关的能量值变量
//通关需要的能量值UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")float PowerToWin;//重写BeginPlayvirtual void BeginPlay() override;
- BeginPlay逻辑
void ABatteryRetrieverGameMode::BeginPlay()
{Super::BeginPlay();ABatteryRetrieverCharacter* MyCharacter = Cast<ABatteryRetrieverCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));//游戏通过条件if (MyCharacter){PowerToWin = MyCharacter->GetInitialPower() * 1.25f;}
}
-
然后在项目名.Build.cs文件中添加模块
-
PublicDependencyModuleNames.AddRange(new string[] { “Slate”, “SlateCore” }); 表示将 Slate 和 SlateCore 模块添加为当前模块的公共依赖项。这意味着在编译当前模块时,编译器会确保先编译和链接 Slate 和 SlateCore 模块。
-
而 PublicDependencyModuleNames.AddRange(new string[] { “UMG” }); 则表示将 UMG 模块添加为当前模块的公共依赖项。由于 UMG 是基于 Slate 和 SlateCore 构建的,所以在启用 UMG 插件后,Slate 和 SlateCore 会自动成为项目的依赖项。
// Copyright Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;public class BatteryRetriever : ModuleRules
{public BatteryRetriever(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput" });PublicDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });PublicDependencyModuleNames.AddRange(new string[] { "UMG" });}
}
- 在电池类中头文件中要添加#include “Core.h”
- 在GameMode类中声明UMG组件
//HUD的WidgetUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power")TSubclassOf<class UUserWidget> HUDWidgetClass;//HUD实例UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Power")UUserWidget* CurrentWidget;
- 创建UMG
- CreateWidget:创建Widget,
头文件#include "Blueprint/UserWidget.h"
- AddToViewport:添加视口
void ABatteryRetrieverGameMode::BeginPlay()
{Super::BeginPlay();ABatteryRetrieverCharacter* MyCharacter = Cast<ABatteryRetrieverCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));//游戏通过条件if (MyCharacter){PowerToWin = MyCharacter->GetInitialPower() * 1.25f;}//创建UMGif (HUDWidgetClass != nullptr){CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWidgetClass);if (CurrentWidget){CurrentWidget->AddToViewport();}}
}
人物能量进度条UMG
- 创建一个UI控件用来显示电池能量的UMG
- 创建一个蓝图的GameMode,添加上当前UI的界面
- 此时UI界面就添加上去了
- 进度条绑定一下函数,与能量值实时交互逻辑
设置游戏状态
- 在GameMode里面添加一个枚举类型表示当前游戏状态,枚举类型变量有游戏中、游戏胜利、游戏结束、游戏未知的状态
UENUM(BlueprintType)
enum class EBatteryPlayState :uint8
{EBPS_Playing UMETA(DisplayName="Playing"),EBPS_GameOver UMETA(DisplayName = "GemaOver"),EBPS_Won UMETA(DisplayName = "Won"),EBPS_Unknown UMETA(DisplayName="Unknow")
};
- 新增一个枚举变量对象,与两个处理函数方法
//当前状态对象EBatteryPlayState CurrentState;//获取当前状态UFUNCTION(BlueprintPure, Category = "State")EBatteryPlayState GetCurrentState();//设置当前状态UFUNCTION(BlueprintCallable,Category="State")void SetCurrentState(EBatteryPlayState StateName);
- 处理函数逻辑
EBatteryPlayState ABatteryRetrieverGameMode::GetCurrentState()
{return CurrentState;
}void ABatteryRetrieverGameMode::SetCurrentState(EBatteryPlayState StateName)
{CurrentState = StateName;
}
- Tick游戏中的逻辑编写,当前电池收集量大于胜利电池能量时游戏状态为胜利,当当前能量小于0了游戏设置结束
void ABatteryRetrieverGameMode::Tick(float DeltaTime)
{Super::Tick(DeltaTime);ABatteryRetrieverCharacter* MyCharacter = Cast<ABatteryRetrieverCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));if (MyCharacter){if (MyCharacter->GetCurrentPower() > PowerToWin){//游戏胜利SetCurrentState(EBatteryPlayState::EBPS_Won);}else if (MyCharacter->GetCurrentPower() > 0.f){//每帧进行衰减能量MyCharacter->UpdataPower(-(DeltaTime * DecayRate * MyCharacter->GetInitialPower()));}else{//游戏结束SetCurrentState(EBatteryPlayState::EBPS_GameOver);}}}
- 在UI中添加一个Text用来表示当前游戏状态
- 运行结果
游戏状态处理
- 需求分析:我们现在得让游戏开始的时候才开始掉落电池,游戏结束了就不在掉落电池
- 在生成器类中添加一个是否开启掉落电池的处理方法,然后删除一开始在BeginPlay里面测试一直生成掉落电池代码
//是否开启电池掉落UFUNCTION(BlueprintCallable, Category = "Spawn Volume")void SetSpawningActive(bool bState);
- 处理方法逻辑:检测状态是否为真,为真就生成电池,为假就停止生产
void ASpawnVolume::SetSpawningActive(bool bState)
{if (bState){SpawnDelay = FMath::FRandRange(MinDelayTimer, MaxDelayTimer);GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &ASpawnVolume::SpawnPickup, SpawnDelay, false);}else{GetWorld()->GetTimerManager().ClearTimer(TimerHandle);}
}
- 在GameMode类中,添加一个数组记录关卡中的SpawnVolume,添加一个回调状态的方法
//记录关卡中的SpawnVolume
TArray<class SpawnVolume*> SpawnVolumeActors;//处理玩家状态方法回调
void HandleNewState(EBatteryPlayState NewState);
- BeginPlay中获取到所有的ASpawnVolume
void ABatteryRetrieverGameMode::BeginPlay()
{Super::BeginPlay();TArray<AActor*> FoundActors;UGameplayStatics::GetAllActorsOfClass(GetWorld(), ASpawnVolume::StaticClass(), FoundActors);for (auto Actor : FoundActors){ASpawnVolume* SpawnVolumeActor = Cast<ASpawnVolume>(Actor);if (SpawnVolumeActor){SpawnVolumeActors.AddUnique(SpawnVolumeActor);}}SetCurrentState(EBatteryPlayState::EBPS_Playing);ABatteryRetrieverCharacter* MyCharacter = Cast<ABatteryRetrieverCharacter>(UGameplayStatics::GetPlayerPawn(this, 0));//游戏通过条件if (MyCharacter){PowerToWin = MyCharacter->GetInitialPower() * 1.25f;}if (HUDWidgetClass != nullptr){CurrentWidget = CreateWidget<UUserWidget>(GetWorld(), HUDWidgetClass);if (CurrentWidget){CurrentWidget->AddToViewport();}}
}
- 在设置当前状态中更新回调处理方法
void ABatteryRetrieverGameMode::SetCurrentState(EBatteryPlayState StateName)
{CurrentState = StateName;HandleNewState(StateName);
}
- 回调方法逻辑
void ABatteryRetrieverGameMode::HandleNewState(EBatteryPlayState NewState)
{switch (NewState){case EBatteryPlayState::EBPS_Playing:for (ASpawnVolume* Volume : SpawnVolumeActors){Volume->SetSpawningActive(true);};break;case EBatteryPlayState::EBPS_GameOver:for (ASpawnVolume* Volume : SpawnVolumeActors){Volume->SetSpawningActive(false);};break;case EBatteryPlayState::EBPS_Won:break;case EBatteryPlayState::EBPS_Unknown:break;default:break;}
}
玩家死亡效果
- 在游戏结束后,我们要禁用用户输入,然后开启物理系统,禁止跳跃
- 头文件:
#include "GameFramework/Character.h"
#include "GameFramework/PawnMovementComponent.h"
void ABatteryRetrieverGameMode::HandleNewState(EBatteryPlayState NewState)
{switch (NewState){case EBatteryPlayState::EBPS_Playing:for (ASpawnVolume* Volume : SpawnVolumeActors){Volume->SetSpawningActive(true);};break;case EBatteryPlayState::EBPS_GameOver:for (ASpawnVolume* Volume : SpawnVolumeActors){Volume->SetSpawningActive(false);APlayerController* PlayerControl = UGameplayStatics::GetPlayerController(this, 0);if (PlayerControl){//禁用部分输入PlayerControl->SetCinematicMode(true, false, false, true, true);}//加入布娃娃系统ACharacter* MyCharacter = UGameplayStatics::GetPlayerCharacter(this, 0);if (MyCharacter){MyCharacter->GetMesh()->SetSimulatePhysics(true);MyCharacter->GetMovementComponent()->MovementState.bCanJump = false;}}; break;case EBatteryPlayState::EBPS_Won:break;case EBatteryPlayState::EBPS_Unknown:break;default:break;}
}
- 在第三人称模版中添加一个物理资产
- 将第三人称模版Mesh碰撞改成启用碰撞(查询与物理)
- 运行结果