Files
Unreal_EmberWild/Source/EmberWild/Public/CharacterCustomizationWidget.h

194 lines
6.5 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "CharacterAppearanceTypes.h"
#include "CharacterPreviewActor.h"
#include "CharacterCustomizationWidget.generated.h"
class ACharacterPreviewActor;
class UButton;
class UCharacterPartsDataAsset;
class UCustomizationCategoryWidget;
class UCustomizationOptionWidget;
class UPanelWidget;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationConfirmed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnCustomizationCancelled);
/**
* Ce que le joueur peut regler, dans l'ordre d'affichage de la colonne.
*
* Pieces et couleurs sont dans le MEME enum : ce sont onze rangees de la meme
* colonne, et les distinguer par deux listes obligerait a dessiner deux fois la
* meme chose. La difference ne vit que dans BuildOptions, qui remplit la grille
* de vignettes ou de pastilles selon le cas.
*/
UENUM(BlueprintType)
enum class ECustomizationCategory : uint8
{
Body,
Head,
Hair,
Eyebrows,
Beard,
Mustache,
Eyes,
SkinColor,
HairColor,
EyeColor,
UnderwearColor,
Count UMETA(Hidden)
};
/**
* L'ecran de customisation : la colonne de reglages a gauche, le personnage a
* droite.
*
* Le widget ne monte rien lui-meme -- il pousse une FCharacterAppearance sur
* ACharacterPreviewActor et lit le catalogue pour savoir quoi proposer. C'est
* le meme partage des roles que partout ailleurs dans le projet : l'interface
* affiche et demande, le systeme decide.
*
* Classe Abstract : on cree un WBP_CharacterCustomization qui en herite.
*/
UCLASS(Abstract)
class EMBERWILD_API UCharacterCustomizationWidget : public UUserWidget
{
GENERATED_BODY()
public:
UCharacterCustomizationWidget(const FObjectInitializer& ObjectInitializer);
virtual void NativeOnInitialized() override;
virtual void NativeConstruct() override;
/**
* Echap = Retour, comme le bouton.
*
* Obligatoire ici et pas seulement confortable : dans la map du menu, le
* controller ne binde AUCUNE action d'input. Tout Echap qui fonctionne passe
* donc par le NativeOnKeyDown d'un widget -- il n'y a pas d'autre chemin.
*/
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
/** Le joueur valide : l'ecran a fini, l'apparence est dans GetAppearance(). */
UPROPERTY(BlueprintAssignable, Category = "Customization")
FOnCustomizationConfirmed OnConfirmed;
/** Le joueur renonce : rien n'a ete valide, on rend la main au menu. */
UPROPERTY(BlueprintAssignable, Category = "Customization")
FOnCustomizationCancelled OnCancelled;
/**
* Branche l'ecran sur son mannequin et son point de depart.
*
* A appeler AVANT d'ajouter le widget au viewport : NativeConstruct
* reconstruit les listes, et il les batirait vides sans catalogue.
*
* Surtout PAS nommee Initialize : UUserWidget en a deja une, virtuelle, et
* une surcharge de signature differente la masquerait au lieu de la
* surcharger (C4263/C4264). Meme famille de piege que les membres nommes
* Player ou Slot dans une classe derivee du moteur.
*/
UFUNCTION(BlueprintCallable, Category = "Customization")
void SetupCustomization(ACharacterPreviewActor* InPreview, const FCharacterAppearance& InAppearance);
UFUNCTION(BlueprintPure, Category = "Customization")
const FCharacterAppearance& GetAppearance() const { return Appearance; }
protected:
/**
* Le catalogue. Assigne-lui le meme DA_CharacterParts que BP_FpsPlayer.
*
* Sur le widget et pas pris sur le mannequin : l'ecran doit pouvoir batir
* ses listes meme si le mannequin manque encore, et un catalogue different
* des deux cotes se verrait immediatement a l'ecran.
*/
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
TObjectPtr<UCharacterPartsDataAsset> Catalog;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
TSubclassOf<UCustomizationCategoryWidget> CategoryWidgetClass;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Customization")
TSubclassOf<UCustomizationOptionWidget> OptionWidgetClass;
/** La colonne des categories. Un Vertical Box nomme exactement ainsi. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UPanelWidget> CategoryList;
/** La grille d'options. Un Wrap Box va tres bien. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UPanelWidget> OptionGrid;
/** Valide et lance la partie. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> PlayButton;
/** Retour au menu principal, sans rien valider. */
UPROPERTY(meta = (BindWidget))
TObjectPtr<UButton> BackButton;
// ------------------------------------------------------------------
// Rotation du personnage au clic-glisser
// ------------------------------------------------------------------
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
virtual FReply NativeOnMouseMove(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
private:
/** Reconstruit la colonne de gauche. Une seule fois, les categories sont fixes. */
void BuildCategories();
/** Reconstruit la grille selon la categorie active. */
void BuildOptions();
/** Repose l'apparence sur le mannequin et rafraichit la selection affichee. */
void RefreshPreview();
/** Encadre la case qui porte le choix courant, efface les autres. */
void RefreshSelection();
/** L'index courant dans la categorie active, pour l'encadrement. */
int32 GetCurrentIndexForCategory(ECustomizationCategory Category) const;
/** Ecrit un choix dans l'apparence. Le seul endroit qui la modifie. */
void SetIndexForCategory(ECustomizationCategory Category, int32 NewIndex);
/** Combien d'options a proposer, catalogue en main. */
int32 GetOptionCountForCategory(ECustomizationCategory Category) const;
UFUNCTION()
void HandleCategoryClicked(int32 CategoryIndex);
UFUNCTION()
void HandleOptionClicked(int32 OptionIndex);
UFUNCTION()
void HandlePlayClicked();
UFUNCTION()
void HandleBackClicked();
UPROPERTY(Transient)
TObjectPtr<ACharacterPreviewActor> Preview;
UPROPERTY(Transient)
TArray<TObjectPtr<UCustomizationCategoryWidget>> CategoryWidgets;
UPROPERTY(Transient)
TArray<TObjectPtr<UCustomizationOptionWidget>> OptionWidgets;
FCharacterAppearance Appearance;
ECustomizationCategory ActiveCategory = ECustomizationCategory::Body;
/** Vrai entre l'appui et le relachement, pendant qu'on fait tourner. */
bool bDragging = false;
};