142 lines
4.3 KiB
C++
142 lines
4.3 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "PlayerNameplateComponent.h"
|
|
|
|
#include "Engine/Engine.h"
|
|
#include "Engine/World.h"
|
|
#include "GameFramework/Pawn.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
#include "GameFramework/PlayerState.h"
|
|
#include "PlayerNameplateWidget.h"
|
|
#include "TimerManager.h"
|
|
|
|
UPlayerNameplateComponent::UPlayerNameplateComponent()
|
|
{
|
|
// Espace ecran : l'etiquette fait toujours face a la camera, par
|
|
// construction, et n'est jamais occultee par le decor.
|
|
SetWidgetSpace(EWidgetSpace::Screen);
|
|
|
|
// Le widget se dimensionne sur son contenu : un pseudo court n'a pas a
|
|
// trainer une boite de la largeur du plus long.
|
|
SetDrawAtDesiredSize(true);
|
|
|
|
// Une etiquette ne se vise pas et n'arrete pas un trace : sans ca, elle
|
|
// bloquerait le canal Visibility et masquerait l'interaction du joueur
|
|
// qu'elle surmonte.
|
|
SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
SetGenerateOverlapEvents(false);
|
|
|
|
// Au-dessus de la tete. Reglable dans le Blueprint, la capsule et la taille
|
|
// du personnage pouvant changer.
|
|
SetRelativeLocation(FVector(0.f, 0.f, 100.f));
|
|
|
|
// Cachee au depart : la premiere passe de Refresh decidera. Sans ca, une
|
|
// etiquette sans nom clignote pendant la frame qui precede l'arrivee du
|
|
// PlayerState.
|
|
SetVisibility(false);
|
|
}
|
|
|
|
void UPlayerNameplateComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Un serveur dedie ne rend rien : lui faire calculer des distances pour des
|
|
// etiquettes que personne ne verra serait du gaspillage pur. Sans effet sur
|
|
// notre serveur d'ecoute, qui n'est pas dans ce mode.
|
|
if (GetNetMode() == NM_DedicatedServer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const APawn* OwnerPawn = Cast<APawn>(GetOwner());
|
|
|
|
// Son propre pseudo : on eteint definitivement, sans timer. Ce n'est pas un
|
|
// cas a reevaluer -- un pawn ne change pas de proprietaire en cours de vie,
|
|
// et apres un respawn c'est un NOUVEL acteur qui repassera par ici.
|
|
if (bHideOnOwningPlayer && OwnerPawn && OwnerPawn->IsLocallyControlled())
|
|
{
|
|
SetVisibility(false);
|
|
return;
|
|
}
|
|
|
|
GetWorld()->GetTimerManager().SetTimer(
|
|
RefreshTimer, this, &UPlayerNameplateComponent::Refresh,
|
|
FMath::Max(RefreshInterval, 0.05f), /*bLoop=*/true);
|
|
|
|
// Une premiere passe tout de suite : attendre RefreshInterval laisserait
|
|
// l'etiquette absente un quart de seconde a l'apparition d'un coequipier.
|
|
Refresh();
|
|
}
|
|
|
|
void UPlayerNameplateComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
// Un timer laisse en vie apres la destruction du composant rappellerait une
|
|
// methode sur un objet mort -- typiquement a la mort d'un joueur, ou son
|
|
// pawn est detruit.
|
|
if (UWorld* World = GetWorld())
|
|
{
|
|
World->GetTimerManager().ClearTimer(RefreshTimer);
|
|
}
|
|
|
|
Super::EndPlay(EndPlayReason);
|
|
}
|
|
|
|
APlayerState* UPlayerNameplateComponent::GetOwnerPlayerState() const
|
|
{
|
|
const APawn* OwnerPawn = Cast<APawn>(GetOwner());
|
|
return OwnerPawn ? OwnerPawn->GetPlayerState() : nullptr;
|
|
}
|
|
|
|
void UPlayerNameplateComponent::Refresh()
|
|
{
|
|
// Le point de vue du joueur LOCAL, et non celui du pawn porteur : c'est de
|
|
// notre ecran qu'on parle. GetFirstLocalPlayerController et non
|
|
// GetFirstPlayerController -- en PIE a deux fenetres, le second rendrait le
|
|
// controller de l'autre instance et l'etiquette clignoterait.
|
|
const APlayerController* Viewer = GEngine ? GEngine->GetFirstLocalPlayerController(GetWorld()) : nullptr;
|
|
if (!Viewer || !Viewer->PlayerCameraManager)
|
|
{
|
|
SetVisibility(false);
|
|
return;
|
|
}
|
|
|
|
const float DistanceSquared = FVector::DistSquared(
|
|
Viewer->PlayerCameraManager->GetCameraLocation(),
|
|
GetComponentLocation());
|
|
|
|
if (DistanceSquared > FMath::Square(MaxDrawDistance))
|
|
{
|
|
SetVisibility(false);
|
|
return;
|
|
}
|
|
|
|
// Le PlayerState arrive apres le pawn chez un client : tant qu'il manque,
|
|
// on reste invisible plutot que d'afficher une etiquette vide. La passe
|
|
// suivante le trouvera.
|
|
const APlayerState* State = GetOwnerPlayerState();
|
|
if (!State)
|
|
{
|
|
SetVisibility(false);
|
|
return;
|
|
}
|
|
|
|
const FString CurrentName = State->GetPlayerName();
|
|
if (CurrentName.IsEmpty())
|
|
{
|
|
SetVisibility(false);
|
|
return;
|
|
}
|
|
|
|
if (CurrentName != LastShownName)
|
|
{
|
|
if (UPlayerNameplateWidget* Nameplate = Cast<UPlayerNameplateWidget>(GetUserWidgetObject()))
|
|
{
|
|
Nameplate->SetPlayerName(FText::FromString(CurrentName));
|
|
LastShownName = CurrentName;
|
|
}
|
|
}
|
|
|
|
SetVisibility(true);
|
|
}
|