Create: Computing AgesDoc Brass
Piloter le monde

É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 :

OrdinateurGrille de texteAspectPixels pour gfxClics dans le terminal
Calculateur à tubes40 × 14encre sombre sur papier de téléscripteuraucunnon
Ordinateur à transistors51 × 19phosphore vert306 × 171oui
Mini-ordinateur51 × 19phosphore ambre306 × 171oui
Micro-ordinateur51 × 1916 couleurs306 × 171oui
Microcontrôleur40 × 1216 couleurs240 × 108oui
Ordinateur moderne64 × 2416 couleurs384 × 216oui

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 :

  • print et write é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. print finit par un retour à la ligne, write non. 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 \n devient 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.

Brass
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)
Écran
Rapport de stock

                   fer : 1450
                   cuivre : 820

Pour 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.

Brass
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)
Écran
Écran

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 :

Brass
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)
Écran
Écran

Sur l'écran vert d'un Ordinateur à transistors :

Brass
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)
Écran
Écran

Et sur l'écran ambre d'un Mini-ordinateur :

Brass
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)
Écran
Écran

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.

Écran
> 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.

Brass
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) .. "%")
Écran
Écran

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 cathodiqueMoniteur LCD
Cadreépais (1/8 de bloc)fin (1/16 de bloc)
Aspectcelui de l'ordinateur : vert, ambre ou couleurstoujours les 16 couleurs
Calculateur à tubesson papier s'affiche en phosphore verten 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 :

ChampValeur
e.name"click"
e.x, e.yla case de caractère cliquée, (1, 1) dans le coin en haut à gauche
e.px, e.pyle pixel cliqué, pour des boutons dessinés avec gfx, (1, 1) dans le coin en haut à gauche
e.button1 pour le bouton gauche, 2 pour le droit, 3 pour celui du milieu ; toujours 1 sur un moniteur
e.source"terminal" ou "monitor"
Brass
while true do
  local e = os.pull_event("click")
  print(e.source .. " case " .. e.x .. "," .. e.y .. " pixel " .. e.px .. "," .. e.py)
end

Utilisez 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 modernetout joueur qui l'a ouvert, avec n'importe quel bouton
Le terminal du Calculateur à tubespersonne : son papier ne se clique pas
Un moniteur (cathodique ou LCD), sur n'importe quel ordinateur, Calculateur à tubes compristout 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 :

Brass
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
end

Les é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 :

Brass
display.set({"Fer : 1450", "Cuivre : 820"})      -- toutes les lignes d'un coup
display.line(3, "Fours : 4 en marche")           -- une seule ligne

C'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.

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 :

Brass
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
Écran
Écran

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 :

Brass
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
Écran
Écran

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é :

Brass
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
Écran
Écran

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éthode size.
  • Évènements : click, drag, key et tous les autres évènements.
  • Couleurs : les 16 couleurs, leurs numéros et leurs chiffres hexadécimaux.