# Survival_projet — Unreal Engine 5.8 Jeu de survie solo à la première personne, gameplay écrit en **C++**, Blueprints réservés au câblage d'assets. Inspirations assumées : Raft, Aloft, Valheim, esthétique solarpunk. Assets d'environnement : pack **SoStylized** (sa végétation n'est pas à l'échelle humaine). ## Interlocuteur L'auteur vient de **Unity** et découvre Unreal. Il écrit en français — **répondre en français**. Faire systématiquement le parallèle Unity quand un concept nouveau apparaît (Blueprint ≈ prefab, DataAsset ≈ ScriptableObject, ActorComponent ≈ MonoBehaviour, BeginPlay ≈ Start, Trace Channel ≈ LayerMask). Il raisonne déjà très bien en composants, inutile de réexpliquer les bases. Signaler en revanche les endroits où le modèle **diverge réellement** de Unity, ce sont ceux qui le piègent : framework GameMode/Controller/Pawn imposé, `UPROPERTY` obligatoire pour le GC, constructeur qui tourne dans l'éditeur (CDO), pas de matrice de collision globale. Il valide les décisions de design lui-même. Proposer **une recommandation argumentée**, pas un catalogue d'options. Quand un choix change matériellement le code (modèle d'inventaire, règles de mort), demander avant de coder plutôt que de deviner. ## Compiler ```powershell & "C:\Program Files\Epic Games\UE_5.8\Engine\Build\BatchFiles\Build.bat" Survival_projetEditor Win64 Development -Project="d:\Projet\Perso\Unreal\Survival_projet\Survival_projet.uproject" -WaitMutex ``` - Un DLL nommé `UnrealEditor-Survival_projet-000N.dll` signifie que **l'éditeur était ouvert** : c'est un hot reload. Prévenir l'auteur de fermer et relancer l'éditeur. - `-Rebuild` échoue tant que l'éditeur tourne (DLL verrouillés). - **Ne jamais réécrire en masse les timestamps** des fichiers source pour forcer un build. - L'outil Edit renvoie parfois `EPERM` sur `FpsPlayer.cpp` (watcher qui tient le handle) : réessayer, ou passer par Write sur le fichier entier. ## Règles d'architecture établies **Un système = un `UActorComponent`.** Jamais de logique empilée dans `AFpsPlayer`, qui n'est qu'un assemblage de composants plus la caméra et le déplacement. **Où va quoi** — le critère est « si le pawn meurt, est-ce que ça doit survivre ? » : | Nature | Emplacement | |---|---| | Le corps : déplacement, caméra, inventaire, stats, interaction | **Pawn** | | Le joueur : widgets HUD, input d'interface (Tab, Q, hotbar, Échap) | **PlayerController** | | Les règles de la partie, le respawn | **GameMode** | | Ce qui survit au changement de map : réglages, sauvegarde | **GameInstance** | | Ce que les autres savent de toi (plus tard, si multi) | **PlayerState** | **Données vs état.** `UItemDataAsset` décrit ce qu'un objet **est** (nom, mesh, icône, effets). Quantité, charges restantes, durabilité sont de l'**état** et vivent sur le slot ou l'acteur. Mettre de l'état dans un DataAsset le partagerait entre toutes les instances du jeu. **Un Blueprint par comportement, un DataAsset par objet.** 200 objets et 3 comportements donnent 3 Blueprints et 200 DataAssets. `BP_Pickup` est générique, son `ItemData` change par instance. **Réglages exposés.** Toute valeur qui se règle à l'œil est une `UPROPERTY(EditAnywhere)`, pour itérer en PIE sans recompiler. **Pièges C++ Unreal rencontrés** : ne pas nommer un membre `Player`, `OldPawn`, `Slot` ou `CrouchedEyeHeight` dans une classe dérivée du moteur (masquage → erreur C4458 / UHT). `TWeakObjectPtr` ne permet pas de détecter une transition par simple comparaison de pointeurs, il faut un booléen d'état à côté. **Le mode d'input vit sur le viewport, pas sur le controller.** `OpenLevel` détruit le PlayerController mais pas la fenêtre : un controller qui arrive sur une map ne doit jamais supposer l'état laissé par la précédente, il repose son `SetInputMode`. Symptôme quand on l'oublie, en Standalone seulement : la caméra ne répond qu'au clic droit maintenu. **Enhanced Input en pause** : un jeu en pause ne fait qu'un tick d'input réduit, où seules les actions cochées **« Trigger When Paused »** sont évaluées. C'est une case sur l'asset `IA_`, pas du C++ — et son corollaire est qu'aucune action du pawn ne se déclenche en pause. ## Systèmes en place | Système | Fichiers | |---|---| | Personnage, caméra, game feel | `FpsPlayer.h/.cpp` | | HUD, input d'interface, fondus, respawn | `FpsPlayerController.h/.cpp` | | Interaction (trace + interface) | `Interactable.h`, `InteractionComponent`, `InteractionPromptWidget` | | Objets | `ItemDataAsset`, `PickupItem` | | Inventaire | `InventoryComponent`, `InventoryWidget`, `InventorySlotWidget`, `HotbarWidget`, `InventoryDragDropOperation` | | Survie | `SurvivalStatsComponent`, `SurvivalStatsWidget` | | Menu principal | `MainMenuGameMode`, `MainMenuPlayerController`, `MainMenuWidget`, `MenuCameraSpot` | | Menu de pause | `PauseMenuWidget` + la partie pause de `FpsPlayerController` | | Réglages | `SurvivalUserSettings`, `SettingsMenuWidget`, `SettingsTabWidget`, `SettingsGameplayWidget`, `SettingRowWidget` | | Fondus, boîtes de dialogue | `ScreenFadeComponent`, `ConfirmDialogWidget` | ### Décisions de gameplay figées - **Inventaire** : un seul `TArray` de 30 slots. Index **0-9 = barre rapide**, **10-29 = grille**. Conteneurs **indépendants** (un objet dans la barre n'apparaît pas dans la grille), même tableau uniquement pour que le glisser-déposer n'ait aucune conversion d'index à faire. - **Ramassage** : complète les piles existantes partout, puis ouvre un slot dans la **barre en priorité**, puis déborde dans la grille. - **Glisser** : normal = pile entière, **Maj** = moitié arrondie au supérieur, **Ctrl** = un seul. Lâcher hors de la grille jette au sol ; lâcher entre deux cases est absorbé (jamais de perte accidentelle). - **Clic gauche** = utiliser l'objet actif de la barre. Bindé sur le **pawn**, donc muet quand l'inventaire est ouvert — le conflit avec le drag & drop se règle tout seul. - **Objets à charges** (`MaxUses > 1`) ne s'empilent jamais. Les valeurs de restauration s'appliquent **par utilisation**. L'usure suit l'objet jusqu'au sol et revient avec lui. - **Mort** : tout tombe au sol autour du corps, respawn au `PlayerStart`, jauges pleines. - **Fondus** : un unique voile plein écran `WBP_Fade` au ZOrder 1000. Fondre widget par widget est fragile — les cases réécrivent leur propre `RenderOpacity`. - Après un respawn, le pawn est un **nouvel acteur** : les widgets doivent se rebrancher via `BindToOwningPawn()`, appelé depuis `OnPossess`. Même raison côté réglages : le pawn les **relit** à son `BeginPlay` et s'abonne à `OnSurvivalSettingsApplied`. - **Échap** a une priorité explicite dans `HandlePauseInput()` : boîte de confirmation → réglages → inventaire → pause. Sans cet ordre, deux écrans se retrouvent ouverts en même temps. - **Menu de pause** : `SetPause(true)` fige le monde, mais **pas Slate** — les animations UMG et `ScreenFadeComponent` continuent (d'où son `bTickEvenWhenPaused`). Pas de bouton « Quitter le jeu » : la sortie se fait depuis le menu principal. - **Réglages** : `USurvivalUserSettings` fait autorité, jamais le CDO du pawn. Application immédiate, aucun bouton « Appliquer » ; la sauvegarde a lieu à la fermeture de l'écran. L'écran ignore qui l'ouvre (`OnCloseRequested`), d'où un seul `WBP_Settings` pour le menu principal et la pause. Une ligne = un index dans une liste de paliers, y compris les nombres : d'où un seul `WBP_SettingRow` pour tout le menu. ## Conventions - Commentaires **en français**. Ils expliquent le *pourquoi* et les pièges, jamais le *quoi*. - Nommage éditeur : `BP_` acteurs, `WBP_` widgets, `DA_` data assets, `IA_` input actions, `IMC_` mapping contexts, `GM_` game mode. - Widgets : classe C++ `Abstract` pour la logique, Widget Blueprint enfant pour la mise en page. Les widgets liés utilisent `meta = (BindWidget)`, ou `BindWidgetOptional` quand l'élément est décoratif. - Les widgets s'abonnent à des délégués, **jamais de Tick** pour lire un état. ## État actuel et suite Boucle complète : récolter → ranger → consommer → survivre → mourir → réapparaître. Autour : menu principal, menu de pause, et un écran de réglages dont **seul l'onglet « Jeu & Contrôles » est rempli**. Suite immédiate sur les réglages, dans l'ordre : onglet **Affichage** (le moteur fait tout, prévoir la confirmation à rebours de 10 s sur la résolution), onglet **Graphismes** (les dix curseurs de `UGameUserSettings` ; végétation et distance d'affichage comptent double avec SoStylized), onglet **Audio** quand des `SoundClass` existeront, et enfin le **remappage des touches** via `UEnhancedInputUserSettings` — le seul morceau réellement coûteux. Prochaine étape gameplay recommandée : **spawner de ressources avec repousse** (`AResourceSpawner`, liste pondérée de DataAssets, rayon, densité, délai). Sans lui la carte se vide en dix minutes. Ensuite, par ordre de valeur : craft, objet visible en main, coffres, sauvegarde, cycle jour/nuit.