Créer un mod
Images et clignotement
Déclarez les ressources, sélectionnez les images et vérifiez une animation sur le temps simulé.
Relier les fichiers à l’indication
À partir d’un signalModel fonctionnel, placez les SVG dans assets et déclarez tous les chemins utilisés avec construction(states = …). assets/closed.svg devient closed.svg dans le paquet. images choisit un chemin de ce catalogue à partir de l’indication complète ; il ne modifie aucune consigne de conduite.
construction(
states = listOf("closed.svg", "open.svg"),
size = 4, left = true
)
images { indication ->
when (indication.aspect) {
Aspect.Closed -> "closed.svg"
Aspect.Open -> "open.svg"
}
}size accepte 0 à 4 ; left et size sont les valeurs initiales de construction. Sans arguments, les valeurs sont size=0 et left=false. L’exemple choisit explicitement la gauche et la taille 4. Un catalogue conserve un ordre stable ; changer un libellé ne justifie pas de réordonner les images d’une version déjà utilisée.
Chaque modèle possède sa propre sélection d’images. Un ordinal commun à deux enums ne signifie pas un aspect commun. Déclarez les chemins complets relatifs au paquet et vérifiez leur présence lors de assembleReleaseMod.
Déclarer une cadence liée au jeu
package wiki.blinking
import nimby.*
enum class BlinkAspect { Closed, Flashing }
enum class BlinkReason { Unknown, Clear }
private val closedImage = steady("closed.svg")
private val flashingImage = blink(on = "on.svg", off = "off.svg", everyMs = 250)
val blinkingSignal = signalModel(
"monmod.clignotant", "Signal clignotant", "textures_clignotantes",
fallback = Indication(BlinkAspect.Closed, BlinkReason.Unknown)
) {
construction(states = listOf("closed.svg", "on.svg", "off.svg"))
rules {
if (fresh && routeKnown && block == Occupancy.Clear &&
!observation.forcedStop && !observation.lampFailed)
Indication(BlinkAspect.Flashing, BlinkReason.Clear)
else Indication(BlinkAspect.Closed, BlinkReason.Unknown)
}
appearance { if (it.aspect == BlinkAspect.Flashing) flashingImage else closedImage }
// Le clignotement ne donne pas de permission : ce modèle exige toujours l'arrêt.
driving { AutomaticDriving.stop() }
}
Ajoutez blinkingSignal à votre signalMod et fournissez closed.svg, on.svg et off.svg dans assets. Quand la règle renvoie Flashing, on et off alternent toutes les 250 millisecondes simulées. Ce modèle exige volontairement toujours l’arrêt : l’animation ne crée aucune permission.
| Déclaration | Résultat |
|---|---|
| steady(path) | Une image fixe, quel que soit le temps simulé. |
| blink(on, off, everyMs) | Deux images, chacune pendant 100 à 10000 ms simulées. 500 donne un cycle complet de 1000 ms. |
| appearance { … } | Retourne la description d’affichage de l’indication ; déclarez les descriptions réutilisées une seule fois. |
| frameAt(simulationMs) | Calcule localement l’image à un instant positif ou nul pour vos tests. |
La pause fige la phase ; l’accélération suit le temps simulé. Les signaux de même cadence sont synchronisés. Le mod n’a pas besoin de thread, de temporisation réelle ou de réévaluer ses règles pour chaque changement d’image. La fluidité effectivement visible dépend encore du rendu du jeu.
val animation = blink("on.svg", "off.svg", everyMs = 250)
check(animation.frameAt(0) == "on.svg")
check(animation.frameAt(249) == "on.svg")
check(animation.frameAt(250) == "off.svg")
check(animation.frameAt(500) == "on.svg")Distinguer règle, ressource et affichage
- Testez l’indication produite par rules avant de vérifier son image.
- Vérifiez que chaque chemin retourné est déclaré et présent dans le paquet.
- Testez les frontières de frameAt, puis observez pause et accélération dans une partie de test.
- Si l’image est correcte mais le mouvement inattendu, examinez la consigne driving et son motif séparément.