7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. JavaScript

Membuat Teka-Teki dengan HTML5 Canvas

Read Time: 18 mins

Indonesian (Bahasa Indonesia) translation by Hasannudin Amin (you can also view the original English article)

Dalam tutorial ini kita akan bekerja dengan HTML5 canvas dan Javascript untuk membuat permainan teka-teki yang dinamis. Hasilnya akan menjadi teka-teki yang bekerja dengan gambar yang diberikan, dan memiliki tingkat kesulitan yang fleksibel yang mudah disesuaikan.


Teka-teki dengan Canvas HTML5 Lengkap

Berikut adalah teka-teki yang akan kita bangun:

Final HTML5 PuzzleFinal HTML5 PuzzleFinal HTML5 Puzzle

Klik untuk main

Beberapa catatan:

  • Kompatibilitas lintas-browser: Teka-teki ini telah diuji dan berfungsi di semua versi Safari, Firefox, dan Chrome yang mendukung elemen canvas.
  • Seluler: Kode yang disediakan di sini berfungsi di browser desktop yang disebutkan di atas dan tidak dioptimalkan untuk seluler. Teka-teki akan memuat dan ditampilkan dengan baik, tetapi karena sentuhan dan perilaku seret di browser seluler, diperlukan pengoptimalan agar berfungsi dengan benar. Mengoptimalkan teka-teki untuk seluler ini akan dibahas dalam tutorial mendatang.
  • Kesulitan yang Dapat Diatur: Kode berisi konstanta, PUZZLE_DIFFICULTY, yang menentukan jumlah potongan. Dalam demo di atas, ini diatur ke 4, memberikan puzzle 4x4. Kami dapat dengan mudah mengubah ini - misalnya, versi ini memiliki PUZZLE_DIFFICULTY dari 10.

Memulai

Untuk memulai, buatlah direktori untuk proyek tersebut. Tempatkan gambar di direktori yang ingin Anda gunakan sebagai teka-teki Anda. Citra ramah web apa pun akan dilakukan, dan itu bisa menjadi ukuran apa pun yang diinginkan hati Anda - pastikan saja itu pas dengan lipatan jendela browser Anda.


Langkah 1: Membuat Template HTML

Buka file baru menggunakan editor teks favorit Anda dan simpan di dalam direktori proyek Anda, di sebelah gambar Anda. Selanjutnya, isi template HTML dasar ini.

Yang perlu kita lakukan di sini adalah membuat template HTML5 standar yang berisi satu tag canvas dengan id "canvas". Kami akan menulis listener onload di tag body yang akan memanggil fungsi init() kami saat diaktifkan.

Sekarang mulailah dengan menempatkan kursor Anda di dalam tag script. Mulai sekarang semuanya sudah javascript. Dengan pengecualian variabel awal, saya akan mengatur bagian berdasarkan fungsi. Pertama menunjukkan Anda kode dan kemudian menjelaskan logika.

Siap? Mari kita lakukan dengan benar!


Langkah 2: Menyiapkan Variabel

Mari siapkan variabel dan lihat masing-masing.

Pertama-tama, kami memiliki beberapa konstanta: PUZZLE_DIFFICULTY dan PUZZLE_HOVER_TINT. Konstanta PUZZLE_DIFFICULTY menyimpan jumlah potongan dalam teka-teki kami. Dalam aplikasi ini, baris dan kolom selalu cocok, jadi dengan mengatur PUZZLE_DIFFICULTY ke 4, kita mendapatkan 16 buah puzzle secara total. Peningkatan ini meningkatkan kesulitan teka-teki.

Selanjutnya adalah serangkaian variabel:

  • _canvas dan _stage akan memegang referensi ke kanvas dan konteks gambarnya masing-masing. Kami melakukan ini sehingga kami tidak perlu menuliskan seluruh kueri setiap kali kami menggunakannya. Dan kami akan banyak menggunakannya!
  • _img akan menjadi referensi ke gambar yang dimuat, yang akan kita salin piksel dari seluruh aplikasi.
  • _puzzleWidth, _puzzleHeight, _pieceWidth, dan _pieceHeight akan digunakan untuk menyimpan dimensi dari keseluruhan puzzle dan setiap potongan puzzle individual. Kami mengatur ini sekali untuk mencegah menghitung mereka berulang kali setiap kali kita membutuhkannya.
  • _currentPiece memegang referensi ke bagian yang sedang diseret.
  • _currentDropPiece memegang referensi ke bagian yang saat ini pada posisi yang akan dijatuhkan. (Dalam demo, bagian ini disorot hijau.)
  • _mouse adalah referensi yang akan memegang posisi x dan y mouse saat ini. Ini akan diperbarui ketika teka-teki diklik untuk menentukan bagian mana yang disentuh, dan ketika sebuah bagian diseret untuk menentukan bagian mana yang melayang.

