PHP Code Editor
<?php /** * BAGIAN 1: LOGIKA PENANGANAN ASET (PHP 7.3) * PHP akan mencoba mengunduh sprite asli ke server agar game bisa jalan offline. */ $assetName = 'sprite_dino.png'; $remoteUrl = 'https://raw.githubusercontent.com'; // Coba unduh jika file belum ada if (!file_exists($assetName)) { // Gunakan @ untuk membungkam error jika folder /var/www/html/ tidak writable (ReadOnly) $data = @file_get_contents($remoteUrl); if ($data) { @file_put_contents($assetName, $data); } } // Gunakan jalur lokal jika berhasil diunduh, jika tidak gunakan remote URL sebagai cadangan (Anti-Gagal) $finalAsset = (file_exists($assetName) && filesize($assetName) > 0) ? $assetName : $remoteUrl; ?> <!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Dino Chrome 1:1 - Debian 10 Ready</title> <!-- SATU-SATUNYA CDN --> <script src="https://cdn.tailwindcss.com"></script> <style> body { background-color: #f7f7f7; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; touch-action: none; overflow: hidden; user-select: none; -webkit-tap-highlight-color: transparent; } canvas { image-rendering: pixelated; display: block; margin: 0 auto; border-bottom: 2px solid #535353; } .retro-border { border: 4px solid #535353; box-shadow: 8px 8px 0px #535353; } </style> </head> <body class="flex items-center justify-center min-h-screen p-4"> <!-- GAME UI --> <div id="gameWrapper" class="relative w-full max-w-[600px] hidden"> <div id="scoreUI" class="text-right text-[#535353] font-bold text-xl mb-2 tracking-tighter">HI 00000 00000</div> <canvas id="dinoCanvas" width="600" height="150" class="w-full h-auto cursor-pointer"></canvas> </div> <!-- MODAL 1: LOADING --> <div id="loadingModal" class="fixed inset-0 bg-white flex flex-col items-center justify-center z-50"> <div class="w-10 h-10 border-4 border-gray-200 border-t-[#535353] rounded-full animate-spin"></div> <p class="mt-4 font-bold text-[#535353] tracking-widest text-xs uppercase">Sinkronisasi Aset...</p> </div> <!-- MODAL 2: GAME OVER (Dialog Modal Tailwind) --> <div id="modal" class="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center hidden z-50"> <div class="bg-white p-10 retro-border text-center max-w-sm w-full mx-4 transform scale-100 transition-transform"> <h2 class="text-[#535353] text-3xl font-black mb-2 uppercase italic tracking-widest">Kalah</h2> <p id="finalScore" class="font-bold mb-8 text-gray-400">SKOR: 00000</p> <button onclick="resetGame()" class="bg-[#535353] hover:bg-black text-white font-bold py-4 px-8 w-full transition-all active:scale-95 uppercase tracking-widest"> Main Lagi </button> </div> </div> <script> const canvas = document.getElementById('dinoCanvas'); const ctx = canvas.getContext('2d'); const sprite = new Image(); // Memuat dari PHP variable (Local Path atau Remote URL) sprite.src = "<?php echo $finalAsset; ?>"; sprite.crossOrigin = "Anonymous"; let state = { active: false, score: 0, highscore: localStorage.getItem('dino_hi_1_1') || 0, speed: 6, frame: 0 }; const dino = { x: 50, y: 125, w: 44, h: 47, dy: 0, jumped: false }; let obstacles = []; function jump() { if (!dino.jumped && state.active) { dino.dy = -11.5; dino.jumped = true; } } // Input Listener window.onkeydown = (e) => { if(e.code === 'Space' || e.code === 'ArrowUp') jump(); }; canvas.ontouchstart = (e) => { e.preventDefault(); jump(); }; function update() { if (!state.active) return; state.frame++; state.score += 0.15; state.speed += 0.0006; dino.dy += 0.65; dino.y += dino.dy; if (dino.y > 125) { dino.y = 125; dino.dy = 0; dino.jumped = false; } if (state.frame % 100 === 0 && Math.random() > 0.3) obstacles.push({ x: 600, w: 34, h: 45 }); obstacles.forEach((o, i) => { o.x -= state.speed; // Collision Hitbox Precision 1:1 if (dino.x < o.x + 22 && dino.x + 32 > o.x && dino.y > 105) stopGame(); if (o.x < -100) obstacles.splice(i, 1); }); updateUI(); } function draw() { ctx.clearRect(0, 0, 600, 150); const groundX = (state.frame * state.speed) % 1200; ctx.drawImage(sprite, 2 + groundX, 104, 600, 24, 0, 130, 600, 24); // Horizon // Frame mapping: 848 (Jump), 936/980 (Run), 1068 (Dead) let sx = state.active ? (dino.jumped ? 848 : (Math.floor(state.frame/8)%2===0?936:980)) : 1068; ctx.drawImage(sprite, sx, 2, 88, 94, dino.x, dino.y, 44, 47); // Dino obstacles.forEach(o => ctx.drawImage(sprite, 446, 2, 68, 140, o.x, 110, 34, 45)); // Cactus if (state.active) requestAnimationFrame(() => { update(); draw(); }); } function updateUI() { const cur = Math.floor(state.score).toString().padStart(5, '0'); const hi = Math.floor(state.highscore).toString().padStart(5, '0'); document.getElementById('scoreUI').innerText = `HI ${hi} ${cur}`; } function stopGame() { state.active = false; if (state.score > state.highscore) { state.highscore = state.score; localStorage.setItem('dino_hi_1_1', state.highscore); } document.getElementById('finalScore').innerText = `SKOR: ${Math.floor(state.score).toString().padStart(5, '0')}`; document.getElementById('modal').classList.remove('hidden'); draw(); } function resetGame() { state = { ...state, active: true, score: 0, speed: 6, frame: 0 }; obstacles = []; dino.y = 125; document.getElementById('modal').classList.add('hidden'); draw(); } sprite.onload = () => { document.getElementById('loading').classList.add('hidden'); document.getElementById('gameWrapper').classList.remove('hidden'); state.active = true; draw(); }; sprite.onerror = () => { document.querySelector('#loading p').innerText = "KONEKSI GAGAL!"; document.querySelector('#loading p').style.color = "red"; }; </script> </body> </html>
Run Code
<?php /** * BAGIAN 1: LOGIKA PENANGANAN ASET (PHP 7.3) * PHP akan mencoba mengunduh sprite asli ke server agar game bisa jalan offline. */ $assetName = 'sprite_dino.png'; $remoteUrl = 'https://raw.githubusercontent.com'; // Coba unduh jika file belum ada if (!file_exists($assetName)) { // Gunakan @ untuk membungkam error jika folder /var/www/html/ tidak writable (ReadOnly) $data = @file_get_contents($remoteUrl); if ($data) { @file_put_contents($assetName, $data); } } // Gunakan jalur lokal jika berhasil diunduh, jika tidak gunakan remote URL sebagai cadangan (Anti-Gagal) $finalAsset = (file_exists($assetName) && filesize($assetName) > 0) ? $assetName : $remoteUrl; ?> <!DOCTYPE html> <html lang="id"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Dino Chrome 1:1 - Debian 10 Ready</title> <!-- SATU-SATUNYA CDN --> <script src="https://cdn.tailwindcss.com"></script> <style> body { background-color: #f7f7f7; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; touch-action: none; overflow: hidden; user-select: none; -webkit-tap-highlight-color: transparent; } canvas { image-rendering: pixelated; display: block; margin: 0 auto; border-bottom: 2px solid #535353; } .retro-border { border: 4px solid #535353; box-shadow: 8px 8px 0px #535353; } </style> </head> <body class="flex items-center justify-center min-h-screen p-4"> <!-- GAME UI --> <div id="gameWrapper" class="relative w-full max-w-[600px] hidden"> <div id="scoreUI" class="text-right text-[#535353] font-bold text-xl mb-2 tracking-tighter">HI 00000 00000</div> <canvas id="dinoCanvas" width="600" height="150" class="w-full h-auto cursor-pointer"></canvas> </div> <!-- MODAL 1: LOADING --> <div id="loadingModal" class="fixed inset-0 bg-white flex flex-col items-center justify-center z-50"> <div class="w-10 h-10 border-4 border-gray-200 border-t-[#535353] rounded-full animate-spin"></div> <p class="mt-4 font-bold text-[#535353] tracking-widest text-xs uppercase">Sinkronisasi Aset...</p> </div> <!-- MODAL 2: GAME OVER (Dialog Modal Tailwind) --> <div id="modal" class="fixed inset-0 bg-black/10 backdrop-blur-sm flex items-center justify-center hidden z-50"> <div class="bg-white p-10 retro-border text-center max-w-sm w-full mx-4 transform scale-100 transition-transform"> <h2 class="text-[#535353] text-3xl font-black mb-2 uppercase italic tracking-widest">Kalah</h2> <p id="finalScore" class="font-bold mb-8 text-gray-400">SKOR: 00000</p> <button onclick="resetGame()" class="bg-[#535353] hover:bg-black text-white font-bold py-4 px-8 w-full transition-all active:scale-95 uppercase tracking-widest"> Main Lagi </button> </div> </div> <script> const canvas = document.getElementById('dinoCanvas'); const ctx = canvas.getContext('2d'); const sprite = new Image(); // Memuat dari PHP variable (Local Path atau Remote URL) sprite.src = "<?php echo $finalAsset; ?>"; sprite.crossOrigin = "Anonymous"; let state = { active: false, score: 0, highscore: localStorage.getItem('dino_hi_1_1') || 0, speed: 6, frame: 0 }; const dino = { x: 50, y: 125, w: 44, h: 47, dy: 0, jumped: false }; let obstacles = []; function jump() { if (!dino.jumped && state.active) { dino.dy = -11.5; dino.jumped = true; } } // Input Listener window.onkeydown = (e) => { if(e.code === 'Space' || e.code === 'ArrowUp') jump(); }; canvas.ontouchstart = (e) => { e.preventDefault(); jump(); }; function update() { if (!state.active) return; state.frame++; state.score += 0.15; state.speed += 0.0006; dino.dy += 0.65; dino.y += dino.dy; if (dino.y > 125) { dino.y = 125; dino.dy = 0; dino.jumped = false; } if (state.frame % 100 === 0 && Math.random() > 0.3) obstacles.push({ x: 600, w: 34, h: 45 }); obstacles.forEach((o, i) => { o.x -= state.speed; // Collision Hitbox Precision 1:1 if (dino.x < o.x + 22 && dino.x + 32 > o.x && dino.y > 105) stopGame(); if (o.x < -100) obstacles.splice(i, 1); }); updateUI(); } function draw() { ctx.clearRect(0, 0, 600, 150); const groundX = (state.frame * state.speed) % 1200; ctx.drawImage(sprite, 2 + groundX, 104, 600, 24, 0, 130, 600, 24); // Horizon // Frame mapping: 848 (Jump), 936/980 (Run), 1068 (Dead) let sx = state.active ? (dino.jumped ? 848 : (Math.floor(state.frame/8)%2===0?936:980)) : 1068; ctx.drawImage(sprite, sx, 2, 88, 94, dino.x, dino.y, 44, 47); // Dino obstacles.forEach(o => ctx.drawImage(sprite, 446, 2, 68, 140, o.x, 110, 34, 45)); // Cactus if (state.active) requestAnimationFrame(() => { update(); draw(); }); } function updateUI() { const cur = Math.floor(state.score).toString().padStart(5, '0'); const hi = Math.floor(state.highscore).toString().padStart(5, '0'); document.getElementById('scoreUI').innerText = `HI ${hi} ${cur}`; } function stopGame() { state.active = false; if (state.score > state.highscore) { state.highscore = state.score; localStorage.setItem('dino_hi_1_1', state.highscore); } document.getElementById('finalScore').innerText = `SKOR: ${Math.floor(state.score).toString().padStart(5, '0')}`; document.getElementById('modal').classList.remove('hidden'); draw(); } function resetGame() { state = { ...state, active: true, score: 0, speed: 6, frame: 0 }; obstacles = []; dino.y = 125; document.getElementById('modal').classList.add('hidden'); draw(); } sprite.onload = () => { document.getElementById('loading').classList.add('hidden'); document.getElementById('gameWrapper').classList.remove('hidden'); state.active = true; draw(); }; sprite.onerror = () => { document.querySelector('#loading p').innerText = "KONEKSI GAGAL!"; document.querySelector('#loading p').style.color = "red"; }; </script> </body> </html>
Run Code New Tab
Result