UE5学习笔记23-给角色添加血量,添加收到伤害的功能

零、一些游戏框架知识

        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是场景中的玩家出生点,多放几个试试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值