零、一些游戏框架知识
1.UE5中包含游戏模式类(Game Mode)、游戏状态类(Game State)、玩家状态类(Player State)、玩家控制器类(Player Controller)、所有的可以被控制的实体或角色类(Pawn)、窗口类(HUD/Widget)
Game Mode:存在在服务器上,当我们在客户端访问该类时获得的指针回事一个空指针
Game State:存在在服务器上和所有的客户端上,可以将服务器上的数据复制到游戏状态类的客户端中
Player State:存在在服务器上和所有客户端上
Player Controller:存在在服务器上和自己的客户端上
Pawn:存在在服务器上和所有客户端上
HUD/Widget:存在在自己的客户端上
2.Player Controller类可以访问HUD类
3.服务器上存在Game Mode、Game State、所有的Player State、所有的Player Controller、所有的Pawn、
4.Player Controller 可以访问当前客户端的Player State
5.玩家状态的网络更新比角色更新速度慢
一、创建血条界面
1.创建界面蓝图
2.选择UserWidget
3.界面中的控件如图
4.创建UserWidget的C++类
5.将之前创建的窗口的蓝图类的父类设置成在4中创建的C++类的类名(如图左下角父类设置)
6.C++类中的代码如下
头文件(注意不要写错变量名字和界面中的名字一定要相同)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CharacterOverLay.generated.h"
/**
*
*/
UCLASS()
class BLASTER_API UCharacterOverLay : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(meta = (Bindwidget))
class UProgressBar* HealthBar;
UPROPERTY(meta = (Bindwidget))
class UTextBlock* HealthText;
UPROPERTY(meta = (Bindwidget))
UTextBlock* ScoreAmount;
UPROPERTY(meta = (Bindwidget))
UTextBlock* DefeatsAmount;
};
源文件
创建之后不用管它源文件中没有写代码
7.添加在界面显示的功能,我在之前绘制界面准心的类中重写了BeginPlay函数
头文件(不要忘记在界面中设置变量CharacterOverlayClass的值)
public:
UPROPERTY(EditAnywhere, Category = "Play States")
TSubclassOf<class UUserWidget> CharacterOverlayClass;
class UCharacterOverLay* CharacterOverlay;
protected:
virtual void BeginPlay() override;
void AddCharacterOverlay();
源文件
void AABasterHUD::BeginPlay()
{
Super::BeginPlay();
AddCharacterOverlay();
}
void AABasterHUD::AddCharacterOverlay()
{
APlayerController* PlayController = GetOwningPlayerController();
if (PlayController && CharacterOverlayClass)
{
CharacterOverlay = CreateWidget<UCharacterOverLay>(PlayController, CharacterOverlayClass);
CharacterOverlay->AddToViewport();
}
}
二、更新血条状态跟新得分分数
1.通过PlayerController可以访问HUD控件类,所以在PlayerController类中代码如下
头文件
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "BlasterPlayerController.generated.h"
/**
*
*/
UCLASS()
class BLASTER_API ABlasterPlayerController : public APlayerController
{
GENERATED_BODY()
public:
void SetHUDHealth(float Health , float MaxHealth);
void SetHUDScore(float Score);
void SetHUDDefeats(int32 Defeats);
void OnPossess(APawn* IaPawn) override;
protected:
virtual void BeginPlay() override;
private:
UPROPERTY()
class AABasterHUD* BlasterHUD;
};
源文件
// Fill out your copyright notice in the Description page of Project Settings.
#include "BlasterPlayerController.h"
#include "Blaster/HUD/ABasterHUD.h"
#include "Blaster/HUD/CharacterOverLay.h"
#include "Components/ProgressBar.h"
#include "Components/TextBlock.h"
#include "Blaster/Character/BlasterCharacter.h"
void ABlasterPlayerController::BeginPlay()
{
Super::BeginPlay();
BlasterHUD = Cast<AABasterHUD>(GetHUD());
}
void ABlasterPlayerController::OnPossess(APawn* IaPawn)
{
Super::OnPossess(IaPawn);
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(IaPawn);
if (BlasterCharacter)
{
SetHUDHealth(BlasterCharacter->GetHealth(), BlasterCharacter->GetMaxHealth());
}
}
void ABlasterPlayerController::SetHUDHealth(float Health, float MaxHealth)
{
BlasterHUD = BlasterHUD == nullptr ? Cast<AABasterHUD>(GetHUD()) : BlasterHUD;
bool bHUDValid = BlasterHUD &&
BlasterHUD->CharacterOverlay &&
BlasterHUD->CharacterOverlay->HealthBar &&
BlasterHUD->CharacterOverlay->HealthText;
if (bHUDValid)
{
const float HealthPercent = Health / MaxHealth;
BlasterHUD->CharacterOverlay->HealthBar->SetPercent(HealthPercent);
FString HealthText = FString::Printf(TEXT("%d/%d"), FMath::CeilToInt(Health), FMath::CeilToInt(MaxHealth)); // CeilToInt向上取整
BlasterHUD->CharacterOverlay->HealthText->SetText(FText::FromString(HealthText));
}
}
void ABlasterPlayerController::SetHUDScore(float Score)
{
BlasterHUD = BlasterHUD == nullptr ? Cast<AABasterHUD>(GetHUD()) : BlasterHUD;
bool bHUDValid = BlasterHUD &&
BlasterHUD->CharacterOverlay &&
BlasterHUD->CharacterOverlay->ScoreAmount;
if (bHUDValid)
{
FString ScoreText = FString::Printf(TEXT("%d"), FMath::FloorToInt(Score)); // FloorToInt 向下取整
BlasterHUD->CharacterOverlay->ScoreAmount->SetText(FText::FromString(ScoreText));
}
}
void ABlasterPlayerController::SetHUDDefeats(int32 Defeats)
{
BlasterHUD = BlasterHUD == nullptr ? Cast<AABasterHUD>(GetHUD()) : BlasterHUD;
bool bHUDValid = BlasterHUD &&
BlasterHUD->CharacterOverlay &&
BlasterHUD->CharacterOverlay->DefeatsAmount;
if (bHUDValid)
{
FString DefeatsText = FString::Printf(TEXT("%d"), Defeats); // FloorToInt 向下取整
BlasterHUD->CharacterOverlay->DefeatsAmount->SetText(FText::FromString(DefeatsText));
}
}
2.我想在角色类中设置血量
角色类头文件
/**
* 玩家健康状态
*/
UPROPERTY(EditAnywhere, Category = "Platyer Stats")
float MaxHealth = 100.f;//玩家的最大血量
UPROPERTY(ReplicatedUsing = OnRep_Health, VisibleAnywhere, Category = "Platyer Stats")
float Health = 100.f;//玩家当前血量
UFUNCTION()
void OnRep_Health();//当人物当前血量改变时执行函数
class ABlasterPlayerController* BlasterPlayerController;
/* 更新界面血量函数 */
void UpdateHUDHealth();
/* 更新界面血量函数 */
/* 等待玩家状态存在后更新界面HUD */
void PollInit();
/* 等待玩家状态存在后更新界面HUD */
角色类源文件
void ABlasterCharacter::UpdateHUDHealth()
{
BlasterPlayerController = BlasterPlayerController == nullptr ? Cast<ABlasterPlayerController>(Controller) : BlasterPlayerController;
if (BlasterPlayerController)
{
BlasterPlayerController->SetHUDHealth(Health, MaxHealth);
}
}
void ABlasterCharacter::PollInit()
{
if (BlasterPlayerState == nullptr)
{
BlasterPlayerState = GetPlayerState<ABlasterPlayerState>();
if (BlasterPlayerState)
{
BlasterPlayerState->AddToScore(0.f);
BlasterPlayerState->AddToDefeats(0);
}
}
}
void ABlasterCharacter::OnRep_Health()
{
UpdateHUDHealth();
}
void ABlasterCharacter::UpdateHUDHealth()
{
BlasterPlayerController = BlasterPlayerController == nullptr ? Cast<ABlasterPlayerController>(Controller) : BlasterPlayerController;
if (BlasterPlayerController)
{
BlasterPlayerController->SetHUDHealth(Health, MaxHealth);
}
}
void ABlasterCharacter::BeginPlay()
{
Super::BeginPlay();
UpdateHUDHealth();
}
实现PlayerState类
头文件
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerState.h"
#include "BlasterPlayerState.generated.h"
/**
*
*/
UCLASS()
class BLASTER_API ABlasterPlayerState : public APlayerState
{
GENERATED_BODY()
public:
virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;
/**
* 复制通知
*/
virtual void OnRep_Score() override;
UFUNCTION()
virtual void OnRep_Defeats();
void AddToScore(float ScoreAmount);
void AddToDefeats(int32 DefeatsAmount);
private:
class ABlasterCharacter* Character;
class ABlasterPlayerController* Controller;
UPROPERTY(ReplicatedUsing = OnRep_Defeats)
int32 Defeats;
};
源文件
// Fill out your copyright notice in the Description page of Project Settings.
#include "BlasterPlayerState.h"
#include "Blaster/Character/BlasterCharacter.h"
#include "Blaster/PlayerController/BlasterPlayerController.h"
#include "Net/UnrealNetwork.h"
void ABlasterPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ABlasterPlayerState, Defeats);
}
void ABlasterPlayerState::OnRep_Score()
{
Super::OnRep_Score();
Character = Character == nullptr ? Cast<ABlasterCharacter>(GetPawn()) : Character;
if (Character)
{
Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
if (Controller)
{
Controller->SetHUDScore(Score);
}
}
}
void ABlasterPlayerState::AddToScore(float ScoreAmount)
{
SetScore(GetScore() + ScoreAmount);
Character = Character == nullptr ? Cast<ABlasterCharacter>(GetPawn()) : Character;
if (Character)
{
Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
if (Controller)
{
Controller->SetHUDScore(Score);
}
}
}
void ABlasterPlayerState::OnRep_Defeats()
{
Character = Character == nullptr ? Cast<ABlasterCharacter>(GetPawn()) : Character;
if (Character)
{
Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
if (Controller)
{
Controller->SetHUDDefeats(Defeats);
}
}
}
void ABlasterPlayerState::AddToDefeats(int32 DefeatsAmount)
{
Defeats += DefeatsAmount;
Character = Character == nullptr ? Cast<ABlasterCharacter>(GetPawn()) : Character;
if (Character)
{
Controller = Controller == nullptr ? Cast<ABlasterPlayerController>(Character->Controller) : Controller;
if (Controller)
{
Controller->SetHUDDefeats(Defeats);
}
}
}
三、添加角色收到伤害功能
1.根据子弹类创建新的C++类,命名为ProjectileBullet
2. 在子弹类中我定义了OnHit()函数去处理当子弹碰到物体时的逻辑,同时OnHit是一个Virtual的函数
头文件
protected:
virtual void OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) override;
源文件
void AProjectileBullet::OnHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
/**
* float ApplyDamage(
* AActor* DamagedActor, // 受伤的对象
* float BaseDamage, // 施加的基础伤害值
* AController* EventInstigator, // 伤害事件的控制者,通常是攻击者的控制器
* AActor* DamageCauser, // 造成伤害的对象,例如武器或攻击者
* TSubclassOf<UDamageType> DamageTypeClass // 指定伤害的类型,继承自 UDamageType
* );
*/
ACharacter* OwnerCharacter = Cast<ACharacter>(GetOwner());
if (OwnerCharacter)
{
AController* OwnerController = OwnerCharacter->Controller;
if (OwnerController)
{
UGameplayStatics::ApplyDamage(OtherActor, Damage, OwnerController,this,UDamageType::StaticClass());
}
}
Super::OnHit(HitComponent, OtherActor, OtherComp, NormalImpulse, Hit);
}
3.在子弹类中添加子弹伤害的变量
public:
UPROPERTY(EditAnywhere)
float Damage = 20.f;//武器伤害
4.在角色类中添加收到伤害的功能
头文件
/* 接收伤害的回调函数 */
UFUNCTION()
void ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, class AController* InstigatorController, AActor* DamageCauser);
/* 接收伤害的回调函数 */
源文件 (BeginPlay中绑定受到伤害的代理)
// Called when the game starts or when spawned
void ABlasterCharacter::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority())
{
OnTakeAnyDamage.AddDynamic(this,&ABlasterCharacter::ReceiveDamage);
}
}
void ABlasterCharacter::ReceiveDamage(AActor* DamagedActor, float Damage, const UDamageType* DamageType, AController* InstigatorController, AActor* DamageCauser)
{
Health = FMath::Clamp(Health - Damage, 0.f, MaxHealth);
UpdateHUDHealth();
PlayHitRecatMontage();
if (Health == 0.f)
{
ABlasterGameMode* BlasterGameMode = GetWorld()->GetAuthGameMode<ABlasterGameMode>();
if(BlasterGameMode)
{
BlasterPlayerController = BlasterPlayerController == nullptr ? Cast<ABlasterPlayerController>(Controller) : BlasterPlayerController;
ABlasterPlayerController* AttackerController = Cast<ABlasterPlayerController>(InstigatorController);
BlasterGameMode->PlayterEliminated(this,BlasterPlayerController, AttackerController);
}
}
}
四、给角色添加分数的游戏规则
1.创建新的C++类
2.代码实现
头文件
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameMode.h"
#include "BlasterGameMode.generated.h"
/**
*
*/
UCLASS()
class BLASTER_API ABlasterGameMode : public AGameMode
{
GENERATED_BODY()
public:
virtual void PlayterEliminated(class ABlasterCharacter* ElimmedCharacter, class ABlasterPlayerController* VictimController, ABlasterPlayerController* AttackerController);
virtual void RequestRespawn(ACharacter* ElimedCharacter, AController* ElimedController);
};
源文件
// Fill out your copyright notice in the Description page of Project Settings.
#include "BlasterGameMode.h"
#include "Blaster/Character/BlasterCharacter.h"
#include "Blaster/PlayerController/BlasterPlayerController.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/PlayerStart.h"
#include "Blaster/PlayerState/BlasterPlayerState.h"
void ABlasterGameMode::PlayterEliminated(ABlasterCharacter* ElimmedCharacter, ABlasterPlayerController* VictimController, ABlasterPlayerController* AttackerController)
{
ABlasterPlayerState* AttackerPlayerState = AttackerController ? Cast<ABlasterPlayerState>(AttackerController->PlayerState) : nullptr;
ABlasterPlayerState* VictimPlayerState = VictimController ? Cast<ABlasterPlayerState>(VictimController->PlayerState) : nullptr;
if (AttackerPlayerState && AttackerPlayerState != VictimPlayerState)
{
AttackerPlayerState->AddToScore(1.f);
}
if (VictimPlayerState)
{
VictimPlayerState->AddToDefeats(1);
}
if (ElimmedCharacter)
{
ElimmedCharacter->Elim();
}
}
void ABlasterGameMode::RequestRespawn(ACharacter* ElimedCharacter, AController* ElimedController)
{
if (ElimedCharacter)
{
ElimedCharacter->Reset();
ElimedCharacter->Destroy();
}
if (ElimedController)
{
TArray<AActor*> PlayerStart;
UGameplayStatics::GetAllActorsOfClass(this, APlayerStart::StaticClass(), PlayerStart);
int32 Selection = FMath::RandRange(0, PlayerStart.Num() - 1);
RestartPlayerAtPlayerStart(ElimedController, PlayerStart[Selection]);
}
}
3.若角色从新生成失败更改如下配置
4.代码中的PlayerStart是场景中的玩家出生点,多放几个试试