Sekarang, ke fungsi kami.


Langkah 3: Fungsi init()

Hal pertama yang ingin kami lakukan dalam aplikasi Anda adalah memuat gambar untuk teka-teki. Objek gambar pertama kali dipakai dan disetel ke variabel _img kita. Selanjutnya, kita load event yang kemudian akan mengaktifkan fungsi onImage() kita ketika gambar telah selesai dimuat. Terakhir kita mengatur sumber gambar, yang memicu memuat.


Langkah 4: Fungsi onImage()

Sekarang setelah gambar berhasil dimuat, kita dapat mengatur sebagian besar variabel yang dinyatakan sebelumnya. Kami melakukan ini di sini karena kami sekarang memiliki informasi tentang gambar dan dapat menetapkan nilai-nilai kami dengan tepat.

Hal pertama yang kami lakukan adalah menghitung ukuran setiap keping puzzle. Kami melakukan ini dengan membagi nilai PUZZLE_DIFFICULTY dengan lebar dan tinggi dari gambar yang dimuat. Kami juga memangkas lemak dari bagian tepi untuk memberi kami sejumlah angka yang bagus untuk digunakan dan memastikan bahwa setiap bagian dapat menukar ‘slot’ dengan orang lain dengan tepat.

Selanjutnya kami menggunakan nilai potongan puzzle baru kami untuk menentukan ukuran total teka-teki dan mengatur nilai-nilai ini ke _puzzleWidth dan _puzzleHeight.

Terakhir, kami membatalkan beberapa fungsi -setCanvas() dan initPuzzle().


Langkah 5: Fungsi setCanvas()

Sekarang nilai-nilai puzzle kami sudah lengkap, kami ingin mengatur elemen canvas kami. Pertama kita mengatur variabel _canvas kita untuk mereferensikan elemen canvas kita, dan _stage untuk mereferensikan context-nya.

Sekarang kami menetapkan width dan height canvas kami untuk mencocokkan ukuran gambar yang dipotong, diikuti dengan menerapkan beberapa gaya sederhana untuk membuat perbatasan hitam di sekitar canvas kami untuk menampilkan batas-batas teka-teki kami.


Langkah 6: Fungsi initPuzzle()

Di sini kami menginisialisasi teka-teki. Kami mengatur fungsi ini sedemikian rupa sehingga kami dapat memanggilnya lagi nanti ketika kami ingin memutar ulang teka-teki. Apa pun yang perlu diatur sebelum bermain tidak perlu diatur lagi.

Pertama kita mengatur _pieces sebagai array kosong dan membuat objek _mouse, yang akan menahan posisi mouse kita di seluruh aplikasi. Selanjutnya kita mengatur _currentPiece dan _currentPieceDrop ke null. (Pada permainan pertama, nilai-nilai ini akan menjadi null, tetapi kami ingin memastikannya disetel ulang saat memutar ulang teka-teki.)

Akhirnya, saatnya menggambar! Pertama kita menggambar seluruh gambar untuk ditampilkan kepada pemain apa yang akan mereka ciptakan. Setelah itu kami membuat beberapa instruksi sederhana dengan memanggil fungsi createTitle() kami.


Langkah 7: Fungsi createTitle()

Di sini kita membuat pesan yang cukup sederhana yang menginstruksikan pengguna untuk mengklik teka-teki untuk memulai.
Pesan kami akan menjadi persegi panjang semi-transparan yang akan berfungsi sebagai latar belakang teks kami. Ini memungkinkan pengguna untuk melihat gambar di belakangnya dan juga memastikan teks putih kami akan terbaca pada gambar apa pun

Kami cukup mengatur fillStyle ke hitam dan globalAlpha ke .4, sebelum mengisi persegi panjang hitam pendek di bagian bawah gambar.

Karena globalAlpha mempengaruhi seluruh kanvas, kita perlu mengaturnya kembali ke 1 (buram) sebelum menggambar teks. Untuk mengatur judul kami, kami mengatur textAlign ke 'center' dan textBaseline ke 'middle'. Kami juga dapat menerapkan beberapa properti font.

Untuk menggambar teks, kami menggunakan metode fillText(). Kami meneruskan dalam variabel msg dan letakkan di tengah horizontal canvas, dan pusat vertikal persegi panjang.


Langkah 8: Fungsi buildPieces()

Akhirnya saatnya membangun teka-teki!

