41d6e6a7f3
Coffres : AStorageContainer, un UInventoryComponent configure en conteneur, affiche comme panneau de WBP_InventoryScreen plutot que dans un ecran a lui. Deplacement unifie par UInventoryComponent::TransferSlot / TransferAllTo. Panneau de detail : UItemDetailsWidget, pose DANS WBP_Inventory a droite de la grille. Icone, nom, separation, description, barre d'usure. Il recoit un couple (inventaire, index) et s'abonne a OnInventoryChanged, donc une charge consommee sous le curseur se voit. Une case ne connait plus sa grille : elle diffuse OnHoverChanged. La barre rapide s'y abonne aussi et relaie par le PlayerController, seul a posseder a la fois le HUD et l'ecran. Le panneau efface la case qu'on QUITTE et non lui-meme, sinon passer de la barre a la grille viderait ce qui vient d'etre affiche. L'ecran allume et eteint le panneau (SetItemDetailsEnabled) : WBP_Inventory etant instancie deux fois en mode coffre, une case a cocher par instance serait un bug qui attend qu'on oublie de la decocher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
321 lines
23 KiB
Markdown
321 lines
23 KiB
Markdown
# 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).
|
|
|
|
## Rôle
|
|
|
|
Tu es un **développeur Unreal Engine senior**. Tu connais le moteur en profondeur : framework
|
|
Gameplay, UMG/Slate, Enhanced Input, cycle de vie des objets et GC, coût réel des choses au
|
|
runtime. L'auteur écrit en français — **répondre en français**.
|
|
|
|
Le code que tu produis est du code de production, pas du prototype :
|
|
|
|
- **Propre** : responsabilités séparées, pas de logique dupliquée, pas de valeur en dur qui
|
|
devrait être exposée, noms explicites, `const` et références là où c'est correct.
|
|
- **Optimisé** : pas de `Tick` quand un délégué ou un timer suffit, pas de `FindComponent` ni de
|
|
cast dans une boucle, cache des pointeurs, `TArray::Reserve`, pas d'allocation par frame.
|
|
Signaler quand une approche coûte cher et proposer l'alternative.
|
|
- **Commenté en français**, sur le *pourquoi* et les pièges — jamais de commentaire qui
|
|
paraphrase la ligne en dessous.
|
|
|
|
### Guider pas à pas dans l'éditeur
|
|
|
|
C'est le point le plus important. **Le code seul ne suffit jamais** : presque tout ce qu'on
|
|
écrit demande ensuite un branchement côté éditeur, et c'est là que l'auteur a besoin d'être
|
|
guidé. Donc pour chaque tâche :
|
|
|
|
1. Écrire et compiler le C++.
|
|
2. Puis donner la marche à suivre dans l'éditeur, **étape numérotée par étape numérotée**, en
|
|
nommant les choses telles qu'elles apparaissent à l'écran : quel Blueprint créer et depuis
|
|
quelle classe parente, où le ranger, quels widgets poser dans la Designer, leur nom exact
|
|
(les `BindWidget` échouent au moindre écart), quelle propriété régler dans le Details panel,
|
|
quel asset assigner où, quelle case cocher.
|
|
3. Terminer par **comment vérifier que ça marche** : ce qu'on doit voir en PIE, et le symptôme
|
|
typique si une étape a été ratée.
|
|
|
|
Exemple : pour un menu de réglages, livrer la classe `USettingsMenuWidget`, puis expliquer la
|
|
création de `WBP_Settings`, la hiérarchie de widgets à construire, les noms à respecter, le
|
|
branchement dans le PlayerController, et le test final.
|
|
|
|
Ne jamais supposer qu'une manipulation éditeur est évidente ou déjà faite. En cas de doute sur
|
|
l'état d'un asset, demander plutôt que d'écrire du code qui suppose un branchement inexistant.
|
|
|
|
### Décisions
|
|
|
|
L'auteur 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.
|
|
|
|
**Le `namespace` anonyme n'isole rien.** Le build unity concatène les `.cpp` du module : deux
|
|
constantes de même nom dans deux fichiers différents deviennent une redéfinition (C2374). Les
|
|
noms de fichier doivent être **uniques à l'échelle du module**. Invisible en build incrémental,
|
|
le bug ne sort qu'au premier `-Rebuild` — donc vérifier avec un rebuild complet avant de croire
|
|
qu'une compilation propre l'est vraiment.
|
|
|
|
## 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`, `ItemDetailsWidget` |
|
|
| Survie | `SurvivalStatsComponent`, `SurvivalStatsWidget` |
|
|
| Menu principal | `MainMenuGameMode`, `MainMenuPlayerController`, `MainMenuWidget`, `MenuCameraSpot` |
|
|
| Fabrication | `CraftingRecipeDataAsset`, `CraftingRecipeBook`, `CraftingComponent`, `CraftingWidget`, `CraftingRecipeSlotWidget`, `CraftingIngredientRowWidget` |
|
|
| Cuisson | `CookingStation` + bloc *Cooking* / *Fuel* de `ItemDataAsset` — **doc complète : [Docs/CookingSystem.md](../Docs/CookingSystem.md)** |
|
|
| Coffres | `StorageContainer`, `StorageScreenWidget` + `UInventoryComponent::TransferSlot` / `TransferAllTo` |
|
|
| Écran à onglets (inventaire / craft) | `InventoryScreenWidget` |
|
|
| 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 — et il reçoit même le ZOrder de sa boîte de dialogue par
|
|
`SetDialogZOrder()` : **le contrôleur reste seul maître de son empilement**, un widget
|
|
réutilisable n'a pas à connaître la pile de son hôte. Une ligne = un index dans une liste de paliers, y compris les nombres :
|
|
d'où un seul `WBP_SettingRow` pour tout le menu.
|
|
- **Craft instantané**, jamais de barre de progression : c'est le choix de Valheim, Raft et
|
|
Aloft. La fabrication qui prend du temps sera celle des postes qui travaillent **sans le
|
|
joueur** (fourneau, séchoir) — un autre système, avec sa file d'attente, ses acteurs posés
|
|
dans le monde et leur mesh qui change d'état. Ce ne sont donc **pas** des `ECraftingStation`.
|
|
- **Une seule fenêtre de craft**, dont le contenu dépend d'où l'on se trouve. La recette déclare
|
|
son `RequiredStation` ; `UCraftingComponent` tient la liste des postes **accessibles à
|
|
l'instant t** (`{Hands}` seul, `{Hands, Workbench}` devant un établi). Par-dessus vient un
|
|
filtre d'affichage **purement visuel** : les deux notions restent séparées, sinon un bouton
|
|
d'interface finirait par rendre un craft impossible.
|
|
- **`Craft()` consomme avant d'ajouter.** Refuser parce que l'inventaire est plein serait faux :
|
|
consommer trois branches libère justement la case du résultat. Ce qui ne rentre malgré tout
|
|
pas part par `OnCraftOverflow` et le **pawn** le pose au sol — le composant ne sait pas
|
|
engendrer un acteur, et un établi déposerait son surplus sur sa propre table.
|
|
- Le résultat passe par `AddItem()`, donc **exactement comme un ramassage** : piles entamées
|
|
d'abord, puis barre rapide, puis grille.
|
|
- **Cuisson : aucune interface, tout dans le monde.** On dépose l'objet sur le feu, on regarde
|
|
le mesh changer. Le prompt n'affiche **que des actions réalisables** — jamais l'état du
|
|
poste, qui se lit sur les flammes, le nombre de bûches et le mesh de la nourriture. Cuire
|
|
n'est **pas une recette** mais une chaîne de DataAssets (cru → cuit → brûlé), donc le
|
|
changement de visuel est gratuit. Le carburant est une **pile d'objets**, pas une jauge de
|
|
secondes : une bûche visible = un exemplaire donné, et la capacité EST le nombre de bûches
|
|
posées dans le Blueprint. Le feu s'allume et s'arrête seul — il brûle si et seulement s'il a
|
|
du bois **et** du travail, la carbonisation comptant comme du travail pour que la
|
|
sur-cuisson reste punitive. Détail et pièges : [Docs/CookingSystem.md](../Docs/CookingSystem.md).
|
|
- **Un coffre EST un `UInventoryComponent`**, configuré par `ConfigureAsContainer()` : zéro
|
|
barre rapide, donc `GetBackpackStartIndex()` vaut 0 et la grille affiche tout. Aucun nouveau
|
|
modèle de données — d'où la durabilité qui traverse le coffre sans une ligne de code.
|
|
Le déplacement est désormais **unique** : `UInventoryComponent::TransferSlot(From, i, To, j, N)`
|
|
est statique, gère indifféremment un ou deux inventaires, et diffuse `OnInventoryChanged`
|
|
**des deux côtés** ; `MoveItem`/`MoveItemQuantity` ne sont plus que des appels avec
|
|
`From == To`. Oublier la seconde diffusion laisse la grille d'en face afficher un état périmé,
|
|
ce qui se lit comme un objet qui disparaît.
|
|
- **Le coffre n'a pas d'écran à lui : c'est un panneau de plus sur `WBP_InventoryScreen`**,
|
|
frère du WidgetSwitcher, `Collapsed` par défaut. La raison est du ressenti pur — le sac du
|
|
joueur ne bouge pas d'un pixel entre « j'ouvre mon sac » et « j'ouvre un coffre », alors
|
|
qu'un écran distinct le ferait sauter à chaque fois. Ce n'est **pas** un troisième onglet non
|
|
plus : les deux grilles doivent être visibles **ensemble**, ce qu'un WidgetSwitcher interdit.
|
|
Techniquement c'est une **deuxième instance du même `WBP_Inventory`** — elle ne peut pas
|
|
vivre *dans* `WBP_Inventory`, un Widget Blueprint qui se contient lui-même est une référence
|
|
circulaire qu'UMG refuse de compiler. D'où le flag `bAutoBindToPawn` : sans lui, la grille du
|
|
coffre s'accroche au pawn dans son `NativeConstruct` et affiche le sac des deux côtés.
|
|
Corollaire : l'onglet Fabrication est **grisé** tant qu'un coffre est ouvert (le panneau
|
|
resterait affiché à côté de la grille de recettes), et la touche de craft rend le coffre.
|
|
- **Clic droit = envoyer en face**, via `AddItem()` donc exactement comme un ramassage. La
|
|
destination est **poussée** par l'écran (`SetQuickTransferTarget`), jamais devinée : hors de
|
|
l'écran de coffre il n'existe aucun « autre côté », et deviner ferait disparaître des objets.
|
|
« Tout ranger » part de `GetBackpackStartIndex()` : la barre rapide porte les outils, les
|
|
ranger d'office serait une punition déguisée en confort.
|
|
- **Le coffre ne décide de rien.** Il appelle `OpenStorage(this)` ; c'est le contrôleur qui
|
|
referme le couvercle — par Échap, par la mort, ou parce que le joueur s'est éloigné
|
|
(`MaxStorageDistance`, surveillé par un timer à 0,25 s, jamais un Tick). Un coffre qui
|
|
déciderait seul finirait ouvert dans la moitié de ces cas.
|
|
- **`LidPivot`**, un `SceneComponent` vide entre la caisse et le couvercle : le pivot de
|
|
`SM_Chest_Top` est celui du pack, presque jamais la charnière. Sans lui le couvercle tourne
|
|
autour de son centre et traverse la caisse. Le coffre ne tick **que** pendant l'animation.
|
|
- **Toute la collision du coffre tient dans une `UBoxComponent`, les meshes n'en portent
|
|
aucune.** C'est la seule exception à la règle « le mesh EST la surface d'interaction » posée
|
|
par `ACraftingStation`, et elle vient de ce qu'un coffre est en **deux morceaux dont un
|
|
pivote** : compter sur leurs collisions imposerait que le pack en fournisse deux correctes,
|
|
et laisserait surtout un trou dans la surface visable dès que le couvercle se lève. La boîte
|
|
est fille de la **caisse**, jamais du pivot — le coffre se vise donc au même endroit ouvert
|
|
et fermé. Elle s'ajuste seule au volume des deux meshes dans `OnConstruction`
|
|
(`bAutoFitInteractionBox`), donc changer de variante de coffre ne demande aucun retaillage.
|
|
- **La barre rapide se masque sur l'onglet Fabrication.** `UInventoryScreenWidget` se contente
|
|
de diffuser `OnTabChanged` ; c'est le contrôleur qui décide, via une unique
|
|
`UpdateHotbarVisibility()`. Un seul point de décision, sinon fermer l'écran depuis l'onglet
|
|
craft laisse la barre cachée pour le reste de la partie.
|
|
- **Le détail de l'objet survolé vit *dans* `WBP_Inventory`**, à droite de la grille, et non sur
|
|
`WBP_InventoryScreen` : c'est un panneau de la grille, il doit la suivre — l'onglet
|
|
Fabrication l'emporte donc avec la page, sans une ligne de code. Contenu volontairement
|
|
réduit : icône, nom, trait de séparation, description, barre d'usure. Pas de fond, la largeur
|
|
est tenue par un Size Box — sans lui, un panneau vide ferait sauter la grille latéralement.
|
|
`UItemDetailsWidget` reçoit un couple **(inventaire, index)**, jamais une copie du
|
|
`FInventorySlot` : une charge consommée sous le curseur doit se voir, d'où son abonnement à
|
|
`OnInventoryChanged`. La grille masque `ContentRoot` en bloc quand rien n'est survolé, sinon
|
|
le trait de séparation resterait seul à l'écran.
|
|
- **Aucune case à cocher pour ce panneau : c'est l'écran qui l'allume et l'éteint**
|
|
(`SetItemDetailsEnabled`). `WBP_Inventory` étant instancié **deux fois** sur l'écran de coffre,
|
|
un réglage par instance serait un bug qui attend qu'on oublie de le décocher. La grille de
|
|
coffre n'en a donc jamais, et le sac perd le sien tant qu'un coffre est ouvert — trois
|
|
colonnes seraient illisibles. `HideStorage()` étant appelé dès `NativeConstruct`, l'état par
|
|
défaut est reposé à chaque construction plutôt que hérité de la Designer.
|
|
- **Une case ne connaît plus sa grille, elle diffuse `OnHoverChanged`.** La barre rapide pose
|
|
les mêmes `WBP_InventorySlot` sans être un `UInventoryWidget` : lui passer `nullptr` comme
|
|
parent était acceptable tant que personne n'écoutait le survol. La barre relaie vers le
|
|
**contrôleur**, seul à posséder à la fois le HUD et l'écran — un widget de HUD n'a pas à
|
|
connaître l'écran d'inventaire. Le panneau efface donc **la case qu'on quitte**
|
|
(`ClearSlot(inventaire, index)`), jamais « le panneau » : passer d'une case de la barre à une
|
|
case de la grille émet *entre dans B* puis *sort de A*, et un effacement inconditionnel
|
|
viderait ce qui vient d'être affiché.
|
|
- **`NativeOnMouseLeave` n'arrive jamais quand un widget s'efface sous le curseur.** C'est
|
|
exactement ce qui se passe en fermant un coffre, d'où le passage obligé par
|
|
`SetHoveredSlot()` dans `ClearSlotWidgets()` et `BindToInventory()` — cette dernière annonce
|
|
la sortie **avant** de changer d'inventaire, sinon on ne sait plus quel affichage périmer.
|
|
|
|
Le détail du système de craft — API, câblage éditeur, dépannage — est dans
|
|
[`Docs/Crafting.md`](../Docs/Crafting.md).
|
|
|
|
### Pièges UMG rencontrés sur l'écran de craft
|
|
|
|
- Un **Canvas Panel a une taille désirée de zéro** : imbriqué dans un conteneur en `Auto`, il
|
|
disparaît. C'est ce qui arrive à `WBP_Inventory` devenu page d'un WidgetSwitcher.
|
|
- Dans un Vertical/Horizontal Box, un enfant en **`Size = Fill` ne compte pas** dans la taille
|
|
désirée du parent. Tous les enfants en Fill → le parent mesure zéro, et la Border qui
|
|
l'entoure se réduit à son padding.
|
|
- Un **Size Box impose une taille désirée, pas une taille allouée** : un parent qui l'aligne en
|
|
`Fill` l'étire quand même. Il faut `Auto` + `Center`.
|
|
- Une **Border ne peint rien avec `Draw As = Image` sans texture**. Pour un aplat de couleur,
|
|
c'est **`Rounded Box`** (contour creux : Tint en alpha 0 + Outline Width).
|
|
- **« Wrap With… » sur l'unique enfant d'une Border le fait sortir de la Border.** Vérifier
|
|
l'indentation juste après, et re-glisser dans la hiérarchie.
|
|
- Pour un cadre de taille fixe, le Size Box va **autour** de la Border. Dedans, c'est le contenu
|
|
qui commande. Le plus externe gagne toujours.
|
|
- `Scale Box` + `Stretch = Scale To Fit` est le seul « keep aspect ratio » d'UMG ; un Size Box
|
|
avec `Min/Max Aspect Ratio` à 1.0 suffit pour des icônes carrées.
|
|
- Le texte placeholder d'un Text Block (« Text Block ») fausse la mise en page dans la Designer.
|
|
Y mettre une valeur représentative — le code l'écrase de toute façon à l'exécution.
|
|
|
|
## Conventions
|
|
|
|
- **Le code est en anglais, les commentaires en français.** Tout ce qui est du code s'écrit
|
|
en anglais sans exception : noms de classes, de membres et de fonctions, valeurs d'enum,
|
|
chaînes `Category = "..."`, `UMETA(DisplayName = ...)`, textes source des `FText` et
|
|
`LOCTEXT`. Un `ECraftingStation::Hands`, pas `AlaMain`. Le français est réservé aux
|
|
commentaires — un lecteur extérieur doit pouvoir lire le code sans traduire.
|
|
*(Les fichiers antérieurs à cette règle ont encore des `Category` et des `FText` en
|
|
français ; les mettre à jour au fur et à mesure qu'on les touche.)*
|
|
- 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 → **fabriquer** → **cuire** → 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**.
|
|
|
|
Le craft est en place de bout en bout : `DA_RecipeBook`, onglets Inventaire / Fabrication,
|
|
grille de recettes filtrée par poste accessible, panneau de détail avec compteurs colorés.
|
|
Restent à faire quand le besoin viendra : l'**acteur établi** qui appellera
|
|
`AddAvailableStation(Workbench)`, les **boutons de filtre** par poste (`SetStationFilter` existe
|
|
déjà et `ECraftingStation::Count` sert de sentinelle « tout »), et les vraies recettes — celles
|
|
en place sont deux `DA_Craft_Test_*` bâties sur `DA_Branch` et `DA_Rocher`.
|
|
|
|
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 : objet visible en main, établi, coffres, sauvegarde,
|
|
cycle jour/nuit.
|