Écrans et moniteurs
Le terminal texte, le dessin en pixels dessous, les moniteurs, les clics et les Liaisons d'affichage : tout ce qu'un ordinateur affiche.
Un ordinateur affiche des choses de trois façons :
- son terminal, l'écran qui s'ouvre d'un clic droit : une grille de caractères, avec une couche de pixels dessous ;
- les moniteurs, des blocs qui montrent ce même terminal dans le monde, grands comme un mur, et sur lesquels les joueurs peuvent appuyer ;
- les Liaisons d'affichage de Create, qui recopient des lignes de texte choisies par le programme sur des Panneaux d'affichage, des Tubes Nixie et des pancartes.
Cette page présente chacun d'eux, la façon dont les joueurs cliquent dessus, et comment organiser un écran qui reste lisible et rapide.
Le terminal
Le terminal est une grille de caractères. Sa taille et son aspect dépendent de l'ordinateur :
| Ordinateur | Grille de texte | Aspect | Pixels pour gfx | Clics dans le terminal |
|---|---|---|---|---|
| Calculateur à tubes | 40 × 14 | encre sombre sur papier de téléscripteur | aucun | non |
| Ordinateur à transistors | 51 × 19 | phosphore vert | 306 × 171 | oui |
| Mini-ordinateur | 51 × 19 | phosphore ambre | 306 × 171 | oui |
| Micro-ordinateur | 51 × 19 | 16 couleurs | 306 × 171 | oui |
| Microcontrôleur | 40 × 12 | 16 couleurs | 240 × 108 | oui |
| Ordinateur moderne | 64 × 24 | 16 couleurs | 384 × 216 | oui |
Un programme obtient la taille avec term.get_size, qui renvoie {w = colonnes, h = lignes}. Servez-vous-en plutôt que de nombres fixes, et le même programme s'adapte à tous les ordinateurs.
Écrire du texte
Il y a deux façons de mettre du texte à l'écran, et la différence compte pour la mise en page :
printetwriteécrivent comme une machine à écrire : en bout de ligne, ils passent à la ligne suivante, une tabulation saute au prochain multiple de 4 colonnes, et en bas de l'écran tout remonte.printfinit par un retour à la ligne,writenon. C'est ce qu'il faut pour un journal ou des messages.term.writeécrit au curseur et ne passe jamais à la ligne ni ne fait défiler : ce qui dépasse du bord droit est perdu, et un\ndevient une espace. C'est ce qu'il faut pour une mise en page fixe : un tableau de bord, un menu, un bouton.
Le curseur est l'endroit où ira le prochain caractère. term.set_cursor(x, y) le déplace, (1, 1) étant le coin en haut à gauche, et term.get_cursor le relit.
term.clear()
term.set_cursor(1, 1)
print("Rapport de stock")
term.set_cursor(20, 3)
term.write("fer : 1450")
term.set_cursor(20, 4)
term.write("cuivre : 820")
term.set_cursor(1, 6)Rapport de stock
fer : 1450
cuivre : 820Pour effacer, term.clear vide toute la grille et term.clear_line la ligne du curseur. Aucun des deux ne déplace le curseur : faites suivre term.clear() de term.set_cursor(1, 1). term.scroll(n) fait monter tout le texte de n lignes (descendre si n est négatif). Les trois remplissent les cases effacées avec la couleur de fond en cours.
Couleurs
term.set_fg choisit la couleur des prochains caractères, term.set_bg celle des cases derrière eux. Les deux prennent un nombre de 0 à 15 : écrivez term.colors.red (14) plutôt que le nombre lui-même. Les 16 couleurs et leurs numéros sont dans Couleurs.
Une couleur s'applique à tout ce qui est écrit ensuite, jusqu'au prochain changement. term.clear peint chaque case avec le fond en cours : term.set_bg(term.colors.blue) suivi de term.clear() met tout l'écran en bleu.
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(2, 2)
term.set_fg(term.colors.yellow)
write("Chaudière ")
term.set_fg(term.colors.lime)
print("en marche")
term.set_cursor(2, 3)
term.set_fg(term.colors.yellow)
write("Eau ")
term.set_bg(term.colors.red)
term.set_fg(term.colors.white)
print(" VIDE ")
term.set_bg(term.colors.black)
L'aspect de chaque ordinateur
Un même programme n'a pas le même aspect selon l'âge de l'ordinateur. Ce petit tableau de bord utilise une barre de titre colorée, des valeurs en couleur, une alerte rouge et une jauge dessinée avec gfx :
local function ligne(y, nom, valeur, couleur)
term.set_cursor(2, y)
term.set_fg(term.colors.light_gray)
term.write(nom)
term.set_cursor(17, y)
term.set_fg(couleur)
term.write(valeur)
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.clear_line()
term.set_fg(term.colors.white)
term.write(" SALLE DES FOURS")
term.set_bg(term.colors.black)
ligne(3, "Charbon", "640", term.colors.lime)
ligne(4, "Minerai de fer", "96", term.colors.yellow)
ligne(5, "Lingots de fer", "1450", term.colors.white)
term.set_cursor(2, 7)
term.set_bg(term.colors.red)
term.set_fg(term.colors.white)
term.write(" COMBUSTIBLE BAS ")
term.set_bg(term.colors.black)
term.set_cursor(2, 9)
term.write("Combustible")
gfx.rect(85, 74, 182, 11, "white")
gfx.rect(87, 76, 40, 7, "orange", true)
Sur l'écran vert d'un Ordinateur à transistors :
local function ligne(y, nom, valeur, couleur)
term.set_cursor(2, y)
term.set_fg(term.colors.light_gray)
term.write(nom)
term.set_cursor(17, y)
term.set_fg(couleur)
term.write(valeur)
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.clear_line()
term.set_fg(term.colors.white)
term.write(" SALLE DES FOURS")
term.set_bg(term.colors.black)
ligne(3, "Charbon", "640", term.colors.lime)
ligne(4, "Minerai de fer", "96", term.colors.yellow)
ligne(5, "Lingots de fer", "1450", term.colors.white)
term.set_cursor(2, 7)
term.set_bg(term.colors.red)
term.set_fg(term.colors.white)
term.write(" COMBUSTIBLE BAS ")
term.set_bg(term.colors.black)
term.set_cursor(2, 9)
term.write("Combustible")
gfx.rect(85, 74, 182, 11, "white")
gfx.rect(87, 76, 40, 7, "orange", true)
Et sur l'écran ambre d'un Mini-ordinateur :
local function ligne(y, nom, valeur, couleur)
term.set_cursor(2, y)
term.set_fg(term.colors.light_gray)
term.write(nom)
term.set_cursor(17, y)
term.set_fg(couleur)
term.write(valeur)
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.clear_line()
term.set_fg(term.colors.white)
term.write(" SALLE DES FOURS")
term.set_bg(term.colors.black)
ligne(3, "Charbon", "640", term.colors.lime)
ligne(4, "Minerai de fer", "96", term.colors.yellow)
ligne(5, "Lingots de fer", "1450", term.colors.white)
term.set_cursor(2, 7)
term.set_bg(term.colors.red)
term.set_fg(term.colors.white)
term.write(" COMBUSTIBLE BAS ")
term.set_bg(term.colors.black)
term.set_cursor(2, 9)
term.write("Combustible")
gfx.rect(85, 74, 182, 11, "white")
gfx.rect(87, 76, 40, 7, "orange", true)
Sur les deux écrans à phosphore, les couleurs du texte et des cases sont ignorées : chaque caractère brille dans la couleur du phosphore sur un écran sombre, si bien que la barre de titre et l'alerte rouge perdent leur fond. Le dessin de gfx, lui, reste visible, car toutes les couleurs sauf le noir s'y allument. Un programme pour ces ordinateurs met en évidence avec des mots et des signes (>, [ ], !) ou avec des traits de gfx, jamais avec une couleur de fond seule. Une zone allumée par gfx derrière du texte le ferait disparaître : les caractères brillent de la même couleur.
Le Calculateur à tubes imprime sur du papier : encre sombre sur un rouleau crème, sans couleurs, sans gfx, et son papier ne se clique pas.
> stock Lingots de fer : 1450 Lingots de cuivre : 820 Charbon : 640
Le Microcontrôleur a le plus petit écran couleur (40 × 12), l'Ordinateur moderne le plus grand (64 × 24). Un programme qui lit term.get_size() et gfx.size() s'adapte aux deux.
Dessiner sous le texte
Sous les caractères se trouve une couche de pixels, 6 × 9 par case de caractère, sur laquelle dessine la bibliothèque gfx : lignes, formes, texte en pixels et sprites. L'écran montre trois couches, de bas en haut : la couleur de fond des cases, le dessin, puis les caractères. Le texte reste toujours lisible par-dessus un dessin, mais la couleur de fond d'une case est cachée là où quelque chose est dessiné.
La case de caractère (col, ligne) couvre les pixels de (col - 1) * 6 + 1 à col * 6 en largeur, et de (ligne - 1) * 9 + 1 à ligne * 9 en hauteur. Avec cela, un dessin s'aligne exactement sur le texte : un cadre autour d'un groupe de cases, une jauge sous une étiquette.
local function cadre(col, ligne, cols, lignes, couleur)
gfx.rect((col - 1) * 6 + 1, (ligne - 1) * 9 + 1, cols * 6, lignes * 9, couleur)
end
term.set_bg(term.colors.black)
term.clear()
gfx.clear()
cadre(2, 2, 26, 6, "light_gray")
term.set_cursor(4, 3)
term.write("Chaudière 1")
term.set_cursor(4, 5)
term.write("Eau")
local niveau = 0.8
cadre(12, 5, 14, 1, "gray") -- le cadre de la jauge : 14 cases de la ligne 5
gfx.rect(68, 38, math.floor(82 * niveau), 7, "blue", true)
term.set_cursor(13, 5)
term.write(math.round(niveau * 100) .. "%")
Le dessin reste quand le programme se termine, et term.clear n'y touche pas : effacez-le avec gfx.clear(). Tout ce qui concerne le dessin est sur la page gfx.
Les moniteurs
Un moniteur montre le terminal d'un ordinateur dans le monde, lisible par tous ceux qui sont autour (jusqu'à 64 blocs). Il y en a deux : le Moniteur cathodique (âge du transistor, cadre épais) et le Moniteur LCD (âge numérique, cadre fin). Voir Moniteurs pour le bloc lui-même.
Construire un écran
- Placez le moniteur de façon qu'il touche l'ordinateur, par n'importe quel côté du moniteur sauf son écran. Il suffit qu'un seul moniteur du mur touche l'ordinateur.
- Des moniteurs du même type (tous cathodiques ou tous LCD), côte à côte et tournés dans le même sens, se fondent en un seul écran. Le groupe doit former un rectangle plein, jusqu'à 8 de large et 6 de haut. Un groupe qui n'est pas un rectangle plein, ou qui est trop grand, ne fusionne pas : chaque moniteur fonctionne alors seul.
- Un mur de moniteurs ne doit toucher qu'un seul ordinateur.
L'écran ne gagne pas de caractères en grandissant : le terminal de l'ordinateur (51 × 19 sur un Micro-ordinateur) est mis à l'échelle pour tenir dans le mur et centré, en gardant ses proportions. Un mur d'autres proportions laisse des bandes sombres sur les côtés, ou en haut et en bas. Les écrans de 51 × 19 et 64 × 24 caractères sont proches du 16/9 : des murs de 5 × 3 ou 7 × 4 moniteurs leur vont bien. L'écran du Microcontrôleur est plus large : 2 × 1 ou 4 × 2.
Un programme peut lire la taille du mur, en blocs, avec monitor.size() (le moniteur est un appareil de l'ordinateur qu'il touche).
Le moniteur s'éteint tant que l'ordinateur ne tourne pas ou est en surcharge, et montre un écran vide tant qu'il est éteint. Les mises à jour partent vers tous les joueurs proches de l'ordinateur, et seules les lignes de pixels qui ont changé voyagent (voir gfx).
Cathodique ou LCD
| Moniteur cathodique | Moniteur LCD | |
|---|---|---|
| Cadre | épais (1/8 de bloc) | fin (1/16 de bloc) |
| Aspect | celui de l'ordinateur : vert, ambre ou couleurs | toujours les 16 couleurs |
| Calculateur à tubes | son papier s'affiche en phosphore vert | en couleurs |
Un Moniteur LCD affiche les couleurs choisies par le programme même sur un Ordinateur à transistors ou un Mini-ordinateur, dont l'écran les ignore. L'ordinateur garde toujours les couleurs ; seul l'écran décide de les montrer ou non.
Ouvrir le terminal, appuyer sur l'écran
Un clic droit sur n'importe quel moniteur d'un mur ouvre le terminal de son ordinateur, comme un clic sur l'ordinateur.
Quand le programme en cours attend des clics, un clic droit sur l'écran appuie dessus à la place : le programme reçoit un évènement click, exactement comme pour un clic dans le terminal (un crayon optique sur le cathodique, un écran tactile sur le LCD). Un programme attend des clics dès qu'il a appelé os.pull_event("click") ou os.pull_event() (n'importe quel évènement), et jusqu'à ce qu'il se termine. Un appui sur le cadre ou sur les bandes sombres autour de l'image ne fait rien.
Pour ouvrir le terminal d'un programme qui attend des clics, accroupissez-vous et faites un clic droit : accroupi, on ouvre toujours le terminal.
Clics et glissés
L'évènement de clic
Un clic dans le terminal, ou un appui sur un moniteur, envoie l'évènement click au programme :
| Champ | Valeur |
|---|---|
e.name | "click" |
e.x, e.y | la case de caractère cliquée, (1, 1) dans le coin en haut à gauche |
e.px, e.py | le pixel cliqué, pour des boutons dessinés avec gfx, (1, 1) dans le coin en haut à gauche |
e.button | 1 pour le bouton gauche, 2 pour le droit, 3 pour celui du milieu ; toujours 1 sur un moniteur |
e.source | "terminal" ou "monitor" |
while true do
local e = os.pull_event("click")
print(e.source .. " case " .. e.x .. "," .. e.y .. " pixel " .. e.px .. "," .. e.py)
endUtilisez e.x et e.y pour des boutons en texte, e.px et e.py pour des boutons dessinés en pixels. Les deux sont toujours fournis.
Quels écrans se cliquent
| Où | Qui peut cliquer |
|---|---|
| Le terminal d'un Ordinateur à transistors, Mini-ordinateur, Micro-ordinateur, Microcontrôleur ou Ordinateur moderne | tout joueur qui l'a ouvert, avec n'importe quel bouton |
| Le terminal du Calculateur à tubes | personne : son papier ne se clique pas |
| Un moniteur (cathodique ou LCD), sur n'importe quel ordinateur, Calculateur à tubes compris | tout joueur, d'un clic droit sur l'écran, pendant que le programme attend des clics |
Glissés
Dans le terminal, bouger la souris en gardant un bouton enfoncé envoie des évènements drag, avec les mêmes champs que click. Le glissé commence par un clic sur l'image et suit le bouton qui a été enfoncé. Il y a au plus un drag par tick, et seulement quand la souris arrive sur un nouveau pixel. Les moniteurs n'envoient que des clics, jamais de glissés.
Un glissé tombe rarement sur chaque pixel : reliez les points par une ligne, comme le fait l'exemple paint :
local avantX, avantY = 0, 0
while true do
local e = os.pull_event()
if e.name == "click" then
gfx.pixel(e.px, e.py, "white", 3)
avantX, avantY = e.px, e.py
elseif e.name == "drag" then
gfx.line(avantX, avantY, e.px, e.py, "white", 3) -- pas de trous quand la souris va vite
avantX, avantY = e.px, e.py
end
endLes évènements, leur ordre et tous les autres (touches, minuteries, redstone) sont dans Évènements.
Les Liaisons d'affichage
La Liaison d'affichage de Create lit un bloc source et écrit du texte sur une cible : un Panneau d'affichage, un Tube Nixie, une pancarte. Un ordinateur peut être la source. Le programme choisit les lignes avec la bibliothèque display :
display.set({"Fer : 1450", "Cuivre : 820"}) -- toutes les lignes d'un coup
display.line(3, "Fours : 4 en marche") -- une seule ligneC'est la troisième façon d'afficher du texte, et souvent la meilleure pour les joueurs éloignés : le tableau des départs d'une gare, un compteur de stock au-dessus d'un coffre à objet renforcé, une grande horloge en Tubes Nixie. Elle marche sur tous les ordinateurs, Calculateur à tubes compris, et n'a besoin d'aucun moniteur. Les lignes (16 au plus, de 128 caractères chacune) restent après la fin du programme, jusqu'au redémarrage ou à l'extinction de l'ordinateur. La Liaison d'affichage les relit toute seule toutes les demi-secondes.
Organiser un écran
Barres de titre et champs fixes
Un tableau de bord garde tout en place : une barre de titre sur la première ligne, une étiquette dans une colonne fixe, sa valeur dans une autre. Écrivez avec term.write pour que rien ne passe à la ligne ni ne défile, et complétez les valeurs jusqu'à une largeur fixe avec string.format, pour qu'une valeur plus courte efface la fin d'une plus longue : string.format("%6d", 82) donne " 82", et "%-10s" complète un texte par la droite.
Menus
Une liste dont une entrée est en surbrillance, déplacée avec les flèches et choisie avec Entrée, ou choisie directement d'un clic sur sa ligne. os.pull_event() sans nom reçoit à la fois les touches et les clics :
local recettes = {"Lingots de fer", "Lingots de cuivre", "Lingots de laiton", "Alliage d'andésite", "Mécanismes de précision"}
local choix = 2
local HAUT = 4
local function dessinerMenu()
for i, nom in ipairs(recettes) do
term.set_cursor(3, HAUT + i - 1)
if i == choix then
term.set_bg(term.colors.yellow)
term.set_fg(term.colors.black)
else
term.set_bg(term.colors.black)
term.set_fg(term.colors.white)
end
term.write(string.format(" %-26s", nom))
end
term.set_bg(term.colors.black)
term.set_fg(term.colors.white)
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(1, 1)
term.set_bg(term.colors.blue)
term.clear_line()
term.write(" COMMANDES D'ATELIER")
term.set_bg(term.colors.black)
term.set_cursor(3, HAUT + #recettes + 1)
term.set_fg(term.colors.light_gray)
term.write("Haut/Bas et Entrée, ou cliquez une ligne")
dessinerMenu()
while true do
local e = os.pull_event()
if e.name == "key" and e.key == "up" and choix > 1 then
choix = choix - 1
dessinerMenu()
elseif e.name == "key" and e.key == "down" and choix < #recettes then
choix = choix + 1
dessinerMenu()
elseif e.name == "click" and e.y >= HAUT and e.y < HAUT + #recettes then
choix = e.y - HAUT + 1
dessinerMenu()
elseif e.name == "key" and e.key == "enter" then
term.set_cursor(3, HAUT + #recettes + 3)
term.clear_line()
term.write("Commandé : " .. recettes[choix])
end
end
Sur un écran monochrome, la barre jaune ne se verrait pas : marquez plutôt la ligne choisie d'un signe comme >.
Boutons
Les boutons en texte sont des blocs de cases colorées. Faites-les hauts de trois lignes et larges : sur un moniteur, les joueurs appuient dessus de loin, avec le réticule. Le clic est dedans quand e.x et e.y tombent dans le bloc :
local boutons = {
{x = 3, y = 4, nom = "Lampes", face = "top", actif = true},
{x = 3, y = 8, nom = "Portail", face = "left", actif = false},
{x = 3, y = 12, nom = "Convoyeur", face = "back", actif = false},
}
local LARGEUR, HAUTEUR = 24, 3
local function dessiner(b)
local etat = "OFF"
if b.actif then
term.set_bg(term.colors.green)
etat = "ON"
else
term.set_bg(term.colors.red)
end
term.set_fg(term.colors.white)
for dy = 0, HAUTEUR - 1 do
term.set_cursor(b.x, b.y + dy)
term.write(string.rep(" ", LARGEUR))
end
term.set_cursor(b.x + 2, b.y + 1)
term.write(string.format("%-16s%4s", b.nom, etat))
term.set_bg(term.colors.black)
end
local function touche(b, x, y)
return x >= b.x and x < b.x + LARGEUR and y >= b.y and y < b.y + HAUTEUR
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(3, 2)
term.write("PANNEAU DE L'ATELIER")
for _, b in ipairs(boutons) do
dessiner(b)
end
while true do
local e = os.pull_event("click")
for _, b in ipairs(boutons) do
if touche(b, e.x, e.y) then
b.actif = not b.actif
rs.set(b.face, b.actif)
dessiner(b)
end
end
end
Le même test avec e.px et e.py fonctionne pour des boutons dessinés en pixels : voir les recettes de gfx.
Ne redessiner que ce qui change
Effacer tout l'écran et tout redessiner à chaque mise à jour le fait scintiller, coûte des instructions, et envoie toute l'image à chaque joueur. Dessinez une fois les parties fixes, gardez les valeurs déjà affichées, et n'écrivez une valeur que si elle a changé :
local champs = {
{nom = "Lingots de fer", y = 3},
{nom = "Lingots de cuivre", y = 4},
{nom = "Charbon", y = 5},
}
local affiche = {}
local function montrer(i, valeur)
if affiche[i] == valeur then
return
end
affiche[i] = valeur
term.set_cursor(20, champs[i].y)
term.write(string.format("%6d", valeur))
end
term.set_bg(term.colors.black)
term.clear()
term.set_cursor(2, 1)
term.write("COFFRE RENFORCÉ")
for _, c in ipairs(champs) do
term.set_cursor(2, c.y)
term.write(c.nom)
end
-- des mesures d'exemple ; dans un monde, lisez-les dans le coffre toutes les quelques secondes
local mesures = {{1450, 820, 640}, {1452, 820, 630}, {1460, 818, 82}}
for _, m in ipairs(mesures) do
for i = 1, #champs do
montrer(i, m[i])
end
sleep(1)
end
Ici, seules les valeurs qui ont bougé sont réécrites, et le remplissage de %6d efface les anciens chiffres quand 630 devient 82.
Voir aussi
term: toutes les fonctions de l'écran texte.gfx: le dessin en pixels, avec des recettes complètes (graphiques, cadrans, animations, boutons en pixels).display: les lignes pour les Liaisons d'affichage.Moniteurs: le bloc moniteur et sa méthodesize.Évènements:click,drag,keyet tous les autres évènements.Couleurs: les 16 couleurs, leurs numéros et leurs chiffres hexadécimaux.