教程的鏈接:https://www.bilibili.com/video/BV1nU4y1X7iQ
總結(jié)一下這次的任務(wù)點(diǎn):
- 用PlayerState來做一個Credit系統(tǒng),需要在我們的ui內(nèi)顯示我們的分?jǐn)?shù)
- 更新血藥對象,每次使用血藥都會扣除相應(yīng)的分?jǐn)?shù)
- 新增一個金幣對象,每次和一個金幣對象交互就會增加一定的分?jǐn)?shù)
- 在地圖內(nèi)隨機(jī)生成金幣和血藥
- 金幣和血藥需要從同一個父類中繼承下來
整個分?jǐn)?shù)系統(tǒng)和我們的血量其實(shí)差不多,無非就是增加減少,檢查剩余的分?jǐn)?shù)夠不夠我們用來和血藥交互的。還是老規(guī)矩,直接上代碼
SPlayerState.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "Delegates/DelegateCombinations.h"
#include "SPlayerState.generated.h"
//藍(lán)圖宏
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnCreditChange, float, NewCredit);
UCLASS()
class ACTIONROGUELIKE_API ASPlayerState : public APlayerState
{
GENERATED_BODY()
ASPlayerState();
//藍(lán)圖節(jié)點(diǎn)
UPROPERTY(BlueprintAssignable)
FOnCreditChange OnCreditChange;
private:
float Credit;
private:
float CreditMax;
public:
UFUNCTION(BlueprintCallable,Category="Credit")
const float GetCredit() {
return Credit;
}
public:
UFUNCTION(BlueprintCallable, Category = "Credit")
void AddCredit(float DeltaCredit);
};
SPlayerState.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SPlayerState.h"
ASPlayerState::ASPlayerState()
{
CreditMax = 100;
Credit = 0;
}
void ASPlayerState::AddCredit(float DeltaCredit)
{
//我給分?jǐn)?shù)加了個上下限,這個功能可加可不加
Credit = FMath::Clamp(Credit + DeltaCredit, 0, CreditMax);
//每次更改都會在日志里頭打印出來
UE_LOG(LogTemp, Warning, TEXT("Credit:%f"),Credit);
//藍(lán)圖節(jié)點(diǎn)廣播
OnCreditChange.Broadcast(Credit);
}
各位應(yīng)該看得出來,我創(chuàng)建的這個類只有得分這一項(xiàng),其實(shí)看APlayerState.h里頭你也能看到一個和分?jǐn)?shù)一樣的東西,就是Score。這個類在這里的作用單純是存儲我們的Credit用的,然后提供get set方法而已。同時我也在代碼內(nèi)寫了一個廣播,那么我在藍(lán)圖編輯器內(nèi)是這樣寫的。
然后就是我們的父類設(shè)計,我這里把它命名為SBaseCreditUseActor,因?yàn)槲夷苷业降墓餐c(diǎn)就這點(diǎn)了。在這個類里面我使用了Interface,也就是說我們需要對著這些物品摁e才會觸發(fā)事件。
SBaseCreditUseActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SGameplayInterface.h"
#include "SPlayerState.h"
#include "SBaseCreditUseActor.generated.h"
UCLASS()
class ACTIONROGUELIKE_API ASBaseCreditUseActor : public AActor , public ISGameplayInterface
{
GENERATED_BODY()
//接口實(shí)現(xiàn)
void Interact_Implementation(APawn* InstigatorPawn) override;
UPROPERTY(EditAnywhere,Category="Component")
class UStaticMeshComponent* MeshComp;
public:
//用來判斷能否執(zhí)行實(shí)現(xiàn)函數(shù)
UFUNCTION(BlueprintCallable,Category="Credit")
bool CanImplement(ASPlayerState* ASP);
//實(shí)現(xiàn)函數(shù)
UFUNCTION(BlueprintCallable,Category="Implementation")
virtual void Implementation(ASPlayerState* ASP , APawn* InstigatorPawn);
public:
//CreditNeed的get set方法
UFUNCTION()
void SetCreditNeed(float Creditneed);
const float GetCreditNeed() {
return CreditNeed;
}
private:
float CreditNeed;
public:
// Sets default values for this actor's properties
ASBaseCreditUseActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
SBaseCreditUseActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SBaseCreditUseActor.h"
//接口實(shí)現(xiàn)函數(shù)
void ASBaseCreditUseActor::Interact_Implementation(APawn* InstigatorPawn)
{
if (!CanImplement(InstigatorPawn->GetPlayerState<ASPlayerState>()))
{
return;
}
Implementation(InstigatorPawn->GetPlayerState<ASPlayerState>(),InstigatorPawn);
}
//判斷能否執(zhí)行實(shí)現(xiàn)函數(shù)的函數(shù)
bool ASBaseCreditUseActor::CanImplement(ASPlayerState*ASP)
{
return ASP->GetCredit() >= CreditNeed;
}
//實(shí)現(xiàn)函數(shù),這里我沒寫是因?yàn)檫@里的函數(shù)每個子類都不相同,所以我們這里的函數(shù)都會在子類里面重寫
void ASBaseCreditUseActor::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{
}
//set方法
void ASBaseCreditUseActor::SetCreditNeed(float Creditneed)
{
CreditNeed = Creditneed;
}
// Sets default values
ASBaseCreditUseActor::ASBaseCreditUseActor()
{
// 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;
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh Component"));
RootComponent = MeshComp;
//CreditNeed默認(rèn)值
CreditNeed = 25;
}
// Called when the game starts or when spawned
void ASBaseCreditUseActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ASBaseCreditUseActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
這一串代碼其實(shí)就是我在之前的血瓶的代碼的基礎(chǔ)上抽象加上一點(diǎn)優(yōu)化得到的代碼,能適應(yīng)大部分的情況,不過少數(shù)的子類仍然需要重寫大量的父類函數(shù)。
首先是稍微簡單一點(diǎn)的金幣
SCoin.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SBaseCreditUseActor.h"
#include "SCoin.generated.h"
/**
*
*/
UCLASS()
class ACTIONROGUELIKE_API ASCoin : public ASBaseCreditUseActor
{
GENERATED_BODY()
ASCoin();
virtual void Implementation(ASPlayerState* ASP, APawn* InstigatorPawn);
};
SCoin.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SCoin.h"
ASCoin::ASCoin()
{
SetCreditNeed(0.0f);
}
void ASCoin::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{
ASP->AddCredit(10);
GetWorld()->DestroyActor(this);
}
這個類基本上沒有做太多的改動
在之后就是改動比較大的血瓶類
SHealthPotion.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "SBaseCreditUseActor.h"
#include "SHealthPotion.generated.h"
/**
*
*/
UCLASS()
class ACTIONROGUELIKE_API ASHealthPotion : public ASBaseCreditUseActor
{
GENERATED_BODY()
void Interact_Implementation(APawn* InstigatorPawn) override;
virtual void Implementation(ASPlayerState* ASP, APawn* InstigatorPawn) override;
UPROPERTY(EditDefaultsOnly,Category="Health")
float HealthDelta;
ASHealthPotion();
};
SHealthPotion.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SHealthPotion.h"
#include "SAttributeComponent.h"
void ASHealthPotion::Interact_Implementation(APawn* InstigatorPawn)
{
USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(InstigatorPawn);
//主要改動是if的判斷,我需要除了credit的扣除檢測之外還要檢測玩家血量,不能說滿血也能扣除分?jǐn)?shù)進(jìn)行補(bǔ)血
if (!CanImplement(InstigatorPawn->GetPlayerState<ASPlayerState>())||AttributeComp->GetHealth()==100)
{
return;
}
Implementation(InstigatorPawn->GetPlayerState<ASPlayerState>(), InstigatorPawn);
}
void ASHealthPotion::Implementation(ASPlayerState* ASP, APawn* InstigatorPawn)
{
USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(InstigatorPawn);
AttributeComp->ApplyHealthChange(this, HealthDelta);
ASP->AddCredit(-GetCreditNeed());
GetWorld()->DestroyActor(this);
}
ASHealthPotion::ASHealthPotion()
{
SetCreditNeed(25);
HealthDelta = 20.0f;
}
寫完以上這些之后,就剩下隨機(jī)生成的問題了,還記得之前的機(jī)器人生成的那個函數(shù)嗎?我們用相同的方法來做。
SGameModeBase.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "../../../../../UE_5.1/Engine/Source/Runtime/AIModule/Classes/EnvironmentQuery/EnvQueryInstanceBlueprintWrapper.h"
#include "SGameModeBase.generated.h"
/**
*
*/
UCLASS()
class ACTIONROGUELIKE_API ASGameModeBase : public AGameModeBase
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly,Category="AI")
class UEnvQuery* SpawnBotQuery;
UPROPERTY(EditDefaultsOnly, Category = "AI")
class UEnvQuery* SpawnHealthQuery;
UPROPERTY(EditDefaultsOnly,Category="AI")
TSubclassOf<AActor> MinionClass;
UPROPERTY(EditDefaultsOnly, Category = "AI")
TSubclassOf<AActor> HealthClass;
UPROPERTY(EditDefaultsOnly,Category="AI")
class UCurveFloat* DifficultyCurve;
FTimerHandle TimerHandle_SpawnBots;
UPROPERTY(EditDefaultsOnly,Category = "AI")
float SpawnTimerInterval;
UFUNCTION()
void SpawnBotTimerElapsed();
UFUNCTION()
void SpawnHealth();
UFUNCTION()
void OnQueryCompleted(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus);
UFUNCTION()
void OnQueryEnd(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus);
public:
ASGameModeBase();
public:
virtual void StartPlay() override;
UFUNCTION(Exec)
void KillAll();
UFUNCTION()
void RespawnPlayerElapsed(AController* Controller);
public:
virtual void OnActorKilled(AActor* VictimActor, AActor* Killer);
};
SGameModeBase.cpp文章來源:http://www.zghlxwxcb.cn/news/detail-530473.html
// Fill out your copyright notice in the Description page of Project Settings.
#include "SGameModeBase.h"
#include "EnvironmentQuery/EnvQueryManager.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "EnvironmentQuery/EnvQueryInstanceBlueprintWrapper.h"
#include "/APP/Tpl/Home/Default/Public/AI/SAICharacter.h"
#include "/APP/Tpl/Home/Default/Public/SAttributeComponent.h"
#include "../../../../../UE_5.1/Engine/Source/Runtime/Engine/Public/EngineUtils.h"
#include "SCharacter.h"
#include "SPlayerState.h"
#include "SHealthPotion.h"
static TAutoConsoleVariable<bool> CVarSpawnBots(TEXT("su.SpawnBots"),true,TEXT("Enable Spawing Bots via timer."), ECVF_Cheat);
void ASGameModeBase::SpawnBotTimerElapsed()
{
//看到下面這個函數(shù)沒,就是我們插入的函數(shù)(其實(shí)是我偷懶,懶得再寫一個timer了)
SpawnHealth();
if (!CVarSpawnBots.GetValueOnGameThread())
{
return;
}
int32 NrOfAliveBots = 0;
for (TActorIterator<ASAICharacter>It(GetWorld()); It; ++It)
{
ASAICharacter* Bot = *It;
USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(Bot);
if (ensure(AttributeComp) && AttributeComp->IsAlive())
{
NrOfAliveBots++;
}
}
float MaxBotCount = 10.0f;
if (DifficultyCurve)
{
MaxBotCount = DifficultyCurve->GetFloatValue(GetWorld()->TimeSeconds);
}
if (NrOfAliveBots >= MaxBotCount)
{
return;
}
UEnvQueryInstanceBlueprintWrapper* QuetyInstance = UEnvQueryManager::RunEQSQuery(this,SpawnBotQuery,this,EEnvQueryRunMode::RandomBest5Pct,nullptr);
if (ensure(QuetyInstance))
{
FScriptDelegate QueryCompleted;
QueryCompleted.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("&ASGameModeBase::OnQueryCompleted")));
QuetyInstance->GetOnQueryFinishedEvent().Add(QueryCompleted);
}
}
void ASGameModeBase::SpawnHealth()
{
int32 HealthExist = 0;
for (TActorIterator<ASHealthPotion>It(GetWorld()); It; ++It)
{
HealthExist++;
}
float MaxHealth = 5;
if (MaxHealth<=HealthExist)
{
return;
}
UEnvQueryInstanceBlueprintWrapper* QueryInstance = UEnvQueryManager::RunEQSQuery(this, SpawnHealthQuery, this, EEnvQueryRunMode::RandomBest25Pct, nullptr);
if (ensure(QueryInstance))
{
FScriptDelegate QueryStop;
QueryStop.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("&ASGameModeBase::OnQueryEnd")));
QueryInstance->GetOnQueryFinishedEvent().Add(QueryStop);
}
}
void ASGameModeBase::OnQueryCompleted(UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus)
{
if (QueryStatus != EEnvQueryStatus::Success)
{
UE_LOG(LogTemp,Warning,TEXT("Spawn EQS Failed!!!"))
return;
}
TArray<FVector> Locations = QueryInstance->GetResultsAsLocations();
if (Locations.IsValidIndex(0))
{
GetWorld()->SpawnActor<AActor>(MinionClass,Locations[0],FRotator::ZeroRotator);
}
}
void ASGameModeBase::OnQueryEnd(class UEnvQueryInstanceBlueprintWrapper* QueryInstance, EEnvQueryStatus::Type QueryStatus)
{
if (QueryStatus != EEnvQueryStatus::Success)
{
UE_LOG(LogTemp, Warning, TEXT("Spawn EQS Failed!!!"))
return;
}
TArray<FVector> Locations = QueryInstance->GetResultsAsLocations();
if (Locations.IsValidIndex(0))
{
GetWorld()->SpawnActor<AActor>(HealthClass, Locations[0], FRotator::ZeroRotator);
}
}
ASGameModeBase::ASGameModeBase()
{
SpawnTimerInterval = 2.0f;
}
void ASGameModeBase::StartPlay()
{
Super::StartPlay();
GetWorldTimerManager().SetTimer(TimerHandle_SpawnBots, this, &ASGameModeBase::SpawnBotTimerElapsed, SpawnTimerInterval, true);
}
void ASGameModeBase::KillAll()
{
for (TActorIterator<ASAICharacter>It(GetWorld()); It; ++It)
{
ASAICharacter* Bot = *It;
USAttributeComponent* AttributeComp = USAttributeComponent::GetArrtibutes(Bot);
if (ensure(AttributeComp) && AttributeComp->IsAlive())
{
AttributeComp->Kill(this);
}
}
}
void ASGameModeBase::RespawnPlayerElapsed(AController* Controller)
{
if (ensure(Controller))
{
Controller->UnPossess();
RestartPlayer(Controller);
//重生會扣50點(diǎn)Credit
ASPlayerState* ASP = Cast<ASPlayerState>(Controller->PlayerState);
if (ASP)
{
ASP->AddCredit(-50.0f);
}
}
}
//玩家被殺后復(fù)活的邏輯
void ASGameModeBase::OnActorKilled(AActor* VictimActor, AActor* Killer)
{
ASCharacter* Player = Cast<ASCharacter>(VictimActor);
if (Player)
{
FTimerHandle TimerHandle_RespawnDelay;
FTimerDelegate Delegate;
Delegate.BindUFunction(this,"RespawnPlayerElapsed",Player->GetController());
float RespawnDelay = 2.0f;
GetWorldTimerManager().SetTimer(TimerHandle_RespawnDelay,Delegate, RespawnDelay,false);
}
}
仿照之前寫的那個機(jī)器人生成的方法,同時我們把生成的函數(shù)直接插入到我們timer結(jié)束就會執(zhí)行的那個函數(shù)里頭(這是個偷懶的方法),有點(diǎn)投機(jī)取巧不過很方便。文章來源地址http://www.zghlxwxcb.cn/news/detail-530473.html
到了這里,關(guān)于UE5.1.1 C++從0開始(16.作業(yè)5思路分享)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!