Betapa indahnya dunia iniconsole.log () adalah metode yang baik untuk menampilkan informasi debug ke konsol web. Anda dapat menampilkan angka, string, array, objek, fungsi, teks biasa, dan di samping itu, Anda dapat menambahkan pemformatan, warna, latar belakang, dan beberapa gaya lainnya untuk semua ini ... Apakah hanya itu yang terjadi? Apakah hanya itu yang dapat dilakukan oleh semua metode ini? Nah ... Bagaimana dengan implementasi di konsol platformer sederhana, algoritma pengecoran Ray, atau fisika jaringan?
Bagi mereka yang datang ke sini hanya untuk melihat, saya akan meninggalkan tautan ke demo di awal:
Github: githubContoh langsung: DemoBuka halaman, tekan F12, rentangkan konsol lebih luas dan pergi ke demo yang menarik minat Anda. Dan tentu saja Anda harus fokus pada halaman agar dapat mengontrol gambar di konsol.
Dianjurkan untuk berjalan dalam chrome, tetapi mungkin ada kasus di mana karakter yang digunakan untuk menampilkan gambar mungkin tidak didukung dan ditampilkan sebagai kotak. Sebagai pilihan, unduh kodenya untuk Anda sendiri dan ubah karakter keluaran ke yang lain.
Dan sekarang sedikit lebih banyak tentang ini
Konsol sebagai kanvas
Mari kita lihat metode console.log () dan konsol secara keseluruhan, bukan sebagai alat debugging, tetapi sebagai kanvas. Ya, sebagai tempat di mana kita bisa menggambar sedikit dan bahkan membuatnya bergerak. Selain itu, tidak ada yang membatalkan karakter Unicode.
Saya menerapkan metode untuk "menggambar" di konsol, seperti metode untuk bekerja dengan kanvas. Tetapi dibandingkan dengan kanvas nyata, output ke konsol dan, apalagi, penggambarannya memberlakukan batasan besar, yang sayangnya tidak dapat dielakkan (setidaknya saya pikir begitu). Tentang mereka secara berurutan.
Ukuran piksel
Saat menggambar di atas kanvas, kita berhadapan dengan piksel, yang memiliki ukuran, perhatian, piksel pada monitor Anda! Ketika ditampilkan di konsol, "pixel" mengambil konsep yang sedikit berbeda. Ya, sehubungan dengan konsol, ini adalah pixelnya yang aneh, tetapi sehubungan dengan pixel yang sebenarnya, apakah itu hanya karakter khusus, misalnya seperti itu? Tetapi ada sedikit batasan pada karakter, atau lebih tepatnya sebuah rekomendasi: itu harus memiliki ketinggian yang sama dengan ketinggian garis putus-putus di konsol. Tapi ini hanya jika kita ingin mendapatkan gambar yang indah (sebanyak mungkin).
Menggambar ulang
Ini adalah masalah utama sejak itu Konsol tidak dirancang untuk sering memperbarui data. Kami membawa mereka ke sana, menyimpulkan dan menyimpulkan. Ada console.clear () yang membersihkannya, tapi saya pikir ini sangat jarang digunakan. Oh ya, tetapi tidak dalam kasus saya, di mana semuanya didasarkan pada kenyataan bahwa Anda harus terus-menerus membersihkannya dan menampilkan teks lagi. Berikut ini hanya satu console.clear () menyebabkan kelebihannya, yang disertai dengan flashing milidetik. Dan jika Anda perlu menggambar ulang secara konstan dengan frekuensi tertentu, maka orang-orang dengan hipersensitivitas lebih baik tidak melihatnya. Namun sayangnya tidak ada yang bisa dilakukan untuk itu.
Warna
Seperti yang saya tulis di awal, pemformatan dapat diterapkan pada output, tetapi dalam kasus saya saya memutuskan untuk tidak puas dengan gambar hitam dan putih, yang dapat dicapai berkat berbagai pilihan karakter Unicode.
Batasan yang lebih rinci dapat dilihat pada contoh langsung, tautan yang saya tinggalkan di akhir artikel. Anda sudah dapat membiasakan diri dengan mereka, tetapi untuk sekarang saya akan menjelaskan proses menggambar itu sendiri. Semua ini saya rancang di perpustakaan kecil yang pada akhirnya saya mengimplementasikan game sederhana dan algoritma Raycasting
Kami menggambar di konsol
Saya mengadopsi nama-nama metode, serta proses menggambar dari kanvas (nanti saya akan menjelaskan mengapa) dan inilah yang akhirnya
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();
Sekarang kita sertakan file ini dalam file html, buka konsol dan dapat mencoba beberapa metode
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()
Inilah hasilnya

Keluarannya adalah gambar yang saya bayangkan, semuanya digambar dalam koordinat dan analogi dengan kanvas.
Contoh keren
Rencana saya adalah memungkinkan untuk mentransfer game kanvas biasa ke game di konsol sesederhana mungkin. Untuk ini, saya menerapkan metode yang sama dengan perubahan minimal. Apa artinya ini? Dan fakta bahwa untuk implementasi game apa pun saya hanya siap pakai di atas kanvas, saya memperbaiki beberapa baris kode dan itu dimulai di konsol!
Sebenarnya inilah yang saya lakukan. Dan hal pertama yang terjadi pada saya (kecuali untuk kotak kecil yang dapat dipindahkan di sekitar konsol) adalah menerapkan algoritma Raycasting
Saya tidak menulis algoritme itu sendiri, tetapi hanya meminjamkannya di
sini dan, setelah mengubah beberapa baris, saya meluncurkannya di konsol.

Terlihat mengesankan, bukan?
Berikut adalah beberapa tangkapan layar dari apa yang saya transfer ke konsol.
Ular
Fisika jaringan yang dapat berkedut dan bahkan robekSekali lagi, implementasi ular dan fisika jaringan ini bukan milik saya, saya hanya mengadaptasinya untuk konsol. Di file sumber, saya meninggalkan tautan ke sumber asli.