Kami melakukan ini dengan membuat object untuk masing-masing bagian. Benda-benda ini tidak akan bertanggung jawab untuk rendering ke kanvas, melainkan hanya memegang referensi tentang apa yang harus menggambar dan di mana. Itu dikatakan, mari kita lakukan.

Pertama-tama, mari kita nyatakan beberapa variabel yang akan kita gunakan kembali melalui pengulangan. Kami ingin mengatur pengulangan untuk mengulang melalui jumlah potongan puzzle yang kami butuhkan. Kami mendapatkan nilai ini dengan mengalikan PUZZLE_DIFFICULTY dengan sendirinya - jadi dalam hal ini kita mendapatkan 16.

Di dalam lingkaran:

Mulai dengan membuat objek piece kosong. Selanjutnya tambahkan properti sx dan sy ke objek. Pada iterasi pertama, nilai-nilai ini adalah 0 dan mewakili titik dalam gambar kita di mana kita akan mulai menggambar. Sekarang dorong ke array _pieces[]. Objek ini juga akan berisi properti xPos dan yPos, yang akan memberi tahu kami posisi saat ini di teka-teki di mana potongan harus ditarik. Kami akan mengocok objek sebelum dapat dimainkan sehingga nilai-nilai ini tidak perlu disetel.

Hal terakhir yang kami lakukan di setiap loop adalah meningkatkan variabel lokal xPos oleh _pieceWidth. Sebelum melanjutkan dengan pengulangan, kami menentukan apakah kami perlu turun ke deretan berikutnya dengan memeriksa apakah xPos berada di luar lebar teka-teki. Jika demikian, kami mengatur ulang xPos kembali ke 0 dan meningkatkan yPos menurut _pieceHeight.

Sekarang kami memiliki potongan puzzle kami semua disimpan dengan baik di array _pieces kami. Pada titik ini, kode akhirnya berhenti mengeksekusi dan menunggu pengguna untuk berinteraksi. Kami mengatur pendengar klik ke document untuk mengaktifkan fungsi shufflePuzzle() saat dipicu, yang akan memulai permainan.


Langkah 9: Fungsi shufflePuzzle()

Hal pertama yang pertama: mengacak array _pieces[]. Saya menggunakan fungsi utilitas yang bagus di sini yang akan mengacak indeks array yang dilewatkan ke dalamnya. Penjelasan tentang fungsi ini berada di luar topik tutorial ini jadi kami akan melanjutkan, mengetahui bahwa kami telah berhasil mengocok bagian kami. (Untuk pengenalan dasar untuk mengacak, lihat tutorial ini.)

Pertama-tama, hapus semua gambar yang ditarik ke canvas untuk memberi cara menggambar potongan kita. Selanjutnya, atur susunan yang sama dengan yang kita lakukan saat pertama kali membuat objek potong kita.

Di dalam lingkaran:

Pertama-tama, gunakan variabel i untuk mengatur referensi kami ke objek potongan saat ini dalam loop. Sekarang kami mengisi properti xPos dan yPos yang saya sebutkan sebelumnya, yang akan menjadi 0 dalam iterasi pertama kami.

Sekarang, akhirnya, kami menggambar potongan kami.

Parameter pertama drawImage() memberikan sumber gambar yang ingin kita gambar. Kemudian gunakan objek potongan sx dan properti sy, bersama dengan _pieceWidth dan _pieceHeight, untuk mengisi parameter yang menyatakan area gambar untuk menggambar. Empat parameter terakhir mengatur luas canvas di mana kita ingin menggambar. Kami menggunakan nilai xPos dan yPos yang kami bangun di loop dan menugaskan ke objek.

Segera setelah ini, kami menggambar goresan cepat di sekitar potongan untuk memberikannya perbatasan, yang akan memisahkannya dengan baik dari potongan-potongan lainnya.

Sekarang kita menunggu pengguna untuk mengambil sepotong dengan mengatur click listener lain. Kali ini akan mengaktifkan fungsi onPuzzleClick().

Final HTML5 PuzzleFinal HTML5 PuzzleFinal HTML5 Puzzle

Langkah 10: Fungsi onPuzzleClick()

Kami tahu bahwa teka-teki itu diklik; sekarang kita perlu menentukan apa yang diklik. Ini bersyarat sederhana akan memberi kita posisi mouse pada semua browser desktop modern yang mendukung canvas, baik menggunakan e.layerX dan e.layerY atau e.offsetX dan e.offsetY. Gunakan nilai-nilai ini untuk memperbarui objek _mouse kami dengan menugaskannya sebuah x dan properti y untuk menahan posisi mouse saat ini - dalam hal ini, posisi di mana ia diklik.

