Pengantar
Pada musim semi lalu (2010), saya tertarik dengan dukungan yang meningkat dengan cepat untuk HTML5 dan teknologi terkait. Pada saat itu, saya dan seorang teman saling tantangan dalam kompetisi pengembangan game selama dua minggu untuk mengasah keterampilan pemrograman dan pengembangan kami serta mewujudkan ide game yang terus-menerus kami ajukan satu sama lain. Jadi, saya secara alami mulai menyertakan elemen HTML5 ke dalam entri kompetisi untuk mendapatkan pemahaman yang lebih baik tentang cara kerjanya dan dapat melakukan hal-hal yang hampir tidak mungkin menggunakan spesifikasi HTML sebelumnya.
Dari sekian banyak fitur baru di HTML5, dukungan yang semakin meningkat untuk tag kanvas memberi saya peluang menarik untuk menerapkan seni interaktif menggunakan JavaScript, yang mendorong saya untuk mencoba menerapkan game teka-teki yang sekarang disebut Entanglement. Saya telah membuat prototipe menggunakan bagian belakang kartu Settlers of Catan, jadi dengan menggunakan ini sebagai semacam cetak biru, ada tiga bagian penting untuk membuat kartu heksagonal di kanvas HTML5 untuk game web: menggambar segi enam, menggambar jalur, dan memutar kartu. Berikut ini penjelasan mendetail tentang cara saya menyelesaikan setiap hal tersebut dalam bentuknya saat ini.
Menggambar Heksagon
Dalam versi asli Entanglement, saya menggunakan beberapa metode gambar kanvas
untuk menggambar segi enam, tetapi bentuk game saat ini menggunakan drawImage()
untuk
menggambar tekstur yang dipangkas dari sheet sprite.

Saya menggabungkan gambar menjadi satu file sehingga hanya ada satu permintaan ke server, bukan sepuluh, dalam hal ini. Untuk menggambar segi enam yang dipilih ke kanvas, kita harus mengumpulkan alat terlebih dahulu: kanvas, konteks, dan gambar.
Untuk membuat kanvas, yang kita perlukan hanyalah tag kanvas dalam dokumen html seperti ini:
<canvas id="myCanvas"></canvas>
Saya memberinya ID agar kita dapat menariknya ke dalam skrip:
var cvs = document.getElementById('myCanvas');
Kedua, kita perlu mengambil konteks 2D untuk kanvas agar dapat mulai menggambar:
var ctx = cvs.getContext('2d');
Terakhir, kita memerlukan gambar. Jika diberi nama "tiles.png" di folder yang sama dengan halaman web, kita bisa mendapatkannya dengan:
var img = new Image();
img.src = 'tiles.png';
Setelah memiliki ketiga komponen tersebut, kita dapat menggunakan ctx.drawImage() untuk menggambar satu segi enam yang kita inginkan dari sheet sprite ke kanvas:
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
Dalam hal ini, kita menggunakan segi enam keempat dari kiri di baris atas. Selain itu, kita akan menggambarnya ke kanvas di sudut kiri atas, dengan mempertahankan ukuran yang sama dengan aslinya. Dengan asumsi bahwa segi enam memiliki lebar 400 piksel dan tinggi 346 piksel, semuanya akan terlihat seperti ini:
var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';
var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
Kita telah berhasil menyalin sebagian gambar ke kanvas dengan hasil sebagai berikut:

Menggambar Jalur
Setelah menggambar segi enam ke kanvas, kita ingin menggambar beberapa garis di atasnya. Pertama, kita akan melihat beberapa geometri terkait ubin segi enam. Kita ingin dua ujung garis per sisi dengan setiap ujung 1/4 dari ujung di sepanjang setiap tepi dan 1/2 tepi terpisah satu sama lain, seperti ini:

Kita juga menginginkan kurva yang bagus, jadi, dengan sedikit trial and error, saya menemukan bahwa, jika saya membuat garis tegak lurus dari tepi di setiap endpoint, persimpangan dari setiap pasangan endpoint di sekitar sudut segi enam tertentu akan membuat titik kontrol bezier yang bagus untuk endpoint yang diberikan:

Sekarang, kita memetakan endpoint dan titik kontrol ke bidang Kartesius yang sesuai dengan gambar kanvas dan kita siap kembali ke kode. Agar tetap sederhana, kita akan memulai dengan satu baris. Kita akan mulai dengan menggambar jalur dari endpoint kiri atas ke endpoint kanan bawah. Dengan gambar segi enam sebelumnya berukuran 400x346, endpoint atas kita akan berukuran 150 piksel dan 0 piksel ke bawah, singkatan (150, 0). Titik kontrolnya adalah (150, 86). Endpoint tepi bawah adalah (250, 346) dengan titik kontrol (250, 260):

