6.9 KiB
0005 — Ticks de plongée fixes et mises à jour typées
- Statut : accepté
- Date : 2026-08-10
- Remplace : aucun
Contexte
La plongée combine une physique Godot, des ressources continues, des dégâts ponctuels, des outils électriques, des alarmes et plusieurs présentations. Si chaque acteur ou interface utilise sa propre horloge ou recalcule une partie de ces règles, la pause, les variations de framerate et l'ordre des signaux peuvent produire des résultats différents.
Les informations n'ont pas toutes la même nature. Une commande exprime une intention, la télémétrie décrit une mesure du monde, un snapshot décrit l'état autoritaire à un instant et un événement rapporte un fait survenu pendant une transition. Les regrouper dans un dictionnaire ou un signal générique rendrait leur propriétaire et leur temporalité implicites.
Décision
Horloge et cadence
La simulation de plongée utilise une cadence de physique configurée à 60 Hz.
L'application fournit exactement un DiveStep au domaine pour chaque tick de
physique accepté. Le domaine n'accède ni à l'horloge système, ni au framerate,
ni à l'arbre de scènes.
Un DiveTick contient au minimum l'identifiant de session, une séquence
strictement croissante et la durée du pas en secondes. La durée normale vaut
1 / 60 seconde. Un pas nul, négatif, non fini ou désordonné est refusé. Un pas
supérieur à 0,1 seconde est subdivisé de manière déterministe par l'application
ou refusé ; il n'est jamais appliqué comme un grand saut unique.
La pause suspend la production de DiveStep. À la reprise, le temps passé en
pause n'est ni simulé, ni rattrapé. Les tests utilisent des ticks construits
explicitement et n'attendent jamais le temps réel.
Contrats d'entrée séparés
VehicleCommandcontient l'intention de propulsion issue du clavier ou d'un autre périphérique remappé. La souris n'oriente jamais le sous-marin.CameraLookCommandcontient uniquement le mouvement de regard dans la cabine. Il ne traverse pas le contrat de pilotage du véhicule.VehicleTelemetryest un snapshot immuable produit par l'acteur physique : transform, vitesses, profondeur et efforts réellement appliqués.DiveTelemetrySampleréduit cette télémétrie aux mesures nécessaires aux règles de plongée. Le domaine ne reçoit aucunNodeouTransform3DGodot.CollisionObservationdécrit un impact ponctuel avec une séquence stable et sa vitesse normale en mètres par seconde. Les observations sont traitées de manière idempotente.ToolPowerDemand,EjectionIntentetReturnRouteSampledécrivent séparément la demande électrique des outils, l'intention d'éjection et les données connues pour estimer le retour.DiveStepagrège ces entrées pour un tick sans changer leur responsabilité.
Ces frontières utilisent des objets typés. Les dictionnaires anonymes restent confinés aux codecs ou aux prototypes et ne traversent pas la production.
Règles résolues et session
Au début de chaque plongée, DiveRulesResolver construit un
ResolvedDiveRules immuable en appliquant, dans cet ordre :
- les règles de base ;
- les capacités et améliorations du sous-marin ;
- le profil de difficulté ;
- les aides d'accessibilité qui modifient le gameplay ;
- la validation et le bornage des valeurs.
DiveSession est l'unique propriétaire de l'oxygène, de la batterie, de la
coque, des seuils de pression, des alertes, de l'estimation de retour et de
l'état terminal de la plongée. C'est un objet de domaine pur : il reçoit un
DiveStep, applique les règles résolues dans un ordre fixe et retourne un
DiveUpdate.
L'ordre d'un pas est : validation, consommation, pression, collisions ordonnées, éjection, estimation du retour, alertes, état terminal, puis publication. Une session terminale ne rejoue pas sa conséquence et refuse toute mutation de gameplay ultérieure.
Snapshot et événements
Chaque pas accepté produit un seul DiveUpdate cohérent, associé à la session
et à la séquence du tick. Il contient :
- un
DiveSnapshotimmuable représentant l'état autoritaire après le pas ; - zéro ou plusieurs événements typés décrivant uniquement les faits survenus pendant ce pas.
Le snapshot contient les valeurs actuelles et maximales, leurs unités et leurs ratios pour batterie, oxygène, coque, pression et profondeur, ainsi que l'estimation du retour, les alertes, l'éjection et l'éventuel motif terminal. Il permet à un nouvel écran de se synchroniser sans rejouer l'historique.
Les événements, tels que AlertLevelChanged, HullDamaged,
ResourceDepleted et DiveTerminated, déclenchent les réactions ponctuelles :
audio, animation, journal ou transition applicative. Ils portent une identité
ou une séquence permettant aux adaptateurs d'éviter un double traitement. Un
événement ne remplace jamais une valeur persistante du snapshot.
La présentation et l'audio consomment DiveUpdate. Ils ne recalculent ni
seuils, ni dégâts, ni consommation, ni fin de plongée. Ils ne mutent jamais
directement DiveSession ou l'acteur physique ; toute action repart sous forme
de commande applicative.
Conséquences
- Une même suite de règles, commandes, télémétries et observations produit le même résultat dans les tests et dans le jeu.
- La pause est une absence de ticks, pas un multiplicateur de temps dispersé entre plusieurs systèmes.
- Le HUD peut afficher des valeurs exactes et des pourcentages sans posséder une copie concurrente des ressources.
- L'audio peut réagir une seule fois aux transitions tout en consultant l'état courant à tout moment.
- La conversion entre les types Godot et les données pures nécessite des adaptateurs explicites.
- Ajouter une nouvelle entrée ou sortie de simulation demande un contrat nommé et des tests, mais évite les clés implicites et les dépendances de scène.
- Les consommations indépendantes du tick de plongée, notamment les actions de port, restent des cas d'usage de campagne distincts.
Alternatives considérées
- Chaque système utilise
_process(delta): rejeté, car les règles dépendraient du framerate et leur ordre serait difficile à garantir. DiveSessionlit directement la physique et l'horloge Godot : rejeté, car le domaine ne serait plus pur ni testable sans scène.- Un dictionnaire unique de données par frame : rejeté, car commandes, mesures, états et événements auraient une temporalité implicite.
- Publier uniquement des événements : rejeté, car un écran ouvert plus tard devrait reconstruire l'état en rejouant l'historique.
- Publier uniquement un snapshot : rejeté, car les consommateurs devraient comparer deux états et réinventer les transitions ponctuelles.
- Rattraper les ticks après une pause ou un ralentissement important : rejeté, car cela provoquerait des consommations et dégâts invisibles ou des pics de calcul au retour.