Sejalan 112 kami kemudian segera menetapkan _currentPiece ke nilai yang dikembalikan dari fungsi checkPieceClicked() kami. Kami memisahkan kode ini karena kami ingin menggunakannya nanti ketika menyeret potongan puzzle. Saya akan menjelaskan fungsi ini di langkah berikutnya.

Jika nilai yang dikembalikan null, kami tidak melakukan apa-apa, karena ini menyiratkan bahwa pengguna tidak benar-benar mengeklik potongan teka-teki. Namun, jika kita mengambil potongan puzzle, kita ingin menempelkannya ke mouse dan memudarkannya sedikit untuk mengungkapkan potongan di bawahnya. Jadi bagaimana kita melakukan ini?

Pertama kita bersihkan area canvas tempat potongan itu duduk sebelum kita mengkliknya. Kami menggunakan clearRect() sekali lagi, tetapi dalam kasus ini kami hanya meneruskan area yang diperoleh dari objek _currentPiece. Sebelum kita menggambar ulang, kami ingin save() konteks kanvas sebelum melanjutkan. Ini akan memastikan bahwa apa pun yang kita hasilkan setelah menabung tidak akan sekadar menarik apa pun yang ada di jalannya. Kami melakukan ini karena kami akan sedikit memudar bagian yang diseret dan ingin melihat potongan di bawahnya. Jika kami tidak memanggil save(), kami hanya menggambar di atas grafik apa pun dengan cara - memudar atau tidak.

Sekarang kita menggambar gambar sehingga pusatnya diposisikan pada penunjuk tetikus. 5 parameter pertama drawImage akan selalu sama di seluruh aplikasi. Saat mengklik, dua parameter berikutnya akan diperbarui untuk memusatkan dirinya ke penunjuk mouse. Dua parameter terakhir, width dan height untuk menggambar, juga tidak akan pernah berubah.

Terakhir kita memanggil metode restore(). Ini pada dasarnya berarti kita selesai menggunakan nilai alpha baru dan ingin mengembalikan semua properti kembali ke tempat mereka berada. Untuk menyelesaikan fungsi ini kami menambahkan dua pendengar lagi. Satu untuk ketika kita menggerakkan mouse (menyeret potongan puzzle), dan satu untuk ketika kita melepaskan (menjatuhkan potongan puzzle).


Langkah 11: Fungsi checkPieceClicked()

Sekarang kita perlu mundur sedikit. Kami dapat menentukan bagian apa yang diklik, tetapi bagaimana kami melakukannya? Cukup sederhana sebenarnya. Yang perlu kita lakukan adalah mengulang semua potongan teka-teki dan menentukan apakah klik itu dalam batas-batas salah satu objek kita. Jika kita menemukannya, kita mengembalikan objek yang cocok dan mengakhiri fungsi. Jika kami tidak menemukan apa pun, kami mengembalikan null.


Langkah 12: Fungsi UpdatePuzzle()

Sekarang kembali ke menyeret. Kami memanggil fungsi ini ketika pengguna menggerakkan mouse. Ini adalah fungsi terbesar dari aplikasi saat melakukan beberapa hal. Mari kita mulai. Saya akan memecahnya saat kita pergi.

Mulai dengan mengatur _currentDropPiece ke null. Kita perlu mengatur ulang ini kembali ke null pada pembaruan karena kemungkinan bahwa bagian kami diseret kembali ke rumahnya. Kami tidak ingin nilai _currentDropPiece sebelumnya berkeliaran. Selanjutnya kita mengatur objek _mouse dengan cara yang sama seperti yang kita lakukan pada klik.

Di sini kita perlu menghapus semua grafik di kanvas. Pada dasarnya kita perlu menggambar kembali potongan-potongan puzzle karena objek yang diseret di atas akan mempengaruhi penampilan mereka. Jika kami tidak melakukan ini, kami akan melihat beberapa hasil yang sangat aneh mengikuti jalur potongan teka-teki yang diseret.

Mulailah dengan mengatur lingkaran potongan kita yang biasa.

Di Loop:

Buat referensi piece kami seperti biasa. Selanjutnya periksa apakah bagian yang kita rasakan saat ini sama dengan bagian yang kita seret. Jika demikian, lanjutkan pengulangan. Ini akan membuat slot rumah potong yang diseret kosong.

Pindah, buat kembali potongan teka-teki menggunakan propertinya persis dengan cara yang sama ketika pertama-tama menggambarnya. Pindah, buat kembali potongan teka-teki menggunakan propertinya persis dengan cara yang sama ketika pertama-tama menggambarnya.