Dengan koordinat yang ada, kita sekarang siap untuk mulai menggambar. Kita akan mulai dari awal dengan ctx.beginPath(), lalu beralih ke endpoint pertama menggunakan:
ctx.moveTo(pointX1,pointY1);
Kemudian, kita dapat menggambar garis itu sendiri menggunakan ctx.bezierCurveTo() sebagai berikut:
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
Karena kita ingin garis memiliki batas yang bagus, kita akan membuat goresan jalur ini dua kali menggunakan lebar dan warna yang berbeda setiap kali. Warna akan ditetapkan menggunakan properti ctx.strokeStyle dan lebar akan ditetapkan menggunakan ctx.lineWidth. Secara keseluruhan, menggambar baris pertama akan terlihat seperti ini:
var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.beginPath();
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
Sekarang kita memiliki ubin heksagonal dengan garis pertama yang berkelok-kelok:

Dengan memasukkan koordinat untuk 10 endpoint lainnya serta titik kontrol kurva bezier yang sesuai, kita dapat mengulangi langkah-langkah di atas dan mungkin membuat kartu seperti ini:

Memutar Kanvas
Setelah memiliki kartu, kita ingin dapat mengubahnya sehingga berbagai jalur dapat
diambil dalam game. Untuk melakukannya menggunakan kanvas, kita menggunakan ctx.translate()
dan ctx.rotate()
. Kita ingin kartu berputar di sekitar pusatnya, jadi langkah pertama kita
adalah memindahkan titik referensi kanvas ke pusat kartu heksagonal.
Untuk melakukannya, kita menggunakan:
ctx.translate(originX, originY);
Dengan originX akan menjadi setengah dari lebar ubin heksagonal dan originY akan menjadi setengah dari tinggi, sehingga kita mendapatkan:
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
Sekarang kita dapat memutar kartu dengan titik tengah baru. Karena segi enam memiliki enam sisi, kita ingin memutarnya dengan beberapa kelipatan Math.PI dibagi 3. Kita akan tetap membuatnya sederhana dan melakukan satu putaran searah jarum jam menggunakan:
ctx.rotate(Math.PI / 3);
Namun, karena segi enam dan garis menggunakan koordinat (0,0) lama sebagai asal, setelah selesai memutar, kita akan ingin menerjemahkan kembali sebelum menggambar. Jadi, sekarang kita memiliki:
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);
Menempatkan terjemahan dan rotasi di atas sebelum kode rendering menyebabkan kode tersebut sekarang merender ubin yang diputar:

Ringkasan
Di atas, saya telah menyoroti beberapa kemampuan yang ditawarkan HTML5 menggunakan tag kanvas, termasuk merender gambar, menggambar kurva bezier, dan memutar kanvas. Menggunakan tag kanvas HTML5 dan alat gambar JavaScript-nya untuk Entanglement terbukti menjadi pengalaman yang menyenangkan, dan saya menantikan banyak aplikasi dan game baru yang dibuat orang lain dengan teknologi terbuka dan yang sedang berkembang ini.
Referensi Kode
Semua contoh kode yang diberikan di atas digabungkan di bawah sebagai referensi:
var cvs = document.getElementById('myCanvas');
var ctx = cvs.getContext('2d');
var img = new Image();
img.src = 'tiles.png';
var originX = 200;
var originY = 173;
ctx.translate(originX, originY);
ctx.rotate(Math.PI / 3);
ctx.translate(-originX, -originY);
var sourceX = 1200;
var sourceY = 0;
var sourceWidth = 400;
var sourceHeight = 346;
var destinationX = 0;
var destinationY = 0;
var destinationWidth = 400;
var destinationHeight = 346;
ctx.drawImage(img, sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight);
ctx.beginPath();
var pointX1 = 150;
var pointY1 = 0;
var controlX1 = 150;
var controlY1 = 86;
var controlX2 = 250;
var controlY2 = 260;
var pointX2 = 250;
var pointY2 = 346;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 250;
pointY1 = 0;
controlX1 = 250;
controlY1 = 86;
controlX2 = 150;
controlY2 = 86;
pointX2 = 75;
pointY2 = 43;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 150;
pointY1 = 346;
controlX1 = 150;
controlY1 = 260;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 325;
pointY1 = 43;
controlX1 = 250;
controlY1 = 86;
controlX2 = 300;
controlY2 = 173;
pointX2 = 375;
pointY2 = 130;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 25;
pointY1 = 130;
controlX1 = 100;
controlY1 = 173;
controlX2 = 100;
controlY2 = 173;
pointX2 = 25;
pointY2 = 213;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();
ctx.beginPath();
pointX1 = 325;
pointY1 = 303;
controlX1 = 250;
controlY1 = 260;
controlX2 = 150;
controlY2 = 260;
pointX2 = 75;
pointY2 = 303;
ctx.moveTo(pointX1, pointY1);
ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, pointX2, pointY2);
ctx.lineWidth = 15;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.lineWidth = 10;
ctx.strokeStyle = '#786c44';
ctx.stroke();