虚幻引擎UE5专用服务器游戏开发-18 隐藏本地玩家角色头顶控件显示的

1.服务器、客户端里面角色和控制器之间的关系:

在客户端中,只有一个本地玩家控制器;但在服务器端(不管是监听服务器还是专用服务器),都有其他玩家的控制器。

所以,我们先判断,屏幕上的角色,是不是由本地玩家控制器控制的:

Source/Crunch/Public/Character/CCharacter.h

	//判断是否有本地玩家控制
	bool IsLocallyControlledByPlayer();

Source/Crunch/Private/Character/CCharacter.cpp

bool ACCharacter::IsLocallyControlledByPlayer()
{
	return GetController() != nullptr && GetController()->IsLocalPlayerController();
}

思考:

如果,我将上面的IsLocalPlayerController()换成IsLocalController()会如何呢?

在Unreal Engine中,IsLocalPlayerController()IsLocalController()的区别主要体现在以下方面:

  1. 所属类与功能定位

    • IsLocalPlayerController()APlayerController类的成员函数,专门用于判断当前PlayerController是否为本地控制的玩家控制器
    • IsLocalController()AController基类的成员函数,适用于所有控制器类型(包括PlayerController和AIController)的本地化判断
  2. 应用场景差异

    • IsLocalPlayerController()通常用于:
      • 分屏游戏中区分不同本地玩家的控制器实例
      • 确保输入事件、UI交互等操作仅对本地玩家生效
    • IsLocalController()更通用,可用于:
      • AI控制的本地模拟(如离线训练模式)
      • 网络游戏中判断任意控制器的本地归属
  3. 返回值逻辑

    • IsLocalPlayerController()需同时满足:
      • 控制器是PlayerController类型
      • 控制器由本地机器控制
    • IsLocalController()仅判断控制器是否由本地机器控制,不关心具体类型
  4. 典型使用示例

    // 仅PlayerController专用判断
    if (PlayerController->IsLocalPlayerController()) {
        // 处理本地玩家专属逻辑
    }
    
    // 通用控制器判断
    if (AnyController->IsLocalController()) {
        // 处理本地控制的AI或玩家逻辑
    }
    

对于需要精确区分玩家控制的场景,推荐使用IsLocalPlayerController();若需兼容AI控制器的本地化判断,则应选择IsLocalController()

void ACCharacter::ConfigureOverHeadStateWidget()
{
	if (!OverHeadWidgetComponent)
	{
		UE_LOG(LogTemp, Error, TEXT("OverHeadWidgetComponent is NULL"));
		return;
	}
	//如果是本地玩家控制的角色,不显示状态条
	if (IsLocallyControlledByPlayer())
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}
	//得到小部件:头顶属性条
	UOverHeadStateGauge* OverHeadStateGauge = Cast<UOverHeadStateGauge>(
		OverHeadWidgetComponent->GetUserWidgetObject());
	if (OverHeadStateGauge)
	{
		//调用能力系统组件,并配置它
		OverHeadStateGauge ->ConfigWithASC(GetAbilitySystemComponent());
		OverHeadWidgetComponent->SetHiddenInGame(false);
	}
}

 效果

拖入AI角色:

发现,AI状态条不更新!

2.检查C++代码,发现,我们只是在玩家控制器调用的ServerSideInit(),而AI角色没有执行过!

void ACPlayerController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);
	CPlayerCharacter = Cast<ACPlayerCharacter>(InPawn);
	if (CPlayerCharacter)
	{
		CPlayerCharacter->ServerSideInit();
	}
}
void ACCharacter::ServerSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
	CAbilitySystemComponent->ApplyInitialEffects();		//效果初始化
}

 所以,要在角色类里,重写服务器调用函数:

	// 只有服务器调用的方法
	virtual void PossessedBy(AController* NewController) override;
void ACCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);
	// AI控制的角色
	if (NewController && !NewController->IsPlayerController())
	{	
		ServerSideInit();
	}
}

源码:

Source/Crunch/Public/Character/CCharacter.h:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "CCharacter.generated.h"

