Import initial du projet Survival (UE 5.8)
Boucle de jeu complète : récolte, inventaire, consommation, stats de survie, mort et respawn. Gameplay en C++, Blueprints réservés au câblage d'assets. Assets binaires (.uasset, .umap, textures, audio) suivis via Git LFS. Binaries/, Intermediate/, Saved/ et DerivedDataCache/ sont ignorés : régénérés au build, ils pèsent 3 Go pour 1,3 Go de contenu utile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "ConfirmDialogWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
class UTextBlock;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogConfirmed);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogCancelled);
|
||||
|
||||
/**
|
||||
* Boite de dialogue "es-tu sur ?", volontairement generique.
|
||||
*
|
||||
* Elle ne sait pas ce qu'elle confirme : elle pose la question et previent via
|
||||
* ses delegues. C'est l'appelant qui decide de l'action. Ainsi la meme boite
|
||||
* servira pour quitter, effacer une sauvegarde ou abandonner une partie.
|
||||
*
|
||||
* Classe Abstract : on cree un WBP_ConfirmDialog qui en herite.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UConfirmDialogWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UConfirmDialogWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Dialogue")
|
||||
FOnConfirmDialogConfirmed OnConfirmed;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Dialogue")
|
||||
FOnConfirmDialogCancelled OnCancelled;
|
||||
|
||||
/**
|
||||
* Affiche la boite et lui donne le focus clavier.
|
||||
*
|
||||
* Passe par ici et jamais par SetVisibility directement : sans le focus,
|
||||
* la boite ne recevrait aucune touche et Echap resterait sans effet.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Dialogue")
|
||||
void Open(const FText& InMessage);
|
||||
|
||||
/** Referme la boite. Ne previent personne : l'appelant sait ce qu'il fait. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Dialogue")
|
||||
void Close();
|
||||
|
||||
protected:
|
||||
/** Echap equivaut a cliquer Annuler. Decoche pour une question incontournable. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue")
|
||||
bool bCancelOnEscape = true;
|
||||
|
||||
/**
|
||||
* Entree equivaut a cliquer Confirmer.
|
||||
*
|
||||
* Desactive par defaut, volontairement : sur une question destructrice, un
|
||||
* joueur qui martele Entree validerait sans avoir lu.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue")
|
||||
bool bConfirmOnEnter = false;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> ConfirmButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> CancelButton;
|
||||
|
||||
/** Optionnel : une boite dont le texte est fige dans le Blueprint reste valide. */
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UTextBlock> MessageText;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleConfirmClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleCancelClicked();
|
||||
};
|
||||
@@ -0,0 +1,395 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "FpsPlayer.generated.h"
|
||||
|
||||
class APickupItem;
|
||||
class UCameraComponent;
|
||||
class UInputAction;
|
||||
class UInputMappingContext;
|
||||
class UInteractionComponent;
|
||||
class UInventoryComponent;
|
||||
class USurvivalStatsComponent;
|
||||
struct FInputActionValue;
|
||||
|
||||
/**
|
||||
* Personnage joueur en vue première personne.
|
||||
* Les assets d'input (Input Mapping Context + Input Actions) se branchent
|
||||
* dans le Blueprint enfant (BP_FpsPlayer), catégorie "Input".
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AFpsPlayer : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this character's properties
|
||||
AFpsPlayer();
|
||||
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
/** Vrai tant que le joueur sprinte (utile pour l'anim ou la stamina plus tard). */
|
||||
UFUNCTION(BlueprintPure, Category = "Mouvement")
|
||||
bool IsSprinting() const { return bIsSprinting; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Camera")
|
||||
UCameraComponent* GetFirstPersonCamera() const { return FirstPersonCamera; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Interaction")
|
||||
UInteractionComponent* GetInteractionComponent() const { return InteractionComponent; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
UInventoryComponent* GetInventoryComponent() const { return InventoryComponent; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
USurvivalStatsComponent* GetSurvivalStats() const { return SurvivalStats; }
|
||||
|
||||
/** Sert a l'UI pour afficher la vraie touche mappee, plutot qu'un "E" ecrit en dur. */
|
||||
UFUNCTION(BlueprintPure, Category = "Input")
|
||||
const UInputAction* GetInteractAction() const { return InteractAction; }
|
||||
|
||||
/**
|
||||
* Coupe les etats maintenus avant de desactiver les inputs du pawn.
|
||||
*
|
||||
* Sprint et saut sont declenches par Started et arretes par Completed.
|
||||
* Si on desactive l'input pendant que la touche est enfoncee, le Completed
|
||||
* n'a plus personne pour l'ecouter et l'etat reste bloque : on rouvrirait
|
||||
* le jeu en sprintant sans toucher a Maj.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Input")
|
||||
void ClearTransientInputStates();
|
||||
|
||||
/**
|
||||
* Retire des objets d'un slot et les fait apparaitre au sol devant le joueur.
|
||||
* @param Quantity nombre d'exemplaires ; passe un grand nombre pour tout jeter.
|
||||
* @return true si quelque chose est reellement sorti de l'inventaire.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool DropSlot(int32 SlotIndex, int32 Quantity);
|
||||
|
||||
/** Vide l'inventaire entier en un tas autour du personnage. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void DropAllItems();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
bool IsDead() const { return bIsDead; }
|
||||
|
||||
/**
|
||||
* Secoue la caméra verticalement. Strength est la vitesse initiale en cm/s
|
||||
* donnée à la caméra ; positif = vers le haut, négatif = vers le bas.
|
||||
* Réutilisable pour une explosion, un recul d'arme, un coup reçu.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Camera")
|
||||
void AddCameraPunch(float Strength);
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
// Appelés par le CharacterMovement quand la capsule change de taille (accroupi)
|
||||
virtual void OnStartCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
|
||||
virtual void OnEndCrouch(float HalfHeightAdjust, float ScaledHalfHeightAdjust) override;
|
||||
|
||||
// Réception au sol et décollage
|
||||
virtual void Landed(const FHitResult& Hit) override;
|
||||
virtual void OnJumped_Implementation() override;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Composants
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Caméra placée à hauteur des yeux, suit la rotation du controller. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
|
||||
TObjectPtr<UCameraComponent> FirstPersonCamera;
|
||||
|
||||
/** Détecte et déclenche les interactions avec le monde. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Interaction")
|
||||
TObjectPtr<UInteractionComponent> InteractionComponent;
|
||||
|
||||
/**
|
||||
* Inventaire du joueur. Sur le pawn et non sur le controller :
|
||||
* un cadavre qui garde son butin fait partie du gameplay de survie.
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
TObjectPtr<UInventoryComponent> InventoryComponent;
|
||||
|
||||
/** Vie, faim, soif. Sur le pawn : c'est ce corps qui a faim. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Survie")
|
||||
TObjectPtr<USurvivalStatsComponent> SurvivalStats;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Enhanced Input : assets à assigner dans le Blueprint
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Contexte ajouté au joueur au BeginPlay (IMC_Default). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputMappingContext> DefaultMappingContext;
|
||||
|
||||
/** Déplacement, valeur Axis2D (X = droite/gauche, Y = avant/arrière). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> MoveAction;
|
||||
|
||||
/** Regard souris / stick droit, valeur Axis2D. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> LookAction;
|
||||
|
||||
/** Saut, valeur Digital (bool). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> JumpAction;
|
||||
|
||||
/** Sprint, valeur Digital (bool) — maintenu. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> SprintAction;
|
||||
|
||||
/** Accroupi, valeur Digital (bool) — maintenu. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> CrouchAction;
|
||||
|
||||
/** Interaction, valeur Digital (bool). */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> InteractAction;
|
||||
|
||||
/**
|
||||
* Utilise l'objet actif de la barre rapide, valeur Digital (bool).
|
||||
* A mapper sur le clic gauche.
|
||||
*
|
||||
* Bindee sur le PAWN volontairement : quand l'inventaire est ouvert, les
|
||||
* inputs du pawn sont coupes, donc le clic gauche sert au glisser-deposer
|
||||
* de l'UI sans qu'on ait le moindre test a ecrire.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> UseItemAction;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : mouvement
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Vitesse de marche normale (cm/s). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
|
||||
float WalkSpeed = 400.f;
|
||||
|
||||
/** Vitesse en sprint (cm/s). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
|
||||
float SprintSpeed = 650.f;
|
||||
|
||||
/** Vitesse accroupi (cm/s). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mouvement", meta = (ClampMin = "0"))
|
||||
float CrouchSpeed = 200.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : objets jetés au sol
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Classe engendrée quand on jette un objet. Assigne ton BP_Pickup_Test :
|
||||
* son ItemData est écrasé au spawn, un seul Blueprint suffit pour tout.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inventaire|Drop")
|
||||
TSubclassOf<APickupItem> DroppedPickupClass;
|
||||
|
||||
/** Distance devant les yeux où l'objet est déposé (cm). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire|Drop", meta = (ClampMin = "10"))
|
||||
float DropDistance = 120.f;
|
||||
|
||||
/** Hauteur au-dessus du sol trouvé, pour éviter que l'objet s'y encastre. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire|Drop")
|
||||
float DropGroundOffset = 5.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : regard
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Sensibilité horizontale : degrés de rotation par unité de souris.
|
||||
* 1.0 = brut (beaucoup trop rapide), ~0.4 est un bon point de départ.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.01"))
|
||||
float LookSensitivityX = 0.4f;
|
||||
|
||||
/** Sensibilité verticale. Souvent réglée un peu plus basse que l'horizontale. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera", meta = (ClampMin = "0.01"))
|
||||
float LookSensitivityY = 0.4f;
|
||||
|
||||
/** Inverse l'axe vertical de la souris. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
|
||||
bool bInvertLookY = false;
|
||||
|
||||
/**
|
||||
* Hauteur de l'oeil AU-DESSUS DES PIEDS, debout (cm).
|
||||
* 160 cm correspond a un personnage d'environ 1m76.
|
||||
* Mesure depuis le sol et non depuis la capsule : c'est ce qui permet
|
||||
* d'annuler le saut instantane de la capsule quand elle retrecit.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
|
||||
float EyeHeightStanding = 160.f;
|
||||
|
||||
/** Hauteur de l'oeil au-dessus des pieds, accroupi (cm). */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
|
||||
float EyeHeightCrouched = 104.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : animation d'accroupissement
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Durée en secondes de la transition debout <-> accroupi. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi", meta = (ClampMin = "0.01"))
|
||||
float CrouchTransitionDuration = 0.4f;
|
||||
|
||||
/** Tangage (degrés) au pic de la transition. Positif = le regard plonge. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi")
|
||||
float CrouchTiltPitch = 3.f;
|
||||
|
||||
/**
|
||||
* Roulis (degrés) au pic de la transition : bascule sur le côté.
|
||||
* A 0 par defaut, le mouvement reste purement vertical.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Accroupi")
|
||||
float CrouchTiltRoll = 0.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : impacts caméra (saut / atterrissage)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fréquence du ressort de caméra en Hz : à quelle vitesse elle revient
|
||||
* en place après un choc. Plus haut = plus sec.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0.1"))
|
||||
float PunchFrequency = 3.f;
|
||||
|
||||
/**
|
||||
* Amortissement du ressort. 1.0 = retour net sans rebond,
|
||||
* 0.5 = un rebond visible, en dessous ça oscille longtemps.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0.05", ClampMax = "2.0"))
|
||||
float PunchDamping = 0.85f;
|
||||
|
||||
/**
|
||||
* Durée (s) sur laquelle une impulsion est injectée dans le ressort.
|
||||
* A 0 elle est appliquée d'un coup : la caméra part à pleine vitesse en
|
||||
* une frame, ce qui se lit comme un a-coup. Quelques centiemes suffisent
|
||||
* a rendre l'entree en mouvement progressive.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
|
||||
float PunchImpulseRampTime = 0.06f;
|
||||
|
||||
/** Degrés de tangage ajoutés par cm de déplacement vertical de la caméra. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact")
|
||||
float PunchPitchPerCm = 0.35f;
|
||||
|
||||
/** Amplitude maximale du déplacement vertical (cm), garde-fou. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
|
||||
float PunchMaxOffset = 50.f;
|
||||
|
||||
/**
|
||||
* Impulsion (cm/s) à l'atterrissage, quand l'impact est au maximum.
|
||||
* Ordre de grandeur : le deplacement en cm de la camera vaut environ
|
||||
* cette valeur x 0.018 (avec les reglages de ressort par defaut).
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
|
||||
float LandPunchStrength = 2000.f;
|
||||
|
||||
/** En dessous de cette vitesse de chute (cm/s), aucun effet à l'atterrissage. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "0"))
|
||||
float LandPunchMinFallSpeed = 150.f;
|
||||
|
||||
/**
|
||||
* Vitesse de chute (cm/s) à partir de laquelle l'effet est au maximum.
|
||||
* Un saut sur place retombe a peu pres a JumpZVelocity (450), donc cette
|
||||
* valeur doit rester proche pour qu'un saut normal se sente deja bien.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact", meta = (ClampMin = "1"))
|
||||
float LandPunchMaxFallSpeed = 900.f;
|
||||
|
||||
/**
|
||||
* Impulsion (cm/s) au décollage du saut. Positif = la caméra s'enfonce
|
||||
* un instant (compression des jambes) ; mets une valeur négative pour
|
||||
* qu'elle parte vers le haut à la place.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|Impact")
|
||||
float JumpPunchStrength = 550.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Réglages : champ de vision
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** FOV au repos, en degrés. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV", meta = (ClampMin = "30", ClampMax = "150"))
|
||||
float BaseFOV = 90.f;
|
||||
|
||||
/** Degrés de FOV ajoutés en sprint. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV")
|
||||
float SprintFOVOffset = 8.f;
|
||||
|
||||
/** Vitesse de transition du FOV. Plus haut = plus brusque. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera|FOV", meta = (ClampMin = "0.1"))
|
||||
float FOVInterpSpeed = 8.f;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Handlers d'input
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
void Move(const FInputActionValue& Value);
|
||||
void Look(const FInputActionValue& Value);
|
||||
void StartSprint();
|
||||
void StopSprint();
|
||||
void StartCrouch();
|
||||
void StopCrouch();
|
||||
void Interact();
|
||||
|
||||
/**
|
||||
* Clic gauche : agit avec l'objet actif de la barre rapide.
|
||||
* Aujourd'hui seuls les consommables font quelque chose ; c'est ici que
|
||||
* viendront les outils et les armes.
|
||||
*/
|
||||
void UseSelectedItem();
|
||||
|
||||
private:
|
||||
/** Fait avancer toutes les animations de caméra et les applique. */
|
||||
void UpdateCameraEffects(float DeltaTime);
|
||||
|
||||
/** Branche sur OnDied du composant de survie. */
|
||||
UFUNCTION()
|
||||
void HandleDeath();
|
||||
|
||||
/** Fait apparaitre un pickup deja configure a un endroit donne. */
|
||||
APickupItem* SpawnPickup(const struct FInventorySlot& Slot, const FVector& Location);
|
||||
|
||||
/** Descend une position jusqu'au sol, pour ne pas laisser d'objet en l'air. */
|
||||
FVector PlaceOnGround(const FVector& Start) const;
|
||||
|
||||
bool bIsDead = false;
|
||||
|
||||
/** Progression de l'animation d'accroupissement : 0 = debout, 1 = accroupi. */
|
||||
float CrouchAlpha = 0.f;
|
||||
|
||||
/** Valeur visée par CrouchAlpha. */
|
||||
float CrouchTargetAlpha = 0.f;
|
||||
|
||||
/** +1 quand on s'accroupit, -1 quand on se relève : inverse le sens du tilt. */
|
||||
float CrouchTiltDirection = 1.f;
|
||||
|
||||
/** Décalage vertical courant de la caméra (cm), état du ressort d'impact. */
|
||||
float PunchOffset = 0.f;
|
||||
|
||||
/** Vitesse du ressort d'impact (cm/s). */
|
||||
float PunchVelocity = 0.f;
|
||||
|
||||
/** Impulsion restant à injecter dans le ressort, étalée sur PunchImpulseRampTime. */
|
||||
float PendingPunch = 0.f;
|
||||
|
||||
/** Vitesse verticale mémorisée pendant la chute, pour calibrer l'atterrissage. */
|
||||
float LastFallSpeed = 0.f;
|
||||
|
||||
bool bIsSprinting = false;
|
||||
|
||||
/** Évite de spammer le log si IA_Look est mal configurée. */
|
||||
bool bLoggedLookTypeWarning = false;
|
||||
};
|
||||
@@ -0,0 +1,169 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "FpsPlayerController.generated.h"
|
||||
|
||||
class UHotbarWidget;
|
||||
class UInputAction;
|
||||
class UInventoryComponent;
|
||||
class UInteractionPromptWidget;
|
||||
class UInventoryWidget;
|
||||
class UScreenFadeComponent;
|
||||
class USurvivalStatsWidget;
|
||||
class UUserWidget;
|
||||
struct FInputActionValue;
|
||||
|
||||
/**
|
||||
* Controller du joueur. C'est ici que vit l'UI, et pas dans le personnage :
|
||||
* le controller survit a la mort du pawn, alors que le personnage non.
|
||||
* Si tu creais le HUD dans AFpsPlayer, il disparaitrait a chaque respawn.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AFpsPlayerController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AFpsPlayerController();
|
||||
|
||||
/** Ouvre ou ferme l'inventaire. */
|
||||
UFUNCTION(BlueprintCallable, Category = "UI")
|
||||
void ToggleInventory();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "UI")
|
||||
bool IsInventoryOpen() const { return bInventoryOpen; }
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void OnPossess(APawn* InPawn) override;
|
||||
virtual void SetupInputComponent() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Fondus
|
||||
//
|
||||
// La mécanique elle-même vit dans UScreenFadeComponent, partagé avec le
|
||||
// menu principal. Ici on ne garde que les durées propres au jeu.
|
||||
// La couleur et le WBP_Fade se règlent sur le composant.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Le voile. Assigne-lui ton WBP_Fade dans BP_FpsPlayerController. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI|Fondu")
|
||||
TObjectPtr<UScreenFadeComponent> ScreenFade;
|
||||
|
||||
/** Décoche pour démarrer sans fondu, pratique pendant les tests. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Fondu")
|
||||
bool bFadeInOnStart = true;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Fondu", meta = (ClampMin = "0.1"))
|
||||
float StartupFadeDuration = 1.5f;
|
||||
|
||||
/** Durée du fondu au noir quand le joueur meurt. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Fondu", meta = (ClampMin = "0.1"))
|
||||
float DeathFadeDuration = 1.f;
|
||||
|
||||
/**
|
||||
* Temps total entre la mort et la réapparition.
|
||||
* Doit rester supérieur à DeathFadeDuration, sinon le joueur réapparaît
|
||||
* avant que l'écran soit entièrement noir.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI|Fondu", meta = (ClampMin = "0.1"))
|
||||
float RespawnDelay = 3.f;
|
||||
|
||||
/**
|
||||
* Ouverture de l'inventaire, valeur Digital (bool).
|
||||
* L'input d'interface vit sur le controller et non sur le pawn : ouvrir
|
||||
* son sac doit rester possible meme sans corps a piloter.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> ToggleInventoryAction;
|
||||
|
||||
/**
|
||||
* Jette l'objet survole, valeur Digital (bool). Ctrl enfonce = un seul
|
||||
* exemplaire au lieu de la pile entiere.
|
||||
* Sur le controller aussi : le pawn a ses inputs coupes quand l'inventaire
|
||||
* est ouvert, donc un binding sur le pawn ne repondrait jamais.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> DropItemAction;
|
||||
|
||||
/**
|
||||
* Selection directe d'un slot de la barre rapide, valeur Axis1D.
|
||||
*
|
||||
* Une seule action pour toutes les touches : dans IMC_Default, mappe les
|
||||
* touches 1 a 6 sur cette action et donne a chacune un modificateur
|
||||
* Scalar different (1, 2, 3...). Le handler lit la valeur et en deduit
|
||||
* le slot. Six actions separees seraient six assets a maintenir.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> HotbarSelectAction;
|
||||
|
||||
/** Molette de souris pour defiler dans la barre, valeur Axis1D. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> HotbarScrollAction;
|
||||
|
||||
/**
|
||||
* Viseur, affiche en permanence. Widget separe du prompt : ce dernier se
|
||||
* met en Collapsed des qu'on ne vise plus rien, et emporterait le viseur
|
||||
* avec lui s'ils partageaient le meme widget.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UUserWidget> CrosshairClass;
|
||||
|
||||
/** A assigner dans BP_FpsPlayerController : ton WBP_InteractionPrompt. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UInteractionPromptWidget> InteractionPromptClass;
|
||||
|
||||
/** Ton WBP_Inventory. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UInventoryWidget> InventoryClass;
|
||||
|
||||
/** Ton WBP_Hotbar, affiche en permanence. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<UHotbarWidget> HotbarClass;
|
||||
|
||||
/** Ton WBP_SurvivalStats : les trois jauges. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "UI")
|
||||
TSubclassOf<USurvivalStatsWidget> SurvivalStatsClass;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
|
||||
TObjectPtr<UUserWidget> Crosshair;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
|
||||
TObjectPtr<UInteractionPromptWidget> InteractionPrompt;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
|
||||
TObjectPtr<UInventoryWidget> InventoryWidget;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
|
||||
TObjectPtr<UHotbarWidget> Hotbar;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "UI")
|
||||
TObjectPtr<USurvivalStatsWidget> SurvivalStatsWidget;
|
||||
|
||||
private:
|
||||
void CreateHudWidgets();
|
||||
|
||||
/** Jette la pile survolee dans la grille, si l'inventaire est ouvert. */
|
||||
void DropHoveredItem();
|
||||
|
||||
void HandleHotbarSelect(const FInputActionValue& Value);
|
||||
void HandleHotbarScroll(const FInputActionValue& Value);
|
||||
|
||||
/** Raccourci vers l'inventaire du pawn possede. */
|
||||
UInventoryComponent* GetPawnInventory() const;
|
||||
|
||||
/** Branche sur OnDied du pawn possede. */
|
||||
UFUNCTION()
|
||||
void HandlePawnDied();
|
||||
|
||||
/** Detruit le cadavre et demande un nouveau pawn au GameMode. */
|
||||
void RespawnPlayer();
|
||||
|
||||
FTimerHandle RespawnTimerHandle;
|
||||
|
||||
bool bInventoryOpen = false;
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "HotbarWidget.generated.h"
|
||||
|
||||
class UHorizontalBox;
|
||||
class UInventoryComponent;
|
||||
class UInventorySlotWidget;
|
||||
|
||||
/**
|
||||
* Barre rapide en bas de l'ecran, affichee en permanence.
|
||||
*
|
||||
* Elle ne possede aucune donnee : elle montre les N premiers slots de
|
||||
* l'inventaire. Deplacer un objet de la grille vers la barre, c'est
|
||||
* simplement le deplacer vers un slot d'index bas -- le glisser-deposer
|
||||
* existant s'en occupe deja sans aucun code supplementaire.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UHotbarWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** Remet a jour contenu et selection. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void Refresh();
|
||||
|
||||
/** (Re)branche sur l'inventaire du pawn possede. Voir UInventoryWidget. */
|
||||
void BindToOwningPawn();
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "SlotContainer" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UHorizontalBox> SlotContainer;
|
||||
|
||||
/** Le meme WBP_InventorySlot que la grille : une seule apparence a maintenir. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
TSubclassOf<UInventorySlotWidget> SlotWidgetClass;
|
||||
|
||||
/** Espacement entre les cases. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire", meta = (ClampMin = "0"))
|
||||
float SlotPadding = 4.f;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleInventoryChanged();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleSelectionChanged(int32 NewIndex);
|
||||
|
||||
void RebuildSlots(int32 DesiredCount);
|
||||
|
||||
UInventoryComponent* ResolveInventory() const;
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundInventory;
|
||||
|
||||
/** UPROPERTY obligatoire : sans ca le GC collecterait les cases. */
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UInventorySlotWidget>> SlotWidgets;
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "Interactable.generated.h"
|
||||
|
||||
/**
|
||||
* Classe technique requise par Unreal. On ne la manipule jamais directement,
|
||||
* elle sert uniquement au systeme de reflection : c'est elle qu'on passe a
|
||||
* Implements<UInteractable>() et a Execute_XXX().
|
||||
*/
|
||||
UINTERFACE(MinimalAPI, BlueprintType)
|
||||
class UInteractable : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
* A implementer par tout ce avec quoi le joueur peut interagir : objets a
|
||||
* ramasser, portes, coffres, etablis, plantes a recolter.
|
||||
*
|
||||
* Les fonctions sont des BlueprintNativeEvent : une classe C++ surcharge
|
||||
* XXX_Implementation(), un Blueprint surcharge l'evenement du meme nom.
|
||||
* Les deux fonctionnent, et l'appelant n'a pas a savoir lequel des deux
|
||||
* il a en face -- c'est tout l'interet de passer par une interface.
|
||||
*
|
||||
* IMPORTANT : pour appeler ces fonctions depuis l'exterieur, utilise
|
||||
* IInteractable::Execute_Interact(Actor, Interactor) et jamais
|
||||
* Actor->Interact() directement. Seul le wrapper Execute_ sait router
|
||||
* l'appel vers une implementation Blueprint.
|
||||
*/
|
||||
class SURVIVAL_PROJET_API IInteractable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Texte affiche au joueur quand il vise l'objet ("Ramasser", "Ouvrir"...).
|
||||
* Renvoie du FText et non du FString : c'est le type localisable d'Unreal,
|
||||
* indispensable si tu traduis le jeu un jour.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
|
||||
FText GetInteractionPrompt() const;
|
||||
virtual FText GetInteractionPrompt_Implementation() const { return FText::GetEmpty(); }
|
||||
|
||||
/**
|
||||
* Permet de refuser l'interaction selon le contexte : porte verrouillee,
|
||||
* inventaire plein, competence manquante. Renvoyer false empeche l'objet
|
||||
* d'etre meme selectionne par le composant d'interaction.
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
|
||||
bool CanInteract(AActor* Interactor) const;
|
||||
virtual bool CanInteract_Implementation(AActor* Interactor) const { return true; }
|
||||
|
||||
/** L'interaction elle-meme. Interactor est le pawn du joueur. */
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
|
||||
void Interact(AActor* Interactor);
|
||||
virtual void Interact_Implementation(AActor* Interactor) {}
|
||||
|
||||
/** Le joueur commence a viser l'objet : surbrillance, son, animation. */
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
|
||||
void OnBeginFocus();
|
||||
virtual void OnBeginFocus_Implementation() {}
|
||||
|
||||
/** Le joueur ne vise plus l'objet. */
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Interaction")
|
||||
void OnEndFocus();
|
||||
virtual void OnEndFocus_Implementation() {}
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "InteractionComponent.generated.h"
|
||||
|
||||
/**
|
||||
* Diffuse a chaque changement de cible visee. NewFocus vaut nullptr quand le
|
||||
* joueur ne vise plus rien : c'est le signal pour cacher le prompt a l'ecran.
|
||||
*/
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnInteractionFocusChanged, AActor*, NewFocus, AActor*, OldFocus);
|
||||
|
||||
/**
|
||||
* A poser sur le pawn du joueur. Lance regulierement un trace depuis le point
|
||||
* de vue de la camera, garde en memoire l'objet IInteractable vise, et declenche
|
||||
* l'interaction a la demande.
|
||||
*
|
||||
* Le composant ne connait aucun type d'objet concret : il ne parle qu'a
|
||||
* l'interface. Tu peux ajouter des portes, des coffres ou des PNJ sans jamais
|
||||
* revenir toucher a ce fichier.
|
||||
*/
|
||||
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
|
||||
class SURVIVAL_PROJET_API UInteractionComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInteractionComponent();
|
||||
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/** Declenche l'interaction sur l'objet vise, s'il y en a un. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Interaction")
|
||||
void TryInteract();
|
||||
|
||||
/** L'acteur actuellement vise, ou nullptr. */
|
||||
UFUNCTION(BlueprintPure, Category = "Interaction")
|
||||
AActor* GetFocusedActor() const { return FocusedActor.Get(); }
|
||||
|
||||
/** Texte a afficher a l'ecran, vide si rien n'est vise. */
|
||||
UFUNCTION(BlueprintPure, Category = "Interaction")
|
||||
FText GetFocusedPrompt() const;
|
||||
|
||||
/** Branche ton widget la-dessus pour afficher / cacher le prompt. */
|
||||
UPROPERTY(BlueprintAssignable, Category = "Interaction")
|
||||
FOnInteractionFocusChanged OnFocusChanged;
|
||||
|
||||
protected:
|
||||
/** Portee du trace en cm. 250 = un peu plus d'un bras et demi. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction", meta = (ClampMin = "1"))
|
||||
float InteractionRange = 250.f;
|
||||
|
||||
/**
|
||||
* Rayon de la sphere balayee. Un trace en ligne pure oblige a viser au pixel
|
||||
* pres et rend le jeu penible ; quelques cm de tolerance changent tout.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction", meta = (ClampMin = "0"))
|
||||
float TraceRadius = 8.f;
|
||||
|
||||
/**
|
||||
* Intervalle entre deux traces, en secondes. Inutile de tracer 120 fois par
|
||||
* seconde pour un affichage que l'oeil lit a 20 Hz.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction", meta = (ClampMin = "0"))
|
||||
float TraceInterval = 0.05f;
|
||||
|
||||
/** Canal de collision teste. Visibility convient dans la majorite des cas. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction")
|
||||
TEnumAsByte<ECollisionChannel> TraceChannel = ECC_Visibility;
|
||||
|
||||
/** Affiche le trace dans le monde, tres utile quand rien ne repond. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction")
|
||||
bool bDrawDebug = false;
|
||||
|
||||
private:
|
||||
/** Relance le trace et met a jour FocusedActor. */
|
||||
void UpdateFocus();
|
||||
|
||||
/** Vrai si l'acteur implemente l'interface et accepte l'interaction. */
|
||||
bool IsValidInteractable(AActor* Actor) const;
|
||||
|
||||
/** Change la cible en notifiant l'ancienne et la nouvelle. */
|
||||
void SetFocusedActor(AActor* NewFocus);
|
||||
|
||||
/** Point de vue reel du joueur, camera comprise. */
|
||||
bool GetViewPoint(FVector& OutLocation, FRotator& OutRotation) const;
|
||||
|
||||
/** Cible courante. TWeakObjectPtr : si l'objet est detruit, on ne garde pas un pointeur mort. */
|
||||
TWeakObjectPtr<AActor> FocusedActor;
|
||||
|
||||
/**
|
||||
* Vrai si on avait une cible au dernier changement diffuse.
|
||||
* Indispensable : quand la cible se detruit (pickup ramasse), FocusedActor
|
||||
* se vide tout seul, et une simple comparaison de pointeurs ne verrait
|
||||
* jamais la transition "j'avais une cible" -> "je n'en ai plus".
|
||||
*/
|
||||
bool bHasFocusedActor = false;
|
||||
|
||||
float TimeSinceLastTrace = 0.f;
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InteractionPromptWidget.generated.h"
|
||||
|
||||
class UInteractionComponent;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Affiche "Appuyez sur E pour Ramasser Objet" quand le joueur vise quelque
|
||||
* chose d'interactif, et se cache sinon.
|
||||
*
|
||||
* La classe est Abstract : on ne l'instancie jamais directement, on cree un
|
||||
* Widget Blueprint (WBP_InteractionPrompt) qui en herite. Le C++ porte la
|
||||
* logique, le Blueprint porte la mise en page. C'est la separation standard
|
||||
* en UMG, l'equivalent d'un prefab d'UI avec son script en Unity.
|
||||
*
|
||||
* Le widget ne fait AUCUN travail par frame : il s'abonne au delegue
|
||||
* OnFocusChanged du composant d'interaction et ne se met a jour que lorsque
|
||||
* la cible change reellement.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UInteractionPromptWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** (Re)branche sur le composant d'interaction du pawn possede. */
|
||||
void BindToOwningPawn();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* meta = (BindWidget) : Unreal relie automatiquement ce pointeur au widget
|
||||
* nomme exactement "PromptText" dans le Widget Blueprint enfant. Si tu
|
||||
* oublies de le creer ou que tu le renommes, le Blueprint refuse de
|
||||
* compiler avec un message clair -- pas de crash silencieux au runtime.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> PromptText;
|
||||
|
||||
/**
|
||||
* Texte affiche. {0} sera remplace par la touche, {1} par l'action.
|
||||
* Editable dans le Widget Blueprint pour ajuster la formulation.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction")
|
||||
FText PromptFormat;
|
||||
|
||||
UInteractionPromptWidget(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
private:
|
||||
/** Appele par le composant d'interaction a chaque changement de cible. */
|
||||
UFUNCTION()
|
||||
void HandleFocusChanged(AActor* NewFocus, AActor* OldFocus);
|
||||
|
||||
/** Recalcule le texte et la visibilite. */
|
||||
void Refresh();
|
||||
|
||||
/** Nom lisible de la touche actuellement mappee sur l'action Interagir. */
|
||||
FText GetInteractKeyDisplayName() const;
|
||||
|
||||
/** Le composant auquel on est abonne, pour pouvoir se desabonner proprement. */
|
||||
TWeakObjectPtr<UInteractionComponent> BoundComponent;
|
||||
};
|
||||
@@ -0,0 +1,230 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "InventoryComponent.generated.h"
|
||||
|
||||
class UItemDataAsset;
|
||||
|
||||
/**
|
||||
* Contenu d'un emplacement. Item nul = slot vide.
|
||||
* Le tableau de slots a toujours exactement la taille de l'inventaire :
|
||||
* l'index dans le tableau EST la position dans la grille, ce qui rend
|
||||
* l'affichage et le futur drag & drop triviaux.
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FInventorySlot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire")
|
||||
TObjectPtr<UItemDataAsset> Item = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire")
|
||||
int32 Quantity = 0;
|
||||
|
||||
/**
|
||||
* Charges restantes de l'exemplaire courant, pour les objets a usages
|
||||
* multiples. Etat par instance, d'ou sa place ici et pas sur le DataAsset :
|
||||
* une canette entamee ne doit pas entamer toutes les canettes du jeu.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire")
|
||||
int32 RemainingUses = 0;
|
||||
|
||||
bool IsEmpty() const { return Item == nullptr || Quantity <= 0; }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Item = nullptr;
|
||||
Quantity = 0;
|
||||
RemainingUses = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/** Diffuse a chaque modification du contenu ou de la taille. L'UI s'y abonne. */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnInventoryChanged);
|
||||
|
||||
/** Diffuse quand le joueur change de slot actif dans la barre rapide. */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSelectedHotbarSlotChanged, int32, NewIndex);
|
||||
|
||||
/**
|
||||
* Inventaire en grille a slots, facon Raft / Valheim.
|
||||
*
|
||||
* Le nombre de slots est dynamique : BaseSlotCount fixe le minimum, et
|
||||
* BonusSlotCount permet a un sac equipe de l'agrandir. Retirer le sac
|
||||
* refuse si les objets ne rentrent plus, plutot que de les faire
|
||||
* disparaitre en silence.
|
||||
*/
|
||||
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
|
||||
class SURVIVAL_PROJET_API UInventoryComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UInventoryComponent();
|
||||
|
||||
virtual void InitializeComponent() override;
|
||||
|
||||
/**
|
||||
* Ajoute des objets, en remplissant d'abord les piles entamees.
|
||||
* @param RemainingUses charges de l'exemplaire ajoute ; -1 signifie "plein".
|
||||
* Sert a rendre son etat a un objet ramasse au sol.
|
||||
* @return le nombre d'exemplaires qui N'ONT PAS pu rentrer (0 = tout est passe).
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
int32 AddItem(UItemDataAsset* Item, int32 Quantity = 1, int32 RemainingUses = -1);
|
||||
|
||||
/**
|
||||
* Consomme une utilisation du slot.
|
||||
* Retire un exemplaire quand il ne reste plus de charge.
|
||||
* @return true si quelque chose a ete consomme.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool ConsumeUse(int32 SlotIndex);
|
||||
|
||||
/**
|
||||
* Retire des objets, en vidant d'abord les piles les plus petites.
|
||||
* @return le nombre d'exemplaires reellement retires.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
int32 RemoveItem(UItemDataAsset* Item, int32 Quantity = 1);
|
||||
|
||||
/** Combien d'exemplaires de cet objet, toutes piles confondues. */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetItemCount(const UItemDataAsset* Item) const;
|
||||
|
||||
/** Combien d'exemplaires pourraient encore rentrer, sans rien modifier. */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetRoomFor(const UItemDataAsset* Item) const;
|
||||
|
||||
/**
|
||||
* Un seul tableau, deux conteneurs distincts :
|
||||
* index 0 .. HotbarSlotCount-1 -> barre rapide
|
||||
* index HotbarSlotCount .. fin -> grille d'inventaire (+ bonus de sac)
|
||||
*
|
||||
* Un objet range dans la barre n'apparait PAS dans la grille, et
|
||||
* inversement. Ils partagent le meme tableau uniquement pour que le
|
||||
* glisser-deposer entre les deux ne demande aucune conversion d'index.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetTotalSlotCount() const { return FMath::Max(0, HotbarSlotCount + BackpackSlotCount + BonusSlotCount); }
|
||||
|
||||
/** Premier index affiche par la grille, juste apres la barre rapide. */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetBackpackStartIndex() const { return GetHotbarSlotCount(); }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetUsedSlotCount() const;
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
const TArray<FInventorySlot>& GetSlots() const { return Slots; }
|
||||
|
||||
/**
|
||||
* Deplace la pile entiere d'un slot vers un autre.
|
||||
* - cible vide -> la pile entiere part
|
||||
* - meme objet -> fusion jusqu'a MaxStackSize, le reste demeure a la source
|
||||
* - objet different -> echange des deux slots
|
||||
* @return true si quelque chose a change.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool MoveItem(int32 FromIndex, int32 ToIndex);
|
||||
|
||||
/**
|
||||
* Deplace seulement une partie d'une pile.
|
||||
*
|
||||
* Un transfert partiel vers un slot occupe par un objet DIFFERENT est
|
||||
* refuse : il n'existe aucune facon sensee d'echanger la moitie d'une pile
|
||||
* contre autre chose. Seul un deplacement total peut provoquer un echange.
|
||||
*
|
||||
* @return true si quelque chose a change.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool MoveItemQuantity(int32 FromIndex, int32 ToIndex, int32 Quantity);
|
||||
|
||||
/** Index du premier slot libre, ou INDEX_NONE si l'inventaire est plein. */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 FindFirstEmptySlot() const;
|
||||
|
||||
/**
|
||||
* Retire jusqu'a Quantity exemplaires d'un slot precis et renvoie ce qui a
|
||||
* ete retire. Sert au drop au sol et, plus tard, au transfert vers un coffre.
|
||||
*
|
||||
* Different de RemoveItem() qui pioche par type sans se soucier du slot :
|
||||
* ici le joueur a designe une case, on ne touche qu'a celle-la.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool TakeFromSlot(int32 SlotIndex, int32 Quantity, FInventorySlot& OutTaken);
|
||||
|
||||
/**
|
||||
* Change le bonus de slots apporte par l'equipement (sac).
|
||||
* @return false si reduire la taille laisserait des objets dehors ;
|
||||
* dans ce cas rien n'est modifie et le sac ne doit pas etre retire.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
bool SetBonusSlotCount(int32 NewBonusSlots);
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Inventaire")
|
||||
FOnInventoryChanged OnInventoryChanged;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Barre rapide
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// La barre rapide n'est PAS un jeu de slots separe : ce sont les
|
||||
// HotbarSlotCount premiers slots de l'inventaire, simplement affiches
|
||||
// deux fois. Consequence directe : le glisser-deposer entre la barre et
|
||||
// la grille fonctionne sans une ligne de code de plus, puisque ce sont
|
||||
// les memes index dans le meme tableau.
|
||||
|
||||
/** Change le slot actif. Un index hors de la barre est ignore. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire|Barre rapide")
|
||||
void SelectHotbarSlot(int32 Index);
|
||||
|
||||
/** Avance ou recule dans la barre, en boucle. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire|Barre rapide")
|
||||
void CycleHotbarSelection(int32 Delta);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide")
|
||||
int32 GetSelectedHotbarIndex() const { return SelectedHotbarIndex; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide")
|
||||
int32 GetHotbarSlotCount() const { return FMath::Min(HotbarSlotCount, Slots.Num()); }
|
||||
|
||||
/** Contenu du slot actif. C'est ce que le joueur "a en main". */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire|Barre rapide")
|
||||
FInventorySlot GetSelectedSlot() const;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Inventaire|Barre rapide")
|
||||
FOnSelectedHotbarSlotChanged OnSelectedHotbarSlotChanged;
|
||||
|
||||
protected:
|
||||
/** Cases affichees dans la grille d'inventaire. N'inclut PAS la barre rapide. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire", meta = (ClampMin = "0"))
|
||||
int32 BackpackSlotCount = 20;
|
||||
|
||||
/** Slots supplementaires apportes par l'equipement (sac). Passe par SetBonusSlotCount(). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
int32 BonusSlotCount = 0;
|
||||
|
||||
/**
|
||||
* Cases de la barre rapide, independantes de celles de la grille.
|
||||
* Elles occupent les premiers index du tableau.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire|Barre rapide", meta = (ClampMin = "0"))
|
||||
int32 HotbarSlotCount = 10;
|
||||
|
||||
/** Toujours dimensionne a GetTotalSlotCount(). */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
TArray<FInventorySlot> Slots;
|
||||
|
||||
private:
|
||||
/** Redimensionne le tableau si la taille cible a change. */
|
||||
void EnsureSlotCount();
|
||||
|
||||
/** Regroupe les slots occupes au debut du tableau. */
|
||||
void CompactSlots();
|
||||
|
||||
int32 SelectedHotbarIndex = 0;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/DragDropOperation.h"
|
||||
#include "InventoryDragDropOperation.generated.h"
|
||||
|
||||
class UInventoryComponent;
|
||||
|
||||
/**
|
||||
* Ce qui voyage entre la case qu'on saisit et celle ou on lache.
|
||||
*
|
||||
* On transporte un INDEX et non le contenu du slot : si l'inventaire change
|
||||
* pendant le glisser (ramassage automatique, script), la cible relira l'etat
|
||||
* reel au moment du drop au lieu d'appliquer une donnee perimee.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API UInventoryDragDropOperation : public UDragDropOperation
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Inventaire")
|
||||
int32 SourceSlotIndex = INDEX_NONE;
|
||||
|
||||
/**
|
||||
* Combien d'exemplaires on deplace. Egal a la pile entiere pour un
|
||||
* glisser normal, la moitie avec Maj, un seul avec Ctrl.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Inventaire")
|
||||
int32 Quantity = 0;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Inventaire")
|
||||
TWeakObjectPtr<UInventoryComponent> SourceInventory;
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "InventorySlotWidget.generated.h"
|
||||
|
||||
class UImage;
|
||||
class UInventoryWidget;
|
||||
class UProgressBar;
|
||||
class UTextBlock;
|
||||
|
||||
/**
|
||||
* Une case de la grille d'inventaire : une icone et une quantite.
|
||||
*
|
||||
* Volontairement passive pour l'affichage : elle ne va jamais chercher
|
||||
* l'information elle-meme, c'est la grille parente qui lui pousse son contenu
|
||||
* via SetSlot(). Elle est en revanche autonome pour le glisser-deposer, parce
|
||||
* que c'est elle qui recoit les evenements souris.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UInventorySlotWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
/** Appele une fois a la creation : dit a la case qui elle represente. */
|
||||
void InitSlot(UInventoryComponent* InInventory, int32 InSlotIndex, UInventoryWidget* InOwningGrid);
|
||||
|
||||
/** Affiche le contenu d'un slot, ou se vide si le slot est vide. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void SetSlot(const FInventorySlot& InSlot);
|
||||
|
||||
/**
|
||||
* Transforme cette instance en fantome de glisser : le fond de case
|
||||
* disparait pour ne laisser flotter que l'objet lui-meme.
|
||||
*/
|
||||
void ConfigureAsDragVisual();
|
||||
|
||||
/** Affiche ou masque le cadre de selection (barre rapide). */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void SetSelected(bool bSelected);
|
||||
|
||||
protected:
|
||||
// --- Glisser-deposer ---
|
||||
virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
||||
virtual void NativeOnDragDetected(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent, UDragDropOperation*& OutOperation) override;
|
||||
virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
|
||||
|
||||
// --- Survol, pour que la touche "jeter" sache sur quoi agir ---
|
||||
virtual void NativeOnMouseEnter(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;
|
||||
virtual void NativeOnMouseLeave(const FPointerEvent& InMouseEvent) override;
|
||||
|
||||
/** A nommer exactement "ItemIcon" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UImage> ItemIcon;
|
||||
|
||||
/** A nommer exactement "QuantityText". */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> QuantityText;
|
||||
|
||||
/**
|
||||
* Fond de case. BindWidgetOptional et non BindWidget : le Blueprint peut
|
||||
* s'en passer sans casser la compilation, c'est purement decoratif.
|
||||
* Nomme-le "SlotBackground" pour qu'il soit masque pendant le glisser.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UWidget> SlotBackground;
|
||||
|
||||
/**
|
||||
* Cadre affiche quand la case est le slot actif de la barre rapide.
|
||||
* Optionnel : la grille d'inventaire ne s'en sert jamais.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UWidget> SelectionBorder;
|
||||
|
||||
/**
|
||||
* Petite barre d'usure en bas de la case, pour les objets a usages
|
||||
* multiples. Masquee automatiquement sur tout le reste.
|
||||
* A nommer "UsesBar" dans le Widget Blueprint.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UProgressBar> UsesBar;
|
||||
|
||||
/** Opacite du fantome qui suit le curseur pendant le glisser. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire", meta = (ClampMin = "0.05", ClampMax = "1.0"))
|
||||
float DragVisualOpacity = 0.85f;
|
||||
|
||||
/** Opacite de la case d'origine tant que le glisser est en cours. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Inventaire", meta = (ClampMin = "0.0", ClampMax = "1.0"))
|
||||
float DraggedSlotOpacity = 0.3f;
|
||||
|
||||
private:
|
||||
/** Rebranche l'opacite normale, que le glisser ait abouti ou non. */
|
||||
UFUNCTION()
|
||||
void HandleDragFinished(UDragDropOperation* Operation);
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> OwningInventory;
|
||||
|
||||
/** La grille parente, pour lui signaler le survol. */
|
||||
TWeakObjectPtr<UInventoryWidget> OwningGrid;
|
||||
|
||||
int32 SlotIndex = INDEX_NONE;
|
||||
|
||||
/** Dernier contenu recu, pour construire le fantome de glisser. */
|
||||
FInventorySlot CachedSlot;
|
||||
|
||||
/** Vrai si cette instance est un fantome et non une vraie case de la grille. */
|
||||
bool bIsDragVisual = false;
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InventoryWidget.generated.h"
|
||||
|
||||
class UInventoryComponent;
|
||||
class UInventorySlotWidget;
|
||||
class UUniformGridPanel;
|
||||
|
||||
/**
|
||||
* Grille d'inventaire. Cree ses cases dynamiquement a partir du nombre de
|
||||
* slots du composant, et se remet a jour uniquement quand l'inventaire change.
|
||||
*
|
||||
* Le nombre de cases n'est PAS fige : quand un sac ajoutera des slots, la
|
||||
* grille s'agrandira toute seule au prochain rafraichissement.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UInventoryWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
/** Reconstruit si necessaire, puis pousse le contenu dans chaque case. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Inventaire")
|
||||
void Refresh();
|
||||
|
||||
/** Index de la case sous le curseur, ou INDEX_NONE. */
|
||||
UFUNCTION(BlueprintPure, Category = "Inventaire")
|
||||
int32 GetHoveredSlotIndex() const { return HoveredSlotIndex; }
|
||||
|
||||
/** Appele par les cases quand le curseur entre ou sort. */
|
||||
void NotifySlotHovered(int32 Index, bool bHovered);
|
||||
|
||||
/**
|
||||
* (Re)branche le widget sur l'inventaire du pawn actuellement possede.
|
||||
* Indispensable apres un respawn : le nouveau pawn a un nouveau composant,
|
||||
* et l'ancien abonnement pointerait vers un objet detruit.
|
||||
*/
|
||||
void BindToOwningPawn();
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "SlotGrid" dans le Widget Blueprint. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UUniformGridPanel> SlotGrid;
|
||||
|
||||
/** Le widget de case a instancier : ton WBP_InventorySlot. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire")
|
||||
TSubclassOf<UInventorySlotWidget> SlotWidgetClass;
|
||||
|
||||
/** Nombre de cases par ligne. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Inventaire", meta = (ClampMin = "1"))
|
||||
int32 ColumnCount = 6;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Absorbe les depots qui ne visent aucune case. Sans ca, lacher un objet
|
||||
* sur le fond du panneau declencherait OnDragCancelled -- et si un jour on
|
||||
* y branche le drop au sol, le joueur jetterait son butin par accident.
|
||||
*/
|
||||
virtual bool NativeOnDrop(const FGeometry& InGeometry, const FDragDropEvent& InDragDropEvent, UDragDropOperation* InOperation) override;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleInventoryChanged();
|
||||
|
||||
/** Cree ou supprime des cases pour coller au nombre de slots. */
|
||||
void RebuildGrid(int32 DesiredCount);
|
||||
|
||||
/** Retrouve l'inventaire du pawn possede. */
|
||||
UInventoryComponent* ResolveInventory() const;
|
||||
|
||||
TWeakObjectPtr<UInventoryComponent> BoundInventory;
|
||||
|
||||
int32 HoveredSlotIndex = INDEX_NONE;
|
||||
|
||||
/** UPROPERTY obligatoire : sans ca le GC collecterait les cases. */
|
||||
UPROPERTY(Transient)
|
||||
TArray<TObjectPtr<UInventorySlotWidget>> SlotWidgets;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "ItemDataAsset.generated.h"
|
||||
|
||||
class UStaticMesh;
|
||||
class UTexture2D;
|
||||
|
||||
/**
|
||||
* Definition d'un TYPE d'objet : "Bois", "Pomme", "Corde".
|
||||
* Un asset par type, cree dans le Content Browser, edite a la main.
|
||||
*
|
||||
* C'est l'equivalent exact d'un ScriptableObject Unity : une donnee partagee
|
||||
* que tout le monde reference et que personne ne duplique. Cent buches dans
|
||||
* le monde pointent toutes vers le meme UItemDataAsset "Bois".
|
||||
*
|
||||
* Regle a ne jamais enfreindre : ceci decrit ce qu'est un objet, jamais son
|
||||
* etat. La quantite, la durabilite, l'usure appartiennent a l'inventaire ou
|
||||
* a l'acteur, pas ici -- sinon ramasser une pomme modifierait toutes les
|
||||
* pommes du jeu.
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class SURVIVAL_PROJET_API UItemDataAsset : public UPrimaryDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Identifiant stable, utilise par la sauvegarde et les recettes de craft. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
FName ItemId;
|
||||
|
||||
/** Nom affiche au joueur. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
FText DisplayName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item", meta = (MultiLine = "true"))
|
||||
FText Description;
|
||||
|
||||
/** Icone affichee dans la grille d'inventaire. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
TObjectPtr<UTexture2D> Icon;
|
||||
|
||||
/** Mesh utilise quand l'objet est pose au sol. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
TObjectPtr<UStaticMesh> WorldMesh;
|
||||
|
||||
/**
|
||||
* Nombre maximum d'exemplaires par slot.
|
||||
* 1 = ne s'empile pas (outils, armes), 20 = ressource classique.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item", meta = (ClampMin = "1"))
|
||||
int32 MaxStackSize = 1;
|
||||
|
||||
/**
|
||||
* Nombre d'utilisations avant que l'objet soit consomme.
|
||||
* 1 = objet a usage unique. 3 = une canette qu'on boit en trois gorgees.
|
||||
*
|
||||
* Les valeurs de restauration sont appliquees A CHAQUE utilisation, pas
|
||||
* reparties : une canette a 3 usages et 20 de soif rend 20 par gorgee.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item", meta = (ClampMin = "1"))
|
||||
int32 MaxUses = 1;
|
||||
|
||||
/**
|
||||
* Taille de pile reellement applicable.
|
||||
*
|
||||
* Un objet a charges ne s'empile jamais : deux canettes entamees a des
|
||||
* niveaux differents ne peuvent pas partager un slot, il faudrait stocker
|
||||
* deux compteurs dans une seule case. La regle est centralisee ici pour
|
||||
* qu'aucun appelant ne puisse l'oublier.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "Item")
|
||||
int32 GetEffectiveMaxStack() const { return MaxUses > 1 ? 1 : FMath::Max(1, MaxStackSize); }
|
||||
|
||||
/**
|
||||
* Slots ajoutes a l'inventaire quand cet objet est equipe comme sac.
|
||||
* Laisse a 0 pour tout ce qui n'est pas un contenant.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Sac", meta = (ClampMin = "0"))
|
||||
int32 BonusInventorySlots = 0;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Consommable
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
/** Coche pour qu'un clic gauche consomme l'objet et applique ses effets. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Consommable")
|
||||
bool bIsConsumable = false;
|
||||
|
||||
/** Faim rendue a la consommation. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Consommable", meta = (EditCondition = "bIsConsumable"))
|
||||
float HungerRestore = 0.f;
|
||||
|
||||
/** Soif rendue. Une valeur negative deshydrate -- utile pour l'eau salee. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Consommable", meta = (EditCondition = "bIsConsumable"))
|
||||
float ThirstRestore = 0.f;
|
||||
|
||||
/** Vie rendue. Negative pour un aliment avarie ou toxique. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Item|Consommable", meta = (EditCondition = "bIsConsumable"))
|
||||
float HealthRestore = 0.f;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameModeBase.h"
|
||||
#include "MainMenuGameMode.generated.h"
|
||||
|
||||
/**
|
||||
* GameMode de la map de menu.
|
||||
*
|
||||
* Son role principal est NEGATIF : empecher AFpsPlayer de spawner. La map de
|
||||
* menu etant une copie de la map de jeu, sans ce GameMode Override dans ses
|
||||
* World Settings, le joueur apparaitrait derriere le menu avec son HUD, ses
|
||||
* jauges et son inventaire.
|
||||
*
|
||||
* Divergence Unity a garder en tete : ici le GameMode est une propriete DE LA
|
||||
* MAP, pas un objet qu'on pose dedans. Il se choisit dans World Settings.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AMainMenuGameMode : public AGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMainMenuGameMode();
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "MainMenuPlayerController.generated.h"
|
||||
|
||||
class AMenuCameraSpot;
|
||||
class UConfirmDialogWidget;
|
||||
class UMainMenuWidget;
|
||||
class UScreenFadeComponent;
|
||||
|
||||
/**
|
||||
* Ce qu'on fait une fois l'ecran devenu noir.
|
||||
*
|
||||
* Quitter n'y figure pas : la sortie est seche, sans fondu, une fois la
|
||||
* confirmation obtenue.
|
||||
*/
|
||||
enum class EMenuPendingAction : uint8
|
||||
{
|
||||
None,
|
||||
StartGame
|
||||
};
|
||||
|
||||
/**
|
||||
* Controller du menu principal. Il ne possede aucun pawn : il pose simplement
|
||||
* sa vue sur une AMenuCameraSpot tiree au hasard et affiche le menu par-dessus.
|
||||
*
|
||||
* Comme dans le jeu, l'UI vit sur le controller et pas sur un acteur du monde.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AMainMenuPlayerController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMainMenuPlayerController();
|
||||
|
||||
/** Fondu au noir, puis chargement de la map de jeu. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Menu")
|
||||
void StartSoloGame();
|
||||
|
||||
/** Ouvre la demande de confirmation. Ne quitte rien par elle-meme. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Menu")
|
||||
void QuitGame();
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/**
|
||||
* Ta map de jeu.
|
||||
*
|
||||
* TSoftObjectPtr et non une reference dure : une reference dure ferait
|
||||
* charger la map de jeu EN MEME TEMPS que le menu, alors qu'on ne la veut
|
||||
* qu'au clic sur Jouer. C'est l'equivalent d'une Addressable en Unity, par
|
||||
* opposition a un champ public qui tire tout l'asset avec lui.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
|
||||
TSoftObjectPtr<UWorld> GameLevel;
|
||||
|
||||
/** Ton WBP_MainMenu. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
|
||||
TSubclassOf<UMainMenuWidget> MainMenuClass;
|
||||
|
||||
/**
|
||||
* Ton WBP_ConfirmDialog. Si tu le laisses vide, le bouton Quitter ferme le
|
||||
* jeu directement -- pratique pour tester, mais laisse-le assigne.
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
|
||||
TSubclassOf<UConfirmDialogWidget> QuitDialogClass;
|
||||
|
||||
/** La question posee avant de fermer le jeu. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu")
|
||||
FText QuitDialogMessage;
|
||||
|
||||
/** Fondu d'arrivee sur le menu, au lancement du jeu. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu", meta = (ClampMin = "0.1"))
|
||||
float OpeningFadeDuration = 1.5f;
|
||||
|
||||
/** Fondu de depart, quand on quitte le menu pour la partie. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Menu", meta = (ClampMin = "0.1"))
|
||||
float LeaveFadeDuration = 1.f;
|
||||
|
||||
/** Le fondu, partage avec AFpsPlayerController. Assigne-lui ton WBP_Fade. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Menu")
|
||||
TObjectPtr<UScreenFadeComponent> ScreenFade;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Menu")
|
||||
TObjectPtr<UMainMenuWidget> MainMenu;
|
||||
|
||||
UPROPERTY(Transient, BlueprintReadOnly, Category = "Menu")
|
||||
TObjectPtr<UConfirmDialogWidget> QuitDialog;
|
||||
|
||||
private:
|
||||
/** Tire un angle de vue au hasard, en evitant celui du dernier passage. */
|
||||
AMenuCameraSpot* PickCameraSpot();
|
||||
|
||||
/** Le fondu vient de se terminer : on execute l'action mise de cote. */
|
||||
UFUNCTION()
|
||||
void HandleFadeFinished();
|
||||
|
||||
/** Oui : on ferme le jeu, immediatement. */
|
||||
UFUNCTION()
|
||||
void HandleQuitConfirmed();
|
||||
|
||||
/** Non : on referme la boite et on rend la main au menu. */
|
||||
UFUNCTION()
|
||||
void HandleQuitCancelled();
|
||||
|
||||
/** Ferme le jeu sans rien demander. */
|
||||
void CloseGame();
|
||||
|
||||
EMenuPendingAction PendingAction = EMenuPendingAction::None;
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MainMenuWidget.generated.h"
|
||||
|
||||
class UButton;
|
||||
|
||||
/**
|
||||
* Le menu principal : quelques boutons par-dessus la vue du monde.
|
||||
*
|
||||
* Classe Abstract comme les autres widgets du projet : on cree un
|
||||
* WBP_MainMenu qui en herite, le C++ porte la logique et le Blueprint la mise
|
||||
* en page.
|
||||
*
|
||||
* Le widget ne decide de rien, il appelle AMainMenuPlayerController. Le jour ou
|
||||
* un bouton devra faire une action reseau, elle sera deja du bon cote : un
|
||||
* widget n'existe que sur la machine du joueur local, le serveur ne le voit pas.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API UMainMenuWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
protected:
|
||||
/** Lance la partie solo. Nomme exactement "PlayButton" dans le WBP. */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> PlayButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> QuitButton;
|
||||
|
||||
/**
|
||||
* Coop a venir. BindWidgetOptional et non BindWidget : tu peux dessiner ton
|
||||
* WBP_MainMenu sans eux aujourd'hui, le Blueprint compilera quand meme.
|
||||
* S'ils existent, ils sont grises automatiquement.
|
||||
*/
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> HostButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UButton> JoinButton;
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandlePlayClicked();
|
||||
|
||||
UFUNCTION()
|
||||
void HandleQuitClicked();
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Camera/CameraActor.h"
|
||||
#include "MenuCameraSpot.generated.h"
|
||||
|
||||
/**
|
||||
* Un angle de vue possible pour le fond du menu principal.
|
||||
*
|
||||
* Pose-en autant que tu veux dans la map de menu : le controller en tire une au
|
||||
* hasard a chaque lancement.
|
||||
*
|
||||
* Une classe dediee plutot qu'un tag "MenuCamera" sur une ACameraActor -- le
|
||||
* reflexe Unity : la recherche est typee au lieu d'etre une chaine de
|
||||
* caracteres, et la classe a un endroit ou porter ses propres reglages.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API AMenuCameraSpot : public ACameraActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMenuCameraSpot();
|
||||
|
||||
/**
|
||||
* Decoche pour retirer cet angle du tirage sans supprimer l'acteur.
|
||||
* Pratique pour mettre un cadrage de cote le temps de le retravailler.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Menu")
|
||||
bool bEnabled = true;
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Interactable.h"
|
||||
#include "PickupItem.generated.h"
|
||||
|
||||
class UItemDataAsset;
|
||||
class USphereComponent;
|
||||
class UStaticMeshComponent;
|
||||
|
||||
/**
|
||||
* Un objet pose dans le monde. Il ne DECRIT pas l'objet, il ne fait que
|
||||
* pointer vers son UItemDataAsset et porter une quantite.
|
||||
*
|
||||
* Consequence pratique : pour creer un nouveau type de ramassable, tu n'as
|
||||
* plus besoin d'un Blueprint par objet. Un seul BP_Pickup suffit, tu changes
|
||||
* juste l'ItemData sur chaque instance posee dans le niveau.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API APickupItem : public AActor, public IInteractable
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
APickupItem();
|
||||
|
||||
/**
|
||||
* Configure un pickup engendre a l'execution (objet jete au sol).
|
||||
* A appeler entre SpawnActorDeferred() et FinishSpawning() pour que
|
||||
* OnConstruction ait deja les bonnes donnees et pose le bon mesh.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Item")
|
||||
void InitPickup(UItemDataAsset* InItemData, int32 InQuantity, int32 InRemainingUses = -1);
|
||||
|
||||
// --- IInteractable ---
|
||||
virtual FText GetInteractionPrompt_Implementation() const override;
|
||||
virtual bool CanInteract_Implementation(AActor* Interactor) const override;
|
||||
virtual void Interact_Implementation(AActor* Interactor) override;
|
||||
virtual void OnBeginFocus_Implementation() override;
|
||||
virtual void OnEndFocus_Implementation() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sans garde WITH_EDITOR : on veut aussi que le mesh se pose correctement
|
||||
* sur les pickups engendres a l'execution quand on jette un objet.
|
||||
*/
|
||||
virtual void OnConstruction(const FTransform& Transform) override;
|
||||
|
||||
/** Visuel uniquement, sans collision. */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
TObjectPtr<UStaticMeshComponent> Mesh;
|
||||
|
||||
/**
|
||||
* Volume que le trace d'interaction vient toucher.
|
||||
* Volontairement separe du mesh : beaucoup de meshes d'asset packs,
|
||||
* en particulier la vegetation, n'ont aucune geometrie de collision.
|
||||
* Avec une sphere dediee, le pickup fonctionne quel que soit le visuel.
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Item")
|
||||
TObjectPtr<USphereComponent> InteractionSphere;
|
||||
|
||||
/** Quel objet ce pickup represente. A assigner sur chaque instance. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
TObjectPtr<UItemDataAsset> ItemData;
|
||||
|
||||
/** Combien d'exemplaires ce tas contient. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item", meta = (ClampMin = "1"))
|
||||
int32 Quantity = 1;
|
||||
|
||||
/**
|
||||
* Charges restantes pour un objet a usages multiples.
|
||||
* -1 = plein, ce qui est le cas de tout objet pose a la main dans le niveau.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
int32 RemainingUses = -1;
|
||||
|
||||
/**
|
||||
* Reprend automatiquement le WorldMesh de l'ItemData dans l'editeur.
|
||||
* Decoche pour imposer un mesh different sur une instance precise.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
bool bUseMeshFromItemData = true;
|
||||
|
||||
/** Active le Custom Depth quand l'objet est vise. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
|
||||
bool bHighlightOnFocus = true;
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ScreenFadeComponent.generated.h"
|
||||
|
||||
class UUserWidget;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnScreenFadeFinished);
|
||||
|
||||
/**
|
||||
* Fondu plein ecran, pose sur un PlayerController.
|
||||
*
|
||||
* Un composant et non du code dans le controller : le fondu sert au jeu (mort,
|
||||
* respawn) comme au menu principal, et deux implementations du meme voile
|
||||
* finiraient par diverger.
|
||||
*
|
||||
* Le voile est un widget UMG et non le fondu camera du moteur : StartCameraFade
|
||||
* ne teinte que le rendu de la SCENE et laisse tout le HUD visible par-dessus.
|
||||
* On garde quand meme StartCameraFade en parallele, uniquement pour le fondu
|
||||
* AUDIO, que le voile ne sait pas faire.
|
||||
*/
|
||||
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
|
||||
class SURVIVAL_PROJET_API UScreenFadeComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UScreenFadeComponent();
|
||||
|
||||
/**
|
||||
* Se declenche a la fin de CHAQUE fondu, entrant comme sortant.
|
||||
* A celui qui ecoute de savoir ce qu'il attendait.
|
||||
*/
|
||||
UPROPERTY(BlueprintAssignable, Category = "Fondu")
|
||||
FOnScreenFadeFinished OnFadeFinished;
|
||||
|
||||
/** Du noir vers le jeu. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Fondu")
|
||||
void FadeIn(float Duration);
|
||||
|
||||
/** Du jeu vers le noir, ou l'ecran reste jusqu'au prochain FadeIn. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Fondu")
|
||||
void FadeOut(float Duration);
|
||||
|
||||
/** Couvre l'ecran immediatement, sans animation ni delegue. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Fondu")
|
||||
void SnapToBlack();
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Fondu")
|
||||
bool IsFading() const { return bFading; }
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override;
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/** Ton WBP_Fade : un simple rectangle plein ecran, sans logique. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Fondu")
|
||||
TSubclassOf<UUserWidget> FadeWidgetClass;
|
||||
|
||||
/** Noir classique. Le blanc donne un reveil brutal. */
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Fondu")
|
||||
FLinearColor FadeColor = FLinearColor::Black;
|
||||
|
||||
private:
|
||||
/** Cree le voile au premier besoin, et pas avant : il depend du controller. */
|
||||
void EnsureWidget();
|
||||
|
||||
/** 1 = ecran entierement couvert, 0 = voile invisible. */
|
||||
void SetCoverage(float Coverage);
|
||||
|
||||
APlayerController* GetOwningController() const;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UUserWidget> FadeWidget;
|
||||
|
||||
/** 0 = ecran couvert, 1 = jeu entierement visible. */
|
||||
float Alpha = 1.f;
|
||||
|
||||
/** Valeur visee par Alpha : 0 pour partir au noir, 1 pour revenir. */
|
||||
float Target = 1.f;
|
||||
|
||||
/** Duree du fondu en cours, distincte des reglages qui restent constants. */
|
||||
float CurrentDuration = 1.f;
|
||||
|
||||
bool bFading = false;
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "SurvivalGameInstance.generated.h"
|
||||
|
||||
/**
|
||||
* Le seul objet qui survit aux changements de map.
|
||||
*
|
||||
* C'est l'equivalent le plus proche d'un singleton DontDestroyOnLoad d'Unity :
|
||||
* il naît au lancement du jeu et meurt a sa fermeture. Tout le reste --
|
||||
* GameMode, GameState, PlayerController, pawns, widgets -- est detruit et
|
||||
* recree a chaque OpenLevel.
|
||||
*
|
||||
* Y mettre : les reglages, la sauvegarde en cours, et plus tard l'hebergement
|
||||
* et le rejoindre-une-partie. Surtout pas d'etat de gameplay, qui appartient au
|
||||
* monde et doit disparaitre avec lui.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API USurvivalGameInstance : public UGameInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Nom de la derniere camera de menu utilisee. NAME_None au premier lancement. */
|
||||
UFUNCTION(BlueprintPure, Category = "Menu")
|
||||
FName GetLastMenuCameraName() const { return LastMenuCameraName; }
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Menu")
|
||||
void SetLastMenuCameraName(FName SpotName) { LastMenuCameraName = SpotName; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Stocke un NOM et pas un index : les index changent des qu'on ajoute ou
|
||||
* supprime une camera dans la map, et on retomberait sur autre chose.
|
||||
*
|
||||
* Ne sert qu'a eviter de retirer deux fois de suite le meme angle quand le
|
||||
* joueur revient au menu -- d'ou le Transient, ca n'a pas a etre sauvegarde.
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
FName LastMenuCameraName = NAME_None;
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameStateBase.h"
|
||||
#include "SurvivalGameState.generated.h"
|
||||
|
||||
/**
|
||||
* L'etat du monde que TOUT LE MONDE doit connaitre.
|
||||
*
|
||||
* Vide pour l'instant, pose des maintenant parce que la rebrancher plus tard
|
||||
* suppose de retoucher le GameMode et tous les Blueprints qui en derivent.
|
||||
*
|
||||
* Le piege a retenir pour la coop : AGameMode n'existe QUE sur le serveur.
|
||||
* Sur un client, GetGameMode() renvoie nullptr, toujours -- ce n'est pas un bug
|
||||
* mais le design du framework. Le GameMode arbitre, il ne se montre pas.
|
||||
* Le GameState, lui, est replique a tout le monde : c'est le seul endroit ou
|
||||
* un client peut lire l'etat de la partie.
|
||||
*
|
||||
* Y mettra : l'heure du jour, le numero de jour, la seed du monde, l'etat des
|
||||
* ressources partagees. Pas les stats d'un joueur, qui vont sur son PlayerState.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API ASurvivalGameState : public AGameStateBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerState.h"
|
||||
#include "SurvivalPlayerState.generated.h"
|
||||
|
||||
/**
|
||||
* Ce que les AUTRES joueurs savent de toi.
|
||||
*
|
||||
* Vide pour l'instant. Y mettra : pseudo, couleur, temps de survie, nombre de
|
||||
* morts -- tout ce qu'un coequipier doit pouvoir lire sur ton compte.
|
||||
*
|
||||
* La regle de partage avec le pawn est simple : le PlayerState survit a la mort
|
||||
* du pawn, le pawn non. L'inventaire et les jauges restent donc sur le pawn,
|
||||
* puisque la regle du jeu veut qu'on perde tout au sol en mourant.
|
||||
*/
|
||||
UCLASS()
|
||||
class SURVIVAL_PROJET_API ASurvivalPlayerState : public APlayerState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,116 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "SurvivalStatsComponent.generated.h"
|
||||
|
||||
/** Diffuse a chaque changement de vie, faim ou soif. L'UI s'y abonne. */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSurvivalStatsChanged);
|
||||
|
||||
/** Diffuse une seule fois, quand la vie atteint zero. */
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSurvivalDeath);
|
||||
|
||||
/**
|
||||
* Vie, faim et soif du personnage.
|
||||
*
|
||||
* Les valeurs descendent sur un timer a basse frequence et non dans Tick :
|
||||
* une jauge qui perd trois points par minute n'a aucune raison d'etre
|
||||
* recalculee 120 fois par seconde.
|
||||
*
|
||||
* Les vitesses sont exprimees PAR MINUTE, parce que c'est l'unite dans
|
||||
* laquelle on raisonne quand on equilibre un jeu de survie -- "cent points
|
||||
* de faim en trente minutes" se lit, "0.055 par seconde" non.
|
||||
*/
|
||||
UCLASS(ClassGroup = (Survival), meta = (BlueprintSpawnableComponent))
|
||||
class SURVIVAL_PROJET_API USurvivalStatsComponent : public UActorComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
USurvivalStatsComponent();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/** Rend de la vie, de la faim et de la soif. Les valeurs sont bornees au max. */
|
||||
UFUNCTION(BlueprintCallable, Category = "Survie")
|
||||
void ApplyRestore(float HealthAmount, float HungerAmount, float ThirstAmount);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Survie")
|
||||
void ApplyDamage(float Amount);
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetHealth() const { return Health; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetHunger() const { return Hunger; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetThirst() const { return Thirst; }
|
||||
|
||||
/** Ratios 0-1, directement utilisables par une ProgressBar. */
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetHealthPercent() const { return MaxHealth > 0.f ? Health / MaxHealth : 0.f; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetHungerPercent() const { return MaxHunger > 0.f ? Hunger / MaxHunger : 0.f; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
float GetThirstPercent() const { return MaxThirst > 0.f ? Thirst / MaxThirst : 0.f; }
|
||||
|
||||
UFUNCTION(BlueprintPure, Category = "Survie")
|
||||
bool IsDead() const { return bDead; }
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Survie")
|
||||
FOnSurvivalStatsChanged OnStatsChanged;
|
||||
|
||||
UPROPERTY(BlueprintAssignable, Category = "Survie")
|
||||
FOnSurvivalDeath OnDied;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Survie|Vie", meta = (ClampMin = "1"))
|
||||
float MaxHealth = 100.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Survie|Faim", meta = (ClampMin = "1"))
|
||||
float MaxHunger = 100.f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Survie|Soif", meta = (ClampMin = "1"))
|
||||
float MaxThirst = 100.f;
|
||||
|
||||
/** Points de faim perdus par minute. 3.3 vide une jauge pleine en ~30 min. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie|Faim", meta = (ClampMin = "0"))
|
||||
float HungerDecayPerMinute = 3.3f;
|
||||
|
||||
/** La soif descend plus vite que la faim : c'est le besoin le plus pressant. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie|Soif", meta = (ClampMin = "0"))
|
||||
float ThirstDecayPerMinute = 5.f;
|
||||
|
||||
/** Vie perdue par minute quand la faim est a zero. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie|Vie", meta = (ClampMin = "0"))
|
||||
float StarvationDamagePerMinute = 6.f;
|
||||
|
||||
/** Vie perdue par minute quand la soif est a zero. Cumulable avec la faim. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie|Vie", meta = (ClampMin = "0"))
|
||||
float DehydrationDamagePerMinute = 9.f;
|
||||
|
||||
/**
|
||||
* Secondes entre deux mises a jour. Une seconde suffit largement et reste
|
||||
* assez fin pour que les barres bougent de facon fluide a l'oeil.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Survie", meta = (ClampMin = "0.05"))
|
||||
float UpdateInterval = 1.f;
|
||||
|
||||
private:
|
||||
/** Appelee par le timer : fait descendre les jauges et applique les degats. */
|
||||
void UpdateStats();
|
||||
|
||||
FTimerHandle UpdateTimerHandle;
|
||||
|
||||
float Health = 0.f;
|
||||
float Hunger = 0.f;
|
||||
float Thirst = 0.f;
|
||||
|
||||
bool bDead = false;
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "SurvivalStatsWidget.generated.h"
|
||||
|
||||
class UProgressBar;
|
||||
class USurvivalStatsComponent;
|
||||
|
||||
/**
|
||||
* Les trois jauges de survie : vie, faim, soif.
|
||||
*
|
||||
* Aucun Tick : le widget s'abonne a OnStatsChanged, qui ne se declenche
|
||||
* qu'une fois par seconde ou lors d'une consommation.
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class SURVIVAL_PROJET_API USurvivalStatsWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeDestruct() override;
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category = "Survie")
|
||||
void Refresh();
|
||||
|
||||
/** (Re)branche sur les stats du pawn possede. Voir UInventoryWidget. */
|
||||
void BindToOwningPawn();
|
||||
|
||||
protected:
|
||||
/** A nommer exactement "HealthBar", "HungerBar" et "ThirstBar". */
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UProgressBar> HealthBar;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UProgressBar> HungerBar;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UProgressBar> ThirstBar;
|
||||
|
||||
/** En dessous de ce ratio, la barre passe en couleur d'alerte. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie", meta = (ClampMin = "0", ClampMax = "1"))
|
||||
float CriticalThreshold = 0.2f;
|
||||
|
||||
/** Couleur d'alerte, commune aux trois jauges. */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Survie")
|
||||
FLinearColor CriticalColor = FLinearColor(0.9f, 0.1f, 0.1f, 1.f);
|
||||
|
||||
private:
|
||||
UFUNCTION()
|
||||
void HandleStatsChanged();
|
||||
|
||||
USurvivalStatsComponent* ResolveStats() const;
|
||||
|
||||
/** Applique le remplissage et la couleur d'une barre. */
|
||||
void ApplyBar(UProgressBar* Bar, float Percent, const FLinearColor& BaseColor) const;
|
||||
|
||||
TWeakObjectPtr<USurvivalStatsComponent> BoundStats;
|
||||
|
||||
/**
|
||||
* Couleurs d'origine relevees au NativeConstruct.
|
||||
*
|
||||
* On les capture depuis les widgets eux-memes plutot que de les redeclarer
|
||||
* en C++ : la palette reste entierement dans le Designer, et le code ne
|
||||
* fait que basculer vers l'alerte puis revenir.
|
||||
*/
|
||||
FLinearColor HealthBaseColor = FLinearColor::White;
|
||||
FLinearColor HungerBaseColor = FLinearColor::White;
|
||||
FLinearColor ThirstBaseColor = FLinearColor::White;
|
||||
};
|
||||
Reference in New Issue
Block a user