// 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(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(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(GetUserWidgetObject())) { Nameplate->SetPlayerName(FText::FromString(CurrentName)); LastShownName = CurrentName; } } SetVisibility(true); }