(Feat) Customisation de personnage -- donnees et montage
Lot 2 du chantier : le corps du lot precedent devient assemblable. Tete, coiffure, sourcils, barbe et moustache sont montes en leader pose sur le mesh du Character, et les couleurs sont posees sur des MID. FCharacterAppearance tient en 11 octets : des INDEX et jamais des chemins d'assets. Un chemin couterait une chaine a chaque replication, alors qu'un index tient dans un octet et se VALIDE contre le catalogue -- un client ne peut donc pas demander un asset arbitraire. Le corps se monte depuis une LISTE de meshes meme si elle n'a qu'une case. Le pack livre exprès SKM_BodyA_torso, _arms, _legs_01/02/03 pour qu'une armure puisse eteindre les segments qu'elle recouvre : le jour venu la case 0 devient SKM_BodyA_empty et les huit segments suivent, sans que ce code change. Un pointeur unique imposerait de tout reprendre au premier plastron. Trois pieges rencontres, aucun visible depuis le code : - Les UFUNCTION(Exec) d'un UActorComponent ne sont JAMAIS appelees. La chaine de routage (UPlayer::Exec) 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. Et rien ne proteste. D'ou EmberPart / EmberColor / EmberAppearanceDump posees sur AFpsPlayer, qui delegue. - L'ordre des slots de materiau varie d'un mesh a l'autre : sur SKM_HeadA_01 l'Element 0 est celui des YEUX et le 1 celui de la peau. Les six tetes venant de deux packs, on cherche le slot par son NOM et on ne pose rien si aucun ne correspond -- ecraser le slot de la peau repeint le visage entier de la couleur des yeux. - Un nom de parametre materiau se lit dans le Material INSTANCE, pas dans les chaines du master : M_Head_Base contient « Skin Base color » mais le parametre expose s'appelle « Skin ». D'ou des UPROPERTY de catalogue plutot que des constantes, pour corriger sans recompiler. Body B reste a remplir dans DA_CharacterParts, avec son ABP_PlayerBody_B et sans barbes ni moustaches -- le pack n'en livre pas, et une liste vide se comporte deja comme NoPart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "CharacterPartsDataAsset.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Rendue par GetParts quand la categorie n'existe pas, pour que l'appelant
|
||||
* n'ait jamais a tester un pointeur.
|
||||
*
|
||||
* Le nom porte le prefixe du fichier : le build unity concatene les .cpp du
|
||||
* module et un namespace anonyme n'isole plus rien. Une constante nommee
|
||||
* "EmptyParts" tout court finirait par entrer en collision -- invisible en
|
||||
* build incremental, elle ne sortirait qu'au premier -Rebuild.
|
||||
*/
|
||||
const TArray<FCharacterPartEntry> CharacterPartsEmptyList;
|
||||
|
||||
/** Borne un index de piece OBLIGATOIRE : hors bornes, on retombe sur 0. */
|
||||
void ClampRequiredIndex(uint8& Index, int32 Count)
|
||||
{
|
||||
if (Count <= 0 || Index >= Count)
|
||||
{
|
||||
Index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Borne un index de piece FACULTATIVE : hors bornes, on retire la piece. */
|
||||
void ClampOptionalIndex(uint8& Index, int32 Count)
|
||||
{
|
||||
if (Index == FCharacterAppearance::NoPart)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Count <= 0 || Index >= Count)
|
||||
{
|
||||
Index = FCharacterAppearance::NoPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FCharacterBodySet* UCharacterPartsDataAsset::GetBodySet(ECharacterBodyType BodyType) const
|
||||
{
|
||||
const int32 Index = static_cast<int32>(BodyType);
|
||||
return BodySets.IsValidIndex(Index) ? &BodySets[Index] : nullptr;
|
||||
}
|
||||
|
||||
const TArray<FCharacterPartEntry>& UCharacterPartsDataAsset::GetParts(ECharacterBodyType BodyType, ECharacterPartCategory Category) const
|
||||
{
|
||||
const FCharacterBodySet* Set = GetBodySet(BodyType);
|
||||
if (!Set)
|
||||
{
|
||||
return CharacterPartsEmptyList;
|
||||
}
|
||||
|
||||
switch (Category)
|
||||
{
|
||||
case ECharacterPartCategory::Head: return Set->Heads;
|
||||
case ECharacterPartCategory::Hair: return Set->Hairstyles;
|
||||
case ECharacterPartCategory::Eyebrows: return Set->Eyebrows;
|
||||
case ECharacterPartCategory::Beard: return Set->Beards;
|
||||
case ECharacterPartCategory::Mustache: return Set->Mustaches;
|
||||
default: return CharacterPartsEmptyList;
|
||||
}
|
||||
}
|
||||
|
||||
int32 UCharacterPartsDataAsset::GetPartCount(ECharacterBodyType BodyType, ECharacterPartCategory Category) const
|
||||
{
|
||||
return GetParts(BodyType, Category).Num();
|
||||
}
|
||||
|
||||
void UCharacterPartsDataAsset::Sanitize(FCharacterAppearance& InOut) const
|
||||
{
|
||||
// La silhouette d'abord : tout le reste est indexe DANS son jeu de pieces,
|
||||
// donc la borner apres ne servirait a rien.
|
||||
if (static_cast<uint8>(InOut.BodyType) >= BodySets.Num())
|
||||
{
|
||||
InOut.BodyType = ECharacterBodyType::BodyA;
|
||||
}
|
||||
|
||||
ClampRequiredIndex(InOut.HeadIndex, GetPartCount(InOut.BodyType, ECharacterPartCategory::Head));
|
||||
ClampRequiredIndex(InOut.EyebrowsIndex, GetPartCount(InOut.BodyType, ECharacterPartCategory::Eyebrows));
|
||||
|
||||
// Cheveux, barbe et moustache sont facultatifs : un crane rase est un choix,
|
||||
// pas une erreur.
|
||||
ClampOptionalIndex(InOut.HairIndex, GetPartCount(InOut.BodyType, ECharacterPartCategory::Hair));
|
||||
ClampOptionalIndex(InOut.BeardIndex, GetPartCount(InOut.BodyType, ECharacterPartCategory::Beard));
|
||||
ClampOptionalIndex(InOut.MustacheIndex, GetPartCount(InOut.BodyType, ECharacterPartCategory::Mustache));
|
||||
|
||||
ClampRequiredIndex(InOut.EyesIndex, EyeMaterials.Num());
|
||||
|
||||
ClampRequiredIndex(InOut.SkinColorIndex, SkinColors.Num());
|
||||
ClampRequiredIndex(InOut.HairColorIndex, HairColors.Num());
|
||||
ClampRequiredIndex(InOut.EyeColorIndex, EyeColors.Num());
|
||||
ClampRequiredIndex(InOut.UnderwearColorIndex, UnderwearColors.Num());
|
||||
}
|
||||
Reference in New Issue
Block a user