175 lines
7.0 KiB
C++
175 lines
7.0 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "CharacterAppearanceTypes.h"
|
|
#include "CharacterAppearanceComponent.generated.h"
|
|
|
|
class UCharacterPartsDataAsset;
|
|
class USkeletalMeshComponent;
|
|
struct FCharacterBodySet;
|
|
|
|
/** Diffuse a chaque apparence appliquee, pour que l'UI se rafraichisse. */
|
|
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnAppearanceApplied);
|
|
|
|
/**
|
|
* Monte un personnage a partir d'une FCharacterAppearance : assigne les meshes,
|
|
* les met en leader pose, et pose les couleurs sur des materiaux dynamiques.
|
|
*
|
|
* Un systeme = un composant, comme le reste du projet. Mais celui-ci a une
|
|
* particularite qui vaut d'etre tenue : il ne suppose PAS vivre sur le pawn. Il
|
|
* cherche le mesh d'un ACharacter s'il en trouve un, accepte qu'on lui en
|
|
* designe un autre, et fonctionne donc a l'identique sur le mannequin de l'ecran
|
|
* de customisation. C'est ce qui garantit que le personnage montre au joueur et
|
|
* celui que ses amis verront ne peuvent pas diverger : c'est le meme code.
|
|
*
|
|
* Il ne replique rien -- l'apparence vivra sur ASurvivalPlayerState, comme le
|
|
* pseudo, et pour la meme raison : elle doit survivre a la mort du pawn.
|
|
*/
|
|
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
|
|
class EMBERWILD_API UCharacterAppearanceComponent : public UActorComponent
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UCharacterAppearanceComponent();
|
|
|
|
virtual void BeginPlay() override;
|
|
|
|
UPROPERTY(BlueprintAssignable, Category = "Appearance")
|
|
FOnAppearanceApplied OnAppearanceApplied;
|
|
|
|
/** Ton DA_CharacterParts. Sans lui le composant ne monte rien et le dit. */
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Appearance")
|
|
TObjectPtr<UCharacterPartsDataAsset> Catalog;
|
|
|
|
/**
|
|
* L'apparence posee au BeginPlay, tant que rien ne l'a remplacee.
|
|
*
|
|
* Elle sert au mannequin de l'ecran, aux tests en PIE, et de repli si les
|
|
* reglages du joueur sont vides -- c'est-a-dire au tout premier lancement.
|
|
*/
|
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Appearance")
|
|
FCharacterAppearance DefaultAppearance;
|
|
|
|
/**
|
|
* Designe le mesh porteur. A appeler AVANT la premiere application.
|
|
*
|
|
* Inutile sur un ACharacter, dont le mesh est trouve tout seul. Necessaire
|
|
* sur un acteur quelconque -- le mannequin de l'ecran de customisation.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
|
void SetTargetMesh(USkeletalMeshComponent* InTargetMesh);
|
|
|
|
/**
|
|
* Monte l'apparence demandee. Idempotente, et bon marche a la repetition :
|
|
* les meshes ne sont retouches que si une PIECE a change, jamais pour une
|
|
* simple couleur. C'est ce qui permet a un curseur d'appeler cette fonction
|
|
* a chaque image sans engendrer un materiau par frame.
|
|
*/
|
|
UFUNCTION(BlueprintCallable, Category = "Appearance")
|
|
void ApplyAppearance(const FCharacterAppearance& InAppearance);
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Appearance")
|
|
const FCharacterAppearance& GetAppearance() const { return Appearance; }
|
|
|
|
// ------------------------------------------------------------------
|
|
// Reglage par nom -- ce qui permet de tester le montage avant qu'il
|
|
// existe la moindre interface.
|
|
//
|
|
// Ces fonctions ne portent PAS le specificateur Exec, et ce n'est pas un
|
|
// oubli : la chaine de routage des commandes console (voir Player.cpp)
|
|
// interroge le monde, le PlayerInput, le controller, le pawn, le HUD, le
|
|
// GameMode, le CheatManager, le GameState et le camera manager -- jamais
|
|
// les composants, AActor ne surchargeant pas ProcessConsoleExec pour les
|
|
// parcourir. Une UFUNCTION(Exec) posee ici serait silencieusement ignoree.
|
|
// Les commandes vivent donc sur AFpsPlayer, qui delegue ici.
|
|
// ------------------------------------------------------------------
|
|
|
|
/**
|
|
* Cherche, parmi les slots de materiau du mesh de tete, celui dont le nom
|
|
* contient SlotHint. INDEX_NONE si aucun -- l'appelant ne pose alors rien.
|
|
*/
|
|
static int32 FindEyeMaterialSlot(const USkeletalMeshComponent* HeadComponent, FName SlotHint);
|
|
|
|
/** Categories : body, head, hair, eyebrows, beard, mustache, eyes. */
|
|
UFUNCTION(BlueprintCallable, Category = "Appearance|Debug")
|
|
void ApplyPartByName(const FString& Part, int32 Index);
|
|
|
|
/** Categories : skin, hair, eye, underwear. */
|
|
UFUNCTION(BlueprintCallable, Category = "Appearance|Debug")
|
|
void ApplyColorByName(const FString& Part, int32 ColorIndex);
|
|
|
|
/** Ecrit l'apparence courante et le nombre d'options de chaque categorie. */
|
|
UFUNCTION(BlueprintCallable, Category = "Appearance|Debug")
|
|
void LogAppearance() const;
|
|
|
|
protected:
|
|
virtual void OnUnregister() override;
|
|
|
|
private:
|
|
/**
|
|
* Trouve le mesh porteur : celui de l'ACharacter en priorite, sinon le
|
|
* premier SkeletalMesh de l'acteur.
|
|
*
|
|
* L'ordre compte et n'est pas anodin : AFpsPlayer porte AUSSI FirstPersonArms,
|
|
* et prendre « le premier trouve » y monterait la tete du personnage sur les
|
|
* bras vus a l'ecran.
|
|
*/
|
|
USkeletalMeshComponent* ResolveTargetMesh();
|
|
|
|
/** Cree le composant d'une categorie s'il manque, et le rend. */
|
|
USkeletalMeshComponent* EnsurePartComponent(ECharacterPartCategory Category);
|
|
|
|
/** Cree un composant enfant deja attache, leader-pose et configure. */
|
|
USkeletalMeshComponent* CreateFollowerComponent(FName ComponentName);
|
|
|
|
/** Assigne les meshes de corps, l'AnimBP, les pieces et le materiau d'iris. */
|
|
void RebuildMeshes(const FCharacterBodySet& BodySet);
|
|
|
|
/** Pose les quatre couleurs sur les materiaux dynamiques. */
|
|
void ApplyColors(const FCharacterBodySet& BodySet);
|
|
|
|
/** Pose un parametre vectoriel sur TOUS les elements d'un mesh. */
|
|
/**
|
|
* Assigne un mesh a un composant en vidant d'abord ses materiaux surcharges.
|
|
*
|
|
* Indispensable : OverrideMaterials appartient au COMPOSANT, pas au mesh, et
|
|
* survit donc a un changement d'asset. Sans ce vidage, le corps B garderait
|
|
* la peau du corps A.
|
|
*/
|
|
static void SetPartMesh(USkeletalMeshComponent* MeshComponent, USkeletalMesh* NewMesh);
|
|
|
|
static void TintAllElements(USkeletalMeshComponent* MeshComponent, FName Parameter, const FLinearColor& Color);
|
|
|
|
/** Le mesh porteur, leader de tous les autres. */
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<USkeletalMeshComponent> TargetMesh;
|
|
|
|
/**
|
|
* Un composant par categorie, indexe par ECharacterPartCategory.
|
|
*
|
|
* Un tableau plutot qu'un membre nomme par piece : le montage tient alors
|
|
* dans une boucle, et ajouter une categorie ne demande qu'une valeur
|
|
* d'enum. Une case peut rester nulle -- une silhouette sans barbe n'engendre
|
|
* jamais le composant correspondant.
|
|
*/
|
|
UPROPERTY(Transient)
|
|
TArray<TObjectPtr<USkeletalMeshComponent>> PartComponents;
|
|
|
|
/**
|
|
* Les morceaux de corps au-dela du porteur. Vide aujourd'hui : le catalogue
|
|
* n'a qu'un mesh par silhouette. C'est ici qu'atterriront les segments le
|
|
* jour des armures, sans que la logique change.
|
|
*/
|
|
UPROPERTY(Transient)
|
|
TArray<TObjectPtr<USkeletalMeshComponent>> ExtraBodyComponents;
|
|
|
|
FCharacterAppearance Appearance;
|
|
|
|
/** Faux tant qu'aucun montage n'a eu lieu : force la premiere reconstruction. */
|
|
bool bBuilt = false;
|
|
};
|