Comme ce monde est beauconsole.log () est une bonne méthode pour afficher des informations de débogage sur la console Web. Vous pouvez afficher des nombres, des chaînes, des tableaux, des objets, des fonctions, du texte brut, et en plus, vous pouvez ajouter une mise en forme, une couleur, un arrière-plan et pas mal d'autres styles à tout cela ... Et c'est tout? Est-ce tout ce que cette méthode seule peut faire? Eh bien ... Qu'en est-il de l'implémentation dans la console d'un simple jeu de plateforme, d'un algorithme de lancer de rayons ou de la physique des tissus?
Pour ceux qui sont venus ici juste pour voir, je vais laisser un lien vers la démo au tout début:
Github: githubExemple en direct: démoOuvrez la page, appuyez sur F12, élargissez la console et accédez à la démo qui vous intéresse. Et bien sûr, vous devez vous concentrer sur la page afin de pouvoir contrôler l'image dans la console.
Il est conseillé de fonctionner en chrome, mais il peut y avoir un cas où les caractères utilisés pour afficher l'image peuvent ne pas être pris en charge et affichés sous forme de carrés. En option, téléchargez le code pour vous-même et remplacez les caractères de sortie par d'autres.
Et maintenant un peu plus à ce sujet
La console comme toile
Examinons la méthode console.log () et la console dans son ensemble, non pas comme un outil de débogage, mais comme un canevas. Oui, comme un endroit où l'on peut dessiner un peu et même le faire bouger. De plus, personne n'a annulé les caractères Unicode.
J'ai implémenté des méthodes de "dessin" dans la console, comme des méthodes de travail avec le canevas. Mais par rapport à un vrai canevas, la sortie vers la console et, de surcroît, son redessin impose de grandes limitations, qui ne peuvent malheureusement pas être contournées (du moins je le pense). À propos d'eux dans l'ordre.
Taille de pixel
Lorsque nous dessinons sur une toile, nous avons affaire à des pixels, qui ont une taille, une attention, un pixel sur votre moniteur! Lorsqu'il est affiché dans la console, le «pixel» prend un concept légèrement différent. Oui, en ce qui concerne la console, c'est son pixel particulier, mais en ce qui concerne le pixel réel, est-ce juste un caractère spécial, par exemple tel? Mais il y a une légère restriction sur les caractères, ou plutôt une recommandation: elle doit avoir une hauteur égale à la hauteur du saut de ligne dans la console. Mais ce n'est que si nous voulons obtenir une belle image (autant que possible).
Redessiner
C'est le principal problème car La console n'est pas conçue pour mettre à jour les données fréquemment. Nous les y amenons, déduisons et déduisons. Il y a console.clear () qui l'efface, mais je pense qu'il est très rarement utilisé. Oh oui, mais pas dans mon cas, où tout est basé sur le fait que vous devez constamment le nettoyer et afficher à nouveau le texte. Voici une seule console.clear () provoque sa surcharge complète, qui s'accompagne d'un clignotement en millisecondes. Et si vous devez le redessiner constamment avec une certaine fréquence, il vaut mieux que les personnes hypersensibles ne le regardent pas. Mais malheureusement, rien ne peut être fait à ce sujet.
La couleur
Comme je l'ai écrit au début, le formatage peut être appliqué à la sortie, mais dans mon cas, j'ai décidé de ne pas me contenter d'une image en noir et blanc, ce qui peut être obtenu grâce à une large sélection de caractères Unicode.
Des limitations plus détaillées peuvent être vues sur des exemples en direct, les liens vers lesquels j'ai laissé à la fin de l'article. Vous pouvez déjà vous familiariser avec eux, mais pour l'instant je vais décrire le processus de dessin lui-même. J'ai conçu tout cela dans une petite bibliothèque avec laquelle à la fin j'implémente des jeux simples et l'algorithme Raycasting
Nous dessinons dans la console
J'ai adopté les noms des méthodes, ainsi que le processus de dessin à partir de la toile (je décrirai plus tard pourquoi) et c'est ce qui a fini par
engine.jsconst canvas = { width: 70, height: 40, getContext(type) { if (type != '2d') { return console.log('Only 2d'); } return new Context2D(type); } } class Context2D { constructor(type) { this.fillStyle = '?'; this.emptyStyle = '?'; this.map = []; for (let i = 0; i < canvas.height; i++) { this.map[i] = []; for (let j = 0; j < canvas.width; j++) { this.map[i][j] = this.emptyStyle; } } this.path = []; this.clear(); } fillRect(x, y, width, height) { for (let i = y; i < y + height; i++) { for (let j = x; j < x + width; j++) { if (!this.map[i]) break; this.map[i][j] = this.fillStyle; } } this.draw(); } strokeRect(x, y, width, height) { for (let j = x; j < x + width; j++) { this.map[y][j] = this.fillStyle; this.map[y + height - 1][j] = this.fillStyle; } for (let i = y + 1; i < y + height - 1; i++) { this.map[i][x] = this.fillStyle; this.map[i][x + width - 1] = this.fillStyle; } this.draw(); } clearRect(x, y, width, height) { for (let i = y; i < y + height; i++) { for (let j = x; j < x + width; j++) { this.map[i][j] = this.emptyStyle; } } this.draw(); } beginPath() { this.path = []; } moveTo(x, y) { this.path.push([Math.round(x), Math.round(y), true]); } lineTo(x, y) { this.path.push([Math.round(x), Math.round(y)]); } closePath() { if (!this.path.length) return false this.path.push([this.path[0][0], this.path[0][1]]); } stroke() { const path = this.path; for (let i = 0; i < path.length - 1; i++) { const x0 = path[i][0]; const y0 = path[i][1]; const x1 = path[i+1][0]; const y1 = path[i+1][1]; this.fillPixel(x1, y1); if (path[i+1][2]) continue; const deltaX = Math.abs(x1 - x0); const deltaY = Math.abs(y1 - y0); const signX = x0 < x1 ? 1 : -1; const signY = y0 < y1 ? 1 : -1; let error = deltaX - deltaY; let x = x0; let y = y0; while(x !== x1 || y !== y1) { this.fillPixel(x, y) const error2 = error * 2; if (error2 > -deltaY) { error -= deltaY; x += signX; } if (error2 < deltaX) { error += deltaX; y += signY; } } } this.draw(); } fillPixel(x, y) { if (!this.map[y]) return false; this.map[y][x] = this.fillStyle; } arc(x1, y1, r) { let x = 0; let y = r; let delta = 1 - 2 * r; let error = 0; while (y >= 0) { this.moveTo(x1 + x, y1 + y); this.moveTo(x1 + x, y1 - y); this.moveTo(x1 - x, y1 + y); this.moveTo(x1 - x, y1 - y); error = 2 * (delta + y) - 1; if (delta < 0 && error <= 0) { delta += 2 * ++x + 1; continue; } if (delta > 0 && error > 0) { delta -= 2 * --y + 1; continue; } delta += 2 * (++x - y--); } this.draw() } draw() { this.clear();
Maintenant, nous incluons ce fichier dans le fichier html, ouvrons la console et pouvons essayer plusieurs méthodes
canvas.width = 70 canvas.height = 30 const ctx = canvas.getContext('2d') ctx.beginPath() ctx.moveTo(30, 5) ctx.lineTo(30, 25) ctx.moveTo(30, 15) ctx.lineTo(35, 13) ctx.lineTo(38, 13) ctx.lineTo(40, 16) ctx.lineTo(40, 25) ctx.stroke()
Voici le résultat

La sortie est une image que j'avais conçue, tout est dessiné en coordonnées et par analogie avec la toile.
Exemples sympas
Mon projet était de permettre de transférer un jeu de toile classique vers un jeu en console aussi simple que possible. Pour cela, j'ai implémenté les mêmes méthodes avec un minimum de changements. Qu'est-ce que cela signifie? Et le fait que pour l'implémentation de n'importe quel jeu je prends juste du ready-made sur canvas, je corrige quelques lignes de code et ça démarre en console!
En fait, c'est ce que j'ai fait. Et la première chose qui m'est venue à l'esprit (à l'exception du petit carré qui peut être déplacé autour de la console) a été d'implémenter l'algorithme Raycasting
Je n'ai pas écrit l'algorithme lui-même, mais je l'ai simplement prêté
ici et lancé plusieurs lignes dans la console en changeant quelques lignes.

Ça a l'air impressionnant, non?
Voici quelques captures d'écran supplémentaires de ce que j'ai transféré sur la console.
Serpent
Physique des tissus pouvant se contracter et même se déchirerEncore une fois, l'implémentation de cette physique des serpents et des tissus n'est pas la mienne, je viens de l'adapter pour la console. Dans les fichiers source, j'ai laissé des liens vers les sources originales.