class UWidgetComponent;
class UCAbilitySystemComponent;
class UCAttributeSet;

UCLASS(Abstract)
class CRUNCH_API ACCharacter : public ACharacter, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	
	ACCharacter();
	//服务器端初始化
	void ServerSideInit();		
	//客户端初始化
	void ClientSideInit();		
	//判断是否有本地玩家控制
	bool IsLocallyControlledByPlayer();
	// 只有服务器调用的方法
	virtual void PossessedBy(AController* NewController) override;

	/********************************************************************************/
	/*                             GameplayAbilitySystem                            */
	/********************************************************************************/
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
	

	
protected:
	
	virtual void BeginPlay() override;


private:
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAbilitySystemComponent> CAbilitySystemComponent;
	
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAttributeSet> CAttributeSet;

	/********************************************************************************/
	/*                                       UI                                     */
	/********************************************************************************/
	UPROPERTY(VisibleDefaultsOnly, Category="UI")
	TObjectPtr<UWidgetComponent> OverHeadWidgetComponent;		//小部件组件

	//控制头顶状态部件
	void ConfigureOverHeadStateWidget();
};

Source/Crunch/Private/Character/CCharacter.cpp:

// Copyright@ChenChao


#include "Character/CCharacter.h"

#include "Components/WidgetComponent.h"
#include "GAS/CAbilitySystemComponent.h"
#include "GAS/CAttributeSet.h"
#include "UI/Widget/OverHeadStateGauge.h"

// Sets default values
ACCharacter::ACCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	// 网格体无碰撞
	GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	//添加组件
	CAbilitySystemComponent = CreateDefaultSubobject<UCAbilitySystemComponent>(TEXT("CAbilitySystemComponent"));
	CAttributeSet = CreateDefaultSubobject<UCAttributeSet>(TEXT("CAttributeSet"));

	OverHeadWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("OverHeadWidgetComponent"));
	OverHeadWidgetComponent->SetupAttachment(GetRootComponent());
}

void ACCharacter::ServerSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
	CAbilitySystemComponent->ApplyInitialEffects();		//效果初始化
}

void ACCharacter::ClientSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
}

bool ACCharacter::IsLocallyControlledByPlayer()
{
	return GetController() && GetController()->IsLocalPlayerController();
}

void ACCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);
	// AI控制的角色
	if (NewController && !NewController->IsPlayerController())
	{	
		ServerSideInit();
	}
}

UAbilitySystemComponent* ACCharacter::GetAbilitySystemComponent() const
{
	return CAbilitySystemComponent;
}


void ACCharacter::BeginPlay()
{
	Super::BeginPlay();
	ConfigureOverHeadStateWidget();
}

void ACCharacter::ConfigureOverHeadStateWidget()
{
	if (!OverHeadWidgetComponent)
	{
		UE_LOG(LogTemp, Error, TEXT("OverHeadWidgetComponent is NULL"));
		return;
	}
	//如果是本地玩家控制的角色,不显示状态条
	if (IsLocallyControlledByPlayer())
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}
	//得到小部件:头顶属性条
	UOverHeadStateGauge* OverHeadStateGauge = Cast<UOverHeadStateGauge>(
		OverHeadWidgetComponent->GetUserWidgetObject());
	if (OverHeadStateGauge)
	{
		//调用能力系统组件,并配置它
		OverHeadStateGauge ->ConfigWithASC(GetAbilitySystemComponent());
		OverHeadWidgetComponent->SetHiddenInGame(false);
	}
}

效果:

PS:其实,我们习惯在角色类里的PossessedBy方法里写服务器代码,在控制器类的AcknowledgePossession方法里写客户端方法:

修改如下:

Source/Crunch/Public/Character/CCharacter.h:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystemInterface.h"
#include "CCharacter.generated.h"

class UWidgetComponent;
class UCAbilitySystemComponent;
class UCAttributeSet;

UCLASS(Abstract)
class CRUNCH_API ACCharacter : public ACharacter, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	
	ACCharacter();
	
	void ServerSideInit();		//服务器端初始化
	void ClientSideInit();		//客户端初始化
	//判断是否有本地玩家控制
	bool IsLocallyControlledByPlayer() const;
	// 只有服务器调用的方法
	virtual void PossessedBy(AController* NewController) override;
