// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Components/WidgetComponent.h" #include "PlayerNameplateComponent.generated.h" class APlayerState; /** * L'etiquette de pseudo posee au-dessus d'un joueur. * * Herite de UWidgetComponent et le configure en espace ECRAN : c'est ce qui la * fait toujours faire face a la camera, gratuitement et exactement, sans le * moindre calcul d'orientation par frame. En espace Monde il faudrait la * reorienter soi-meme a chaque image, et elle serait occultee par le decor -- * un pseudo qui disparait derriere un caillou n'aide personne. * * L'espace ecran n'applique AUCUNE echelle de distance -- verifie dans * SWorldWidgetScreenLayer::Tick, qui ne fait que projeter la position. Le * pseudo garderait donc une taille en pixels constante pendant que le * personnage, lui, retrecit : l'oeil lit ca comme une etiquette qui GROSSIT en * s'eloignant. C'est pourquoi l'echelle est calculee ici, a la main, en 1/d * comme le ferait une vraie perspective. * * L'opacite suit le meme chemin : une etiquette qui s'eteint d'un coup a la * distance limite se remarque bien plus qu'un fondu, alors qu'elle est censee * ne pas se faire remarquer du tout. * * Le composant ne replique RIEN : le pseudo vit deja sur le PlayerState, qui * est replique par le moteur. Ajouter une propriete ici enverrait deux fois la * meme chaine sur le reseau. */ UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent)) class EMBERWILD_API UPlayerNameplateComponent : public UWidgetComponent { GENERATED_BODY() public: UPlayerNameplateComponent(); virtual void BeginPlay() override; /** * Un Tick ici, contrairement au reste du projet. * * La regle "jamais de Tick pour lire un etat" vise les widgets qui * interrogent une valeur au lieu de s'abonner a un delegue. Ce n'est pas le * cas : on interpole en continu deux grandeurs geometriques -- echelle et * opacite -- qui changent a chaque pas du joueur. Sur un timer a 0,25 s * l'etiquette grossirait par paliers visibles. * * Le cout est nul en pratique : UWidgetComponent tick deja en espace ecran * pour se projeter, on ajoute une distance et deux setters. */ virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; protected: /** * Au-dela de cette distance, l'etiquette disparait. 2500 cm = 25 m, soit un * peu plus que la portee a laquelle on distingue un coequipier dans un * decor dense. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate", meta = (ClampMin = "100", Units = "cm")) float MaxDrawDistance = 2500.f; /** * Distance a laquelle l'etiquette est a sa taille de reference, c'est-a-dire * exactement celle dessinee dans le WBP. Plus pres elle grossit, plus loin * elle retrecit -- en 1/d, comme une vraie perspective. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "50", Units = "cm")) float ReferenceDistance = 600.f; /** * Bornes de l'echelle. Le minimum evite un pseudo reduit a une bouillie de * deux pixels ; le maximum evite qu'un coequipier colle a la camera ne * remplisse l'ecran de son nom. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "0.05")) float MinScale = 0.4f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Echelle", meta = (ClampMin = "0.1")) float MaxScale = 1.4f; /** * Longueur du fondu, comptee EN DECA de MaxDrawDistance : l'opacite passe de * 1 a 0 sur cette bande, puis l'etiquette est carrement eteinte. * * Exprimee comme une longueur et non comme une distance de depart : une * distance de depart superieure a MaxDrawDistance serait un reglage invalide * silencieux, alors qu'une longueur se borne toute seule. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate|Fondu", meta = (ClampMin = "0", Units = "cm")) float FadeLength = 700.f; /** * Cacher l'etiquette de son PROPRE personnage. * * Vrai par defaut : en vue premiere personne on ne voit jamais son propre * corps, mais une etiquette en espace ecran, elle, se projetterait en plein * milieu du viseur. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Nameplate") bool bHideOnOwningPlayer = true; private: /** Le PlayerState du pawn porteur, ou nullptr tant qu'il n'est pas arrive. */ APlayerState* GetOwnerPlayerState() const; /** Recopie le pseudo dans le widget, uniquement s'il a change. */ void RefreshName(); /** * Dernier pseudo reellement ecrit dans le widget. * * Le PlayerState arrive APRES le pawn chez un client : la premiere passe * trouve donc un nom vide, et il faut continuer a regarder. Comparer plutot * que reecrire evite de reconstruire la mise en page du Text Block quatre * fois par seconde pour une chaine identique. */ FString LastShownName; };