Karena kami memiliki referensi ke setiap objek dalam loop, kami juga dapat menggunakan kesempatan ini untuk memeriksa apakah bagian yang diseret berada di atasnya. Kami melakukan ini karena kami ingin memberikan umpan balik kepada pengguna tentang potongan apa yang dapat dijatuhkan. Mari kita gali kode itu sekarang.

Pertama kami ingin melihat apakah loop ini telah menghasilkan target penurunan. Jika demikian, kita tidak perlu repot karena hanya satu target penurunan yang dapat dilakukan dan setiap gerakan mouse yang diberikan. Jika tidak, _currentDropPiece akan menjadi null dan kita dapat melanjutkan ke logika. Karena mouse kita berada di tengah-tengah bagian yang diseret, yang perlu kita lakukan hanyalah menentukan bagian lain dari mouse kita.

Selanjutnya, gunakan fungsi checkPieceClicked() kami yang berguna untuk menentukan apakah mouse melayang di atas objek potongan saat ini dalam loop. Jika demikian, kita mengatur variabel _currentDropPiece dan menggambar kotak berwarna di atas potongan puzzle, menunjukkan bahwa itu sekarang adalah target drop.

Ingat untuk save() dan restore(). Jika tidak, Anda akan mendapatkan kotak berwarna dan bukan gambar di bawahnya.

Keluar dari Loop:

Last but not least kita perlu redraw potongan yang diseret. Kode ini sama seperti saat pertama kali kita mengkliknya, tetapi mouse telah pindah sehingga posisinya akan diperbarui.


Langkah 13: Fungsi pieceDropped()

Oke, yang terburuk ada di belakang kita. Kami sekarang berhasil menyeret potongan teka-teki dan bahkan mendapatkan umpan balik visual tentang di mana itu akan dijatuhkan. Sekarang semua yang tersisa adalah menjatuhkannya. Pertama-tama, hapus pendengar segera karena tidak ada yang diseret.

Selanjutnya, periksa bahwa _currentDropPiece tidak null. Jika ya, ini berarti kami menyeretnya kembali ke area rumah potong dan bukan di atas slot lain. Jika bukan null, kami melanjutkan dengan fungsi ini.

Apa yang kita lakukan sekarang hanyalah menukar xPos dan yPos masing-masing bagian. Kami membuat objek temp cepat sebagai buffer untuk menahan salah satu nilai objek dalam proses swapping. Pada titik ini, kedua bagian tersebut memiliki nilai xPos dan yPos baru, dan akan masuk ke rumah baru mereka pada undian berikutnya. Itulah yang akan kita lakukan sekarang, sekaligus memeriksa apakah game telah dimenangkan.


Langkah 14: Fungsi resetPuzzleAndCheckWin()

Sekali lagi, kosongkan canvas dan atur variabel gameWin, atur ke true secara default. Sekarang lanjutkan dengan pengulangan potongan yang terlalu familiar.

Kode di sini seharusnya terlihat familier jadi kami tidak akan membahasnya. Ini hanya menarik potongan-potongan kembali ke slot asli atau baru. Dalam lingkaran ini, kami ingin melihat apakah setiap bagian ditarik dalam posisi menang. Ini sederhana: kami memeriksa untuk melihat apakah properti sx dan sy kami cocok dengan xPos dan yPos. Jika tidak, kami tahu kami tidak mungkin memenangkan teka-teki dan menyetel gameWin menjadi false. Jika kami berhasil melewati loop dengan semua orang di tempat kemenangan mereka, kami mengatur timeout cepat untuk memanggil metode gameOver() kami. (Kami menetapkan batas waktu sehingga layar tidak berubah begitu drastis saat menjatuhkan potongan puzzle.)


Langkah 15: Fungsi gameOver()

Ini adalah fungsi terakhir kami! Di sini kita hanya menghapus semua listener dan memanggil initPuzzle(), yang me-reset semua nilai yang diperlukan dan menunggu pengguna untuk bermain lagi.


Kesimpulan

Klik di sini untuk melihat hasil akhir.

Seperti yang Anda lihat, Anda dapat melakukan banyak hal kreatif baru di HTML5 menggunakan area bitmap yang dipilih untuk memuat gambar dan gambar. Anda dapat dengan mudah memperluas aplikasi ini dengan menambahkan skor dan mungkin bahkan pengatur waktu untuk memberikan lebih banyak permainan. Ide lain adalah untuk meningkatkan kesulitan dan memilih gambar yang berbeda dalam fungsi gameOver(), memberikan level permainan.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.