protected:
	
	virtual void BeginPlay() override;
	
	/********************************************************************************/
	/*                             GameplayAbilitySystem                            */
	/********************************************************************************/
public:
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
	
private:
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAbilitySystemComponent> CAbilitySystemComponent;
	
	UPROPERTY(VisibleDefaultsOnly, Category="GamePlay Ability")
	TObjectPtr<UCAttributeSet> CAttributeSet;
	
	/********************************************************************************/
	/*                                       UI                                     */
	/********************************************************************************/
private:
	//控制头顶状态部件
	void ConfigureOverHeadStateWidget();



	//检测状态条可见性
	void UpdateHeadGaugeVisibility();
private:
	UPROPERTY(VisibleDefaultsOnly, Category="UI")
	TObjectPtr<UWidgetComponent> OverHeadWidgetComponent;		//小部件组件

	//计时器频率
	UPROPERTY(EditDefaultsOnly, Category="UI")
	float CheakUpdateRate = 0.2f;

	//状态条可见距离
	UPROPERTY(EditDefaultsOnly, Category="UI")
	float CheakUpdateDistance = 300.f;

	// 距离检测计时器句柄
	FTimerHandle VisibilityCheckTimerHandle;
	
};

Source/Crunch/Private/Character/CCharacter.cpp:

// Copyright@ChenChao


#include "Character/CCharacter.h"

#include "Components/WidgetComponent.h"
#include "GAS/CAbilitySystemComponent.h"
#include "GAS/CAttributeSet.h"
#include "Kismet/GameplayStatics.h"
#include "UI/Widget/OverHeadStateGauge.h"

// Sets default values
ACCharacter::ACCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

	// 网格体无碰撞
	GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	//添加组件
	CAbilitySystemComponent = CreateDefaultSubobject<UCAbilitySystemComponent>(TEXT("CAbilitySystemComponent"));
	CAttributeSet = CreateDefaultSubobject<UCAttributeSet>(TEXT("CAttributeSet"));

	OverHeadWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("OverHeadWidgetComponent"));
	OverHeadWidgetComponent->SetupAttachment(GetRootComponent());
}

void ACCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);
	ServerSideInit();
	
	/*
	// AI控制的角色
	if (NewController && !NewController->IsPlayerController())
	{	
		ServerSideInit();
	}
	*/
	
}

void ACCharacter::ServerSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
	CAbilitySystemComponent->ApplyInitialEffects();		//效果初始化
}

void ACCharacter::ClientSideInit()
{
	CAbilitySystemComponent->InitAbilityActorInfo(this, this);	//应用能力系统组件前都要初始化
}

UAbilitySystemComponent* ACCharacter::GetAbilitySystemComponent() const
{
	return CAbilitySystemComponent;
}

void ACCharacter::ConfigureOverHeadStateWidget()
{
	if (!OverHeadWidgetComponent)
	{
		UE_LOG(LogTemp, Error, TEXT("OverHeadWidgetComponent is NULL"));
		return;
	}
	//如果是本地玩家控制的角色,不显示状态条
	if (IsLocallyControlledByPlayer())
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}
	//得到小部件:头顶属性条
	UOverHeadStateGauge* OverHeadStateGauge = Cast<UOverHeadStateGauge>(OverHeadWidgetComponent->GetUserWidgetObject());
	if (OverHeadStateGauge)
	{
		//调用能力系统组件,并配置它
		OverHeadStateGauge ->ConfigWithASC(GetAbilitySystemComponent());
		OverHeadWidgetComponent->SetHiddenInGame(false);

		GetWorldTimerManager().ClearTimer(VisibilityCheckTimerHandle);
		GetWorldTimerManager().SetTimer(VisibilityCheckTimerHandle, this, &ACCharacter::UpdateHeadGaugeVisibility, CheakUpdateRate, true);
	}
}

