Wir spielen in der Browserkonsole

Bild
Wie schön diese Welt ist

console.log () ist eine gute Methode, um Debugging-Informationen an die Webkonsole auszugeben. Sie können Zahlen, Zeichenfolgen, Arrays, Objekte, Funktionen, einfachen Text anzeigen und außerdem Formatierungen, Farben, Hintergründe und einige andere Stile hinzufügen ... Ist das alles? Ist das alles, was diese Methode alleine kann? Nun ... Was ist mit der Implementierung eines einfachen Plattformers, eines Ray-Casting-Algorithmus oder einer Gewebephysik in der Konsole?

Für diejenigen, die hierher gekommen sind, um zu sehen, werde ich ganz am Anfang einen Link zur Demo hinterlassen:

Github: Github
Live-Beispiel: Demo

Öffnen Sie die Seite, drücken Sie F12, strecken Sie die Konsole weiter und gehen Sie zu der Demo, die Sie interessiert. Und natürlich müssen Sie sich auf die Seite konzentrieren, um das Bild in der Konsole steuern zu können.
Es ist ratsam, in Chrom zu arbeiten, aber es kann vorkommen, dass die zur Anzeige des Bildes verwendeten Zeichen nicht unterstützt und als Quadrate angezeigt werden. Laden Sie optional den Code selbst herunter und ändern Sie die Ausgabezeichen in andere.

Und jetzt ein bisschen mehr dazu

Konsole als Leinwand


Betrachten wir die console.log () -Methode und die Konsole als Ganzes, nicht als Debugging-Tool, sondern als Zeichenfläche. Ja, als Ort, an dem wir ein wenig zeichnen und es sogar bewegen können. Außerdem hat niemand Unicode-Zeichen abgebrochen.

Ich habe Methoden zum "Zeichnen" in der Konsole implementiert, wie Methoden zum Arbeiten mit Canvas. Aber im Vergleich zu einer echten Leinwand bringt die Ausgabe auf der Konsole und darüber hinaus das Neuzeichnen große Einschränkungen mit sich, die leider nicht umgangen werden können (zumindest denke ich das). Über sie in Ordnung.

Pixelgröße


Beim Zeichnen auf Leinwand haben wir es mit Pixeln zu tun, die eine Größe, Aufmerksamkeit und Pixel auf Ihrem Monitor haben! Bei der Anzeige in der Konsole nimmt das „Pixel“ ein etwas anderes Konzept an. Ja, in Bezug auf die Konsole ist dies das eigentümliche Pixel, aber in Bezug auf das reale Pixel ist es zum Beispiel nur ein Sonderzeichen, wie z. Es gibt jedoch eine geringfügige Einschränkung der Zeichen oder vielmehr eine Empfehlung: Die Höhe sollte der Höhe des Zeilenumbruchs in der Konsole entsprechen. Dies ist jedoch nur möglich, wenn wir ein schönes Bild erhalten möchten (so viel wie möglich).

Neu zeichnen


Dies ist das Hauptproblem seitdem Die Konsole ist nicht dafür ausgelegt, Daten häufig zu aktualisieren. Wir bringen sie dorthin, ableiten und ableiten. Es gibt console.clear (), das es löscht, aber ich denke, es wird sehr selten verwendet. Oh ja, aber nicht in meinem Fall, wo alles auf der Tatsache basiert, dass Sie es ständig reinigen und den Text erneut anzeigen müssen. Hier ist nur eine Konsole. Clear () verursacht die vollständige Überlastung, die von einem Millisekunden-Blinken begleitet wird. Und wenn Sie es ständig mit einer gewissen Häufigkeit neu zeichnen müssen, ist es für Menschen mit Überempfindlichkeit besser, es nicht anzusehen. Aber leider kann nichts dagegen unternommen werden.

Farbe


Wie ich zu Beginn geschrieben habe, kann die Formatierung auf die Ausgabe angewendet werden, aber in meinem Fall habe ich beschlossen, mich nicht mit einem Schwarzweißbild zufrieden zu geben, was dank einer großen Auswahl an Unicode-Zeichen erreicht werden kann.

Detailliertere Einschränkungen sind in Live-Beispielen zu sehen, deren Links ich am Ende des Artikels hinterlassen habe. Sie können sich bereits mit ihnen vertraut machen, aber ich werde zunächst den Zeichenprozess selbst beschreiben. All dies habe ich in einer kleinen Bibliothek entworfen, mit der ich am Ende einfache Spiele und den Raycasting-Algorithmus implementiere

Wir zeichnen in der Konsole


Ich habe die Namen der Methoden sowie den Zeichenprozess von der Leinwand übernommen (später werde ich beschreiben, warum) und das ist, was endete

engine.js
const 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(); //2D to String const map = this.map.map(val => val.join('')).join('\n'); console.log(map); } clear() { console.clear(); } } 


Jetzt fügen wir diese Datei in die HTML-Datei ein, öffnen die Konsole und können verschiedene Methoden ausprobieren

 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() 

Hier ist das Ergebnis

Bild

Die Ausgabe ist ein Bild, das ich konzipiert hatte, alles ist in Koordinaten und in Analogie zur Leinwand gezeichnet.

Coole Beispiele


Meine Pläne waren es, es möglich zu machen, ein normales Canvas-Spiel so einfach wie möglich auf ein Konsolenspiel zu übertragen. Dafür habe ich die gleichen Methoden mit minimalen Änderungen implementiert. Was bedeutet das? Und die Tatsache, dass ich für die Implementierung eines Spiels, das ich einfach fertig auf Leinwand nehme, ein paar Codezeilen korrigiere und es in der Konsole beginnt!

Eigentlich habe ich das getan. Und das erste, was mir einfiel (mit Ausnahme des kleinen Quadrats, das um die Konsole verschoben werden kann), war die Implementierung des Raycasting-Algorithmus
Ich habe den Algorithmus nicht selbst geschrieben, sondern ihn einfach hier ausgeliehen und nach dem Ändern mehrerer Zeilen in der Konsole gestartet.

Bild

Es sieht beeindruckend aus, nicht wahr?

Hier sind einige weitere Screenshots von dem, was ich auf die Konsole übertragen habe.

Bild
Schlange

Bild
Physik des Gewebes, das zuckt und sogar zerrissen werden kann

Auch hier ist die Implementierung dieser Schlangen- und Gewebephysik nicht meine, ich habe sie nur für die Konsole angepasst. In den Quelldateien habe ich Links zu den Originalquellen hinterlassen.

Source: https://habr.com/ru/post/de452442/


All Articles