// //bagian tempat main ama buat generate game 2d var canvas = document.getElementById("game"); var content = canvas.getContext("2d"); const Text = document.getElementById("text"); const UpDead = document.getElementById("gameover-overlay"); const ScoreMain = document.getElementById("gameover-score"); const PlayAgain = document.getElementById("ulangi"); const Udahan = document.getElementById("keluar"); //settingan awal var grid = 24; var count = 0; var speed = 10; var score = 0; var highscore = 0; var ArahUlar = 0; var GameOverTimer = 0; var GameStart = false; var ModeH = false; const persiapan = document.getElementById("start"); //generate gambar external var KepalaKeKanan = new Image(); KepalaKeKanan.src = "image/KepalaHorizontalKanan.png"; var KepalaKeAtas = new Image(); KepalaKeAtas.src = "image/KepalaVertikalAtas.png"; var KepalaKeKiri = new Image(); KepalaKeKiri.src = "image/KepalaVertikalBawah.png"; // Asumsi ini adalah Kepala ke KIRI var KepalaKeBawah = new Image(); KepalaKeBawah.src = "image/KepalaHorizontalKiri.png"; // Asumsi ini adalah Kepala ke BAWAH // ↑ kepala ular var BadanHori = new Image(); BadanHori.src = "image/BadanHorizontal.png"; var BadanVerti = new Image(); BadanVerti.src = "image/BadanVertical.png"; // ↑ badan ular var ApelImage = new Image(); ApelImage.src = "image/ApelLayer.png"; // Apel var TembokImage = new Image(); TembokImage.src = "image/Tembok.png"; // Tembok Batagor //set posisi ular dan Apel var Ular = { x: 528, y: 240, dx: grid, dy: 0, cells: [], maxCells: 4 }; var Apel = { x: 0, y: 0 }; var Tembok = []; UpdateScore(0); RandomizeApel(); //mastiin gambar external generate dulu sebelum game start KepalaKeKanan.onload = function () { // Memastikan gambar termuat, meskipun fungsi ini tidak melakukan apa-apa selain deklarasi function gameLoop() {} }; //ngatur biar game ga langsung jalan function StartingGame(menantang) { ModeH = menantang; persiapan.style.display = "none"; if (ModeH) { RandomSpawnWall(); } else { Tembok = []; } GameStart = true; } //buat ngatur batu spwan apa ga nanti document.getElementById("mode-normal").addEventListener("click", function () { StartingGame(false); }); document.getElementById("mode-tambahan").addEventListener("click", function () { StartingGame(true); }); //play again pas gameover function resetGame() { Ular.x = 528; Ular.y = 240; Ular.cells = []; Ular.maxCells = 4; Ular.dx = grid; Ular.dy = 0; Tembok = []; RandomizeApel(); UpdateScore(0); ModeH = false; // Penting: Jangan set GameStart=false di sini jika ingin memulai ulang game loop // Kita set GameStart=false saat game over, lalu di sini kita persiapkan UI } //ngatur tombol pas gameover kemana abis di klik PlayAgain.addEventListener("click", function () { UpDead.style.display = "none"; resetGame(); persiapan.style.display = "flex"; }); Udahan.addEventListener("click", function () { UpDead.style.display = "none"; resetGame(); // Opsional: Redirect ke halaman lain // window.location.href = 'leaderboard.php'; }); //gameover terus ngatur ular ke setting awal function gameLoop() { if (!GameStart) { requestAnimationFrame(gameLoop); return; } ClearCanvas(); Movement(); IntiGame(); requestAnimationFrame(gameLoop); } //random spawn Apel function RandomizeApel() { var pembataslebar = Math.floor(canvas.width / grid); var pembatastinggi = Math.floor(canvas.height / grid); do { Apel.x = Math.floor(Math.random() * pembataslebar) * grid; Apel.y = Math.floor(Math.random() * pembatastinggi) * grid; // Pastikan Apel tidak spawn di atas ular var isOverlapSnake = Ular.cells.some(cell => cell.x === Apel.x && cell.y === Apel.y); // Pastikan Apel tidak spawn di atas tembok var isOverlapWall = Tembok.some(bata => bata.x === Apel.x && bata.y === Apel.y); } while (isOverlapSnake || isOverlapWall); } //random spawn tembok function RandomSpawnWall() { var TembokX, TembokY; var kosong; var pembataslebar = Math.floor(canvas.width / grid); var pembatastinggi = Math.floor(canvas.height / grid); //create tembok do { kosong = true; TembokX = Math.floor(Math.random() * pembataslebar) * grid; TembokY = Math.floor(Math.random() * pembatastinggi) * grid; // cek untuk posisi yang mau di kasih tembok ada/tidak ada ularnya for (var i = 0; i < Ular.cells.length; i++) { // Hindari spawn di sekitar kepala ular (misal 5 kotak pertama) if (Math.abs(Ular.cells[i].x - TembokX) <= grid * 5 && Math.abs(Ular.cells[i].y - TembokY) <= grid * 5) { kosong = false; break; } } // tidak memperbolehkan tembok spawn sama dengan apel if (TembokX === Apel.x && TembokY === Apel.y) { kosong = false; } // Cek agar tembok tidak menumpuk for (var i = 0; i < Tembok.length; i++) { if (Tembok[i].x === TembokX && Tembok[i].y === TembokY) { kosong = false; break; } } } while (kosong === false); Tembok.push({ x: TembokX, y: TembokY }); } //nambah tembok tiap score kelipatan function PenambahanTembok() { if (ModeH) { // Tambah tembok setiap 2 skor, tapi jangan melebihi batas (misal 15 tembok) if (score > 0 && score % 2 === 0 && Tembok.length < 15 && Tembok.length < score / 2) { RandomSpawnWall(); } } } //set dan update score function UpdateScore(amount) { score = amount > 0 ? score + amount : 0; // Highscore hanya di update jika skor > highscore yang TERCATAT LOKAL. // Highscore dari database biasanya diambil saat inisialisasi. highscore = score > highscore ? score : highscore; Text.innerHTML = "Score: " + score + "
Highscore: " + highscore + "
Speed: " + speed; PenambahanTembok(); } //tampilin gameover function GameOver() { GameStart = false; ClearCanvas(); const modePermainan = ModeH ? "Tambahan" : "Normal"; // Kirim skor ke server hanya jika skor lebih besar dari 0 (atau sesuai kriteria) if (score > 0) { kirimSkorKeServer(score, modePermainan); } // TAMPILKAN POP-UP GAME OVER ScoreMain.innerHTML = "Score: " + score; UpDead.style.display = "flex"; } //reset isi canvas doang function ClearCanvas() { content.clearRect(0, 0, canvas.width, canvas.height); } //bagian utama: //ular makan dan mati pas ketemu badannya function IntiGame() { // buat gambarnya bisa keluar content.drawImage(ApelImage, Apel.x, Apel.y, grid, grid); // Gambar Tembok Tembok.forEach(function (bata) { content.drawImage(TembokImage, bata.x, bata.y, grid, grid); }); // Gambar Ular Ular.cells.forEach(function (cell, index) { if (index === 0) { // Logika Pemilihan Gambar Kepala Ular var posisiKepalaImage; if (Ular.dx === grid) { // KANAN posisiKepalaImage = KepalaKeKanan; } else if (Ular.dx === -grid) { // KIRI posisiKepalaImage = KepalaKeKiri; } else if (Ular.dy === -grid) { // ATAS posisiKepalaImage = KepalaKeAtas; // Perlu dicek, di atas Anda menamakannya KepalaKeBawah? } else if (Ular.dy === grid) { // BAWAH posisiKepalaImage = KepalaKeBawah; // Perlu dicek, di atas Anda menamakannya KepalaKeAtas? } else { posisiKepalaImage = KepalaKeKanan; } content.drawImage(posisiKepalaImage, cell.x, cell.y, grid, grid); } else { // Logika sederhana untuk badan: selalu horizontal // Catatan: untuk badan vertikal/belokan, perlu logika tambahan, tapi kita ikuti kode asli Anda. content.drawImage(BadanHori, cell.x, cell.y, grid, grid); } // buat pas ular makan Apel (Deteksi tabrakan kepala vs Apel) if (index === 0 && cell.x === Apel.x && cell.y === Apel.y) { Ular.maxCells += 1; UpdateScore(1); RandomizeApel(); } // tabrak tembok = mati (Hanya kepala yang dicek) if (index === 0) { Tembok.forEach(function (bata) { if (cell.x === bata.x && cell.y === bata.y) { GameOver(); } }); } // buat pas ular mati kena badan sendiri (Hanya kepala vs sisa badan) if (index === 0) { for (var i = 1; i < Ular.cells.length; i++) { // Mulai dari index 1 (badan) if (cell.x === Ular.cells[i].x && cell.y === Ular.cells[i].y) { GameOver(); } } } }); } //update teks pada speed kalau score update function Movement() { if (++count < speed) return; if (ArahUlar > 0) ArahUlar--; count = 0; // Pindahkan kepala Ular.x += Ular.dx; Ular.y += Ular.dy; // Cek batas layar if ( Ular.x < 0 || Ular.x >= canvas.width || Ular.y < 0 || Ular.y >= canvas.height ) GameOver(); // Tambahkan posisi baru ke sel pertama (Kepala) Ular.cells.unshift({ x: Ular.x, y: Ular.y }); // Hapus sel terakhir (Ekor) if (Ular.cells.length > Ular.maxCells) Ular.cells.pop(); } //input keyboard function InputKeyboard() { document.addEventListener("keydown", function (e) { if (!GameStart) return; // Key A, D (Kiri/Kanan) if ( ArahUlar == 0 && ((e.code == "KeyA" && Ular.dx === 0) || (e.code == "KeyD" && Ular.dx === 0)) ) { ArahUlar = 1; Ular.dx = e.code === "KeyA" ? -grid : grid; Ular.dy = 0; } // Key W, S (Atas/Bawah) else if ( ArahUlar == 0 && ((e.code === "KeyW" && Ular.dy === 0) || (e.code == "KeyS" && Ular.dy === 0)) ) { ArahUlar = 1; Ular.dy = e.code == "KeyW" ? -grid : grid; Ular.dx = 0; } // Arrow Up, Down else if ( ArahUlar == 0 && ((e.code === "ArrowUp" && Ular.dy === 0) || (e.code == "ArrowDown" && Ular.dy === 0)) ) { ArahUlar = 1; Ular.dy = e.code == "ArrowUp" ? -grid : grid; Ular.dx = 0; } // Arrow Left, Right else if ( ArahUlar == 0 && ((e.code === "ArrowLeft" && Ular.dx === 0) || (e.code == "ArrowRight" && Ular.dx === 0)) ) { ArahUlar = 1; Ular.dx = e.code == "ArrowLeft" ? -grid : grid; Ular.dy = 0; } // Key E (Cepat/Kurangi speed), Key Q (Lambat/Tambah speed) if (e.code === "KeyE" || e.code == "KeyQ") speed = e.code == "KeyE" && speed > 2 ? speed - 1 : e.code == "KeyQ" && speed < 30 ? speed + 1 : speed; }); } function kirimSkorKeServer(skor, modePermainan) { console.log(`Mengirim skor ${skor} (Mode: ${modePermainan}) ke server...`); // Ganti 'score.php' sesuai dengan path di server Anda fetch('score.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ score: skor, mode: modePermainan }), }) .then(response => { if (!response.ok) { return response.json().then(error => { throw new Error(error.message || 'Gagal menyimpan skor'); }); } return response.json(); }) .then(data => { if (data.status === 'success') { console.log('✅ Berhasil:', data.message); // Opsional: Perbarui highscore lokal dengan skor yang baru jika ini highscore if (data.message.includes('Highscore baru')) { highscore = skor; UpdateScore(0); // Update tampilan score/highscore } } else { console.error('❌ Error Server:', data.message); } }) .catch((error) => { console.error('⚠️ Error Jaringan atau Proses:', error.message); // Tampilkan pesan error ke user jika perlu // alert("Gagal terhubung ke server skor. Pastikan Anda sudah login."); }); } InputKeyboard(); gameLoop();