bool ACCharacter::IsLocallyControlledByPlayer() const
{
	return GetController()  && GetController()->IsLocalPlayerController();
}

void ACCharacter::UpdateHeadGaugeVisibility()
{
	//测算距离
	APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
	if (!PlayerPawn) return;
	float Distance = FVector::DistSquared(GetActorLocation(), PlayerPawn->GetActorLocation());
	//隐藏状态条
	OverHeadWidgetComponent->SetHiddenInGame(Distance > CheakUpdateDistance*CheakUpdateDistance);
}

void ACCharacter::BeginPlay()
{
	Super::BeginPlay();
	ConfigureOverHeadStateWidget();
}

Source/Crunch/Public/Player/CPlayerController.h:

// Copyright@ChenChao

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "CPlayerController.generated.h"

class ACPlayerCharacter;
class UGameplayWidget;
/**
 * 
 */
UCLASS()
class CRUNCH_API ACPlayerController : public APlayerController
{
	GENERATED_BODY()

public:
	
	/*
	// 只有服务器调用的方法
	virtual void OnPossess(APawn* InPawn) override;
	*/
	
	// 只有客户端(或者监听服务器)调用的方法
	virtual void AcknowledgePossession(class APawn* P) override;
private:
	UPROPERTY()
	TObjectPtr<ACPlayerCharacter> CPlayerCharacter;

	/*************************************************************************************************/
	/*                                             UI                                                */
	/*************************************************************************************************/
private:
	void SpawnGameplayWidget();
	
	UPROPERTY()
	TObjectPtr<UGameplayWidget> GameplayWidget;

	UPROPERTY(EditDefaultsOnly, Category="UI")
	TSubclassOf<UGameplayWidget> GameplayWidgetClass;
};

Source/Crunch/Private/Player/CPlayerController.cpp:

// Copyright@ChenChao


#include "Player/CPlayerController.h"

#include "Blueprint/UserWidget.h"
#include "Player/CPlayerCharacter.h"
#include "UI/Widget/GameplayWidget.h"

/*
void ACPlayerController::OnPossess(APawn* InPawn)
{
	Super::OnPossess(InPawn);
	CPlayerCharacter = Cast<ACPlayerCharacter>(InPawn);
	if (CPlayerCharacter)
	{
		CPlayerCharacter->ServerSideInit();
	}
}
*/

void ACPlayerController::AcknowledgePossession(class APawn* P)
{
	Super::AcknowledgePossession(P);
	CPlayerCharacter = Cast<ACPlayerCharacter>(P);
	if (CPlayerCharacter)
	{
		CPlayerCharacter->ClientSideInit();
		SpawnGameplayWidget();
	}
	
}

void ACPlayerController::SpawnGameplayWidget()
{
	if (!IsLocalPlayerController()) return;

	//创建游戏玩法小部件
	GameplayWidget = CreateWidget<UGameplayWidget>(this, GameplayWidgetClass);
	if (GameplayWidget)
	{
		GameplayWidget->AddToViewport();		//添加到视口
	}
}

效果:

 出现BUG:

当用监听服务器运行游戏时,别的玩家也会隐藏状态条:

实验:

(1) ROLE_SimulatedProxy - 模拟代理,表示该对象在本地是远程对象的模拟副本

if ( GetLocalRole() == ROLE_SimulatedProxy)
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}

效果:

客户端模式:其他的客户端的角色和AI控制的角色都隐藏了

监听服务器模式:其他客户端的角色隐藏了,AI没隐藏

(2) ROLE_AutonomousProxy - 自主代理,表示该对象在本地有自主控制权(如玩家控制的角色)

if ( GetLocalRole() == ROLE_AutonomousProxy )
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}

效果:

客户端:自己控制的角色隐藏了

服务器:别的客户端控制的角色隐藏了

(3) ROLE_Authority - 权威角色,表示该对象在网络中具有最高控制权(通常是服务器)

if ( GetLocalRole() == ROLE_Authority)
	{
		OverHeadWidgetComponent->SetHiddenInGame(true);
		return;
	}

效果:

客户端:没有隐藏的

服务端:全部隐藏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值