Jugamos en la consola del navegador

imagen
Que hermoso es este mundo

console.log () es un buen método para enviar información de depuración a la consola web. Puede mostrar números, cadenas, matrices, objetos, funciones, texto sin formato, y además, puede agregar formato, color, fondo y muchos otros estilos a todo esto ... ¿Eso es todo? ¿Es eso todo lo que este método solo puede hacer? Bueno ... ¿Qué pasa con la implementación en la consola de un simple juego de plataformas, algoritmo de fundición de rayos o física de tejidos?

Para aquellos que vinieron aquí solo para ver, dejaré un enlace a la demostración al principio:

Github: github
Ejemplo en vivo: Demo

Abra la página, presione F12, estire la consola más y vaya a la demostración que le interese. Y, por supuesto, debe centrarse en la página para poder controlar la imagen en la consola.
Se recomienda ejecutar en cromo, pero puede haber un caso en el que los caracteres utilizados para mostrar la imagen no sean compatibles y se muestren como cuadrados. Como opción, descargue el código usted mismo y cambie los caracteres de salida a otros.

Y ahora un poco más sobre esto

Consola como un lienzo


Veamos el método console.log () y la consola como un todo, no como una herramienta de depuración, sino como un lienzo. Sí, como un lugar donde podemos dibujar un poco e incluso hacer que se mueva. Además, nadie canceló los caracteres Unicode.

Implementé métodos para "dibujar" en la consola, como métodos para trabajar con el lienzo. Pero en comparación con un lienzo real, la salida a la consola y, además, su rediseño impone grandes limitaciones, que desafortunadamente no se pueden eludir (al menos eso creo). Sobre ellos en orden.

Tamaño de píxel


Al dibujar en el lienzo, estamos tratando con píxeles, que tienen un tamaño, atención, píxeles en su monitor. Cuando se muestra en la consola, el "píxel" adquiere un concepto ligeramente diferente. Sí, con respecto a la consola, este es su píxel peculiar, pero con respecto al píxel real, ¿es solo un personaje especial, por ejemplo, tal? Pero hay una ligera restricción en los caracteres, o más bien una recomendación: debe tener una altura igual a la altura del salto de línea en la consola. Pero esto es solo si queremos obtener una imagen hermosa (tanto como sea posible).

Redibujando


Este es el principal problema desde La consola no está diseñada para actualizar datos con frecuencia. Los traemos allí, deducimos y deducimos. Hay console.clear () que lo borra, pero creo que se usa muy raramente. Ah, sí, pero no en mi caso, donde todo se basa en el hecho de que necesitas limpiarlo constantemente y mostrar el texto nuevamente. Aquí hay solo una consola.clear () causa su sobrecarga completa, que se acompaña de un parpadeo de milisegundos. Y si necesita volver a dibujarlo constantemente con cierta frecuencia, es mejor que las personas con hipersensibilidad no lo vean. Pero desafortunadamente no se puede hacer nada al respecto.

Color


Como escribí al principio, el formato se puede aplicar a la salida, pero en mi caso decidí no contentarme con una imagen en blanco y negro, lo que se puede lograr gracias a una amplia selección de caracteres Unicode.

Se pueden ver limitaciones más detalladas en ejemplos en vivo, los enlaces a los que dejé al final del artículo. Ya puede familiarizarse con ellos, pero por ahora describiré el proceso de dibujo en sí. Todo esto lo diseñé en una pequeña biblioteca con la que al final implemento juegos simples y el algoritmo Raycasting

Dibujamos en la consola


Adopté los nombres de los métodos, así como el proceso de dibujo del lienzo (más adelante describiré por qué) y esto es lo que terminó

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


Ahora incluimos este archivo en el archivo html, abrimos la consola y podemos probar varios métodos

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

Aquí está el resultado.

imagen

La salida es una imagen que había concebido, todo está dibujado en coordenadas y por analogía con el lienzo.

Buenos ejemplos


Mis planes eran hacer posible transferir un juego de lienzo normal a un juego en la consola lo más simple posible. Para esto, implementé los mismos métodos con cambios mínimos. ¿Qué significa esto? ¡Y el hecho de que para la implementación de cualquier juego que acabo de tomar listo en el lienzo, corrijo algunas líneas de código y comienza en la consola!

En realidad esto es lo que hice. Y lo primero que se me ocurrió (a excepción del pequeño cuadrado que se puede mover alrededor de la consola) fue implementar el algoritmo Raycasting
No escribí el algoritmo en sí, sino que simplemente lo presté aquí y, después de cambiar varias líneas, lo inicié en la consola.

imagen

Se ve impresionante, ¿no?

Aquí hay algunas capturas de pantalla más de lo que transferí a la consola.

imagen
Serpiente

imagen
Física del tejido que puede ser retorcido e incluso desgarrado.

Una vez más, la implementación de esta física de serpientes y tejidos no es mía, simplemente la adapté para la consola. En los archivos de origen, dejé enlaces a las fuentes originales.

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


All Articles