|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>Neon Cyberpunk Snake</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); |
|
|
|
|
|
body { |
|
|
background-color: #0f0f1a; |
|
|
font-family: 'Press Start 2P', cursive; |
|
|
overflow: hidden; |
|
|
color: #00f7ff; |
|
|
} |
|
|
|
|
|
.game-container { |
|
|
position: relative; |
|
|
border: 4px solid #ff00f7; |
|
|
box-shadow: 0 0 20px #ff00f7, |
|
|
inset 0 0 20px #ff00f7; |
|
|
border-radius: 5px; |
|
|
} |
|
|
|
|
|
.snake { |
|
|
background-color: #00f7ff; |
|
|
box-shadow: 0 0 10px #00f7ff; |
|
|
border-radius: 2px; |
|
|
} |
|
|
|
|
|
.food { |
|
|
background-color: #ff00f7; |
|
|
box-shadow: 0 0 10px #ff00f7; |
|
|
border-radius: 50%; |
|
|
animation: pulse 0.5s infinite alternate; |
|
|
} |
|
|
|
|
|
@keyframes pulse { |
|
|
0% { |
|
|
transform: scale(1); |
|
|
opacity: 1; |
|
|
} |
|
|
100% { |
|
|
transform: scale(1.2); |
|
|
opacity: 0.8; |
|
|
} |
|
|
} |
|
|
|
|
|
.neon-text { |
|
|
text-shadow: 0 0 5px #00f7ff, |
|
|
0 0 10px #00f7ff, |
|
|
0 0 15px #00f7ff; |
|
|
} |
|
|
|
|
|
.neon-pink { |
|
|
text-shadow: 0 0 5px #ff00f7, |
|
|
0 0 10px #ff00f7, |
|
|
0 0 15px #ff00f7; |
|
|
} |
|
|
|
|
|
.grid-line { |
|
|
border: 1px solid rgba(0, 247, 255, 0.1); |
|
|
} |
|
|
|
|
|
.scanline { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: linear-gradient(to bottom, |
|
|
rgba(0, 247, 255, 0) 0%, |
|
|
rgba(0, 247, 255, 0.1) 50%, |
|
|
rgba(0, 247, 255, 0) 100%); |
|
|
background-size: 100% 8px; |
|
|
pointer-events: none; |
|
|
animation: scan 3s linear infinite; |
|
|
} |
|
|
|
|
|
@keyframes scan { |
|
|
0% { |
|
|
background-position: 0 0; |
|
|
} |
|
|
100% { |
|
|
background-position: 0 100%; |
|
|
} |
|
|
} |
|
|
|
|
|
.glitch { |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.glitch::before, .glitch::after { |
|
|
content: attr(data-text); |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: #0f0f1a; |
|
|
} |
|
|
|
|
|
.glitch::before { |
|
|
left: 2px; |
|
|
text-shadow: -2px 0 #ff00f7; |
|
|
clip-path: polygon(0 20%, 100% 20%, 100% 30%, 0 30%); |
|
|
animation: glitch-anim 2s infinite linear alternate-reverse; |
|
|
} |
|
|
|
|
|
.glitch::after { |
|
|
left: -2px; |
|
|
text-shadow: 2px 0 #00f7ff; |
|
|
clip-path: polygon(0 60%, 100% 60%, 100% 70%, 0 70%); |
|
|
animation: glitch-anim 1.5s infinite linear alternate-reverse; |
|
|
} |
|
|
|
|
|
@keyframes glitch-anim { |
|
|
0% { |
|
|
transform: translate(0); |
|
|
} |
|
|
20% { |
|
|
transform: translate(-3px, 3px); |
|
|
} |
|
|
40% { |
|
|
transform: translate(-3px, -3px); |
|
|
} |
|
|
60% { |
|
|
transform: translate(3px, 3px); |
|
|
} |
|
|
80% { |
|
|
transform: translate(3px, -3px); |
|
|
} |
|
|
100% { |
|
|
transform: translate(0); |
|
|
} |
|
|
} |
|
|
|
|
|
.btn-neon { |
|
|
background: transparent; |
|
|
border: 2px solid #00f7ff; |
|
|
color: #00f7ff; |
|
|
padding: 10px 20px; |
|
|
font-family: 'Press Start 2P', cursive; |
|
|
font-size: 12px; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s; |
|
|
box-shadow: 0 0 10px #00f7ff; |
|
|
position: relative; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
.btn-neon:hover { |
|
|
background: #00f7ff; |
|
|
color: #0f0f1a; |
|
|
box-shadow: 0 0 20px #00f7ff; |
|
|
} |
|
|
|
|
|
.btn-neon::before { |
|
|
content: ''; |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: -100%; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background: linear-gradient(90deg, transparent, rgba(0, 247, 255, 0.4), transparent); |
|
|
transition: 0.5s; |
|
|
} |
|
|
|
|
|
.btn-neon:hover::before { |
|
|
left: 100%; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="min-h-screen flex flex-col items-center justify-center p-4"> |
|
|
<div class="text-center mb-6"> |
|
|
<h1 class="glitch neon-text text-4xl md:text-5xl mb-2" data-text="NEON SNAKE">NEON SNAKE</h1> |
|
|
<p class="neon-pink text-sm">CYBERPUNK EDITION</p> |
|
|
</div> |
|
|
|
|
|
<div class="relative"> |
|
|
<div class="game-container"> |
|
|
<canvas id="gameCanvas" class="bg-black"></canvas> |
|
|
<div class="scanline"></div> |
|
|
</div> |
|
|
|
|
|
<div id="gameOver" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80"> |
|
|
<h2 class="neon-text text-3xl mb-6">GAME OVER</h2> |
|
|
<p id="finalScore" class="neon-pink text-xl mb-6">SCORE: 0</p> |
|
|
<button id="restartBtn" class="btn-neon">RESTART</button> |
|
|
</div> |
|
|
|
|
|
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80"> |
|
|
<h2 class="neon-text text-3xl mb-6">NEON SNAKE</h2> |
|
|
<p class="neon-pink text-sm mb-8">USE ARROW KEYS TO MOVE</p> |
|
|
<button id="startBtn" class="btn-neon">START GAME</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 flex justify-between w-full max-w-md"> |
|
|
<div class="text-center"> |
|
|
<p class="neon-text text-sm">SCORE</p> |
|
|
<p id="score" class="neon-pink text-2xl">0</p> |
|
|
</div> |
|
|
<div class="text-center"> |
|
|
<p class="neon-text text-sm">HIGH SCORE</p> |
|
|
<p id="highScore" class="neon-pink text-2xl">0</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 grid grid-cols-4 gap-4"> |
|
|
<button id="upBtn" class="btn-neon py-2">↑</button> |
|
|
<button id="leftBtn" class="btn-neon py-2">←</button> |
|
|
<button id="downBtn" class="btn-neon py-2">↓</button> |
|
|
<button id="rightBtn" class="btn-neon py-2">→</button> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
const canvas = document.getElementById('gameCanvas'); |
|
|
const ctx = canvas.getContext('2d'); |
|
|
const startScreen = document.getElementById('startScreen'); |
|
|
const gameOverScreen = document.getElementById('gameOver'); |
|
|
const startBtn = document.getElementById('startBtn'); |
|
|
const restartBtn = document.getElementById('restartBtn'); |
|
|
const scoreDisplay = document.getElementById('score'); |
|
|
const highScoreDisplay = document.getElementById('highScore'); |
|
|
const finalScoreDisplay = document.getElementById('finalScore'); |
|
|
|
|
|
|
|
|
const upBtn = document.getElementById('upBtn'); |
|
|
const downBtn = document.getElementById('downBtn'); |
|
|
const leftBtn = document.getElementById('leftBtn'); |
|
|
const rightBtn = document.getElementById('rightBtn'); |
|
|
|
|
|
|
|
|
const gridSize = 20; |
|
|
const tileCount = 20; |
|
|
let speed = 7; |
|
|
|
|
|
|
|
|
let snake = []; |
|
|
let food = {}; |
|
|
let direction = 'right'; |
|
|
let nextDirection = 'right'; |
|
|
let score = 0; |
|
|
let highScore = localStorage.getItem('highScore') || 0; |
|
|
let gameRunning = false; |
|
|
let gameLoop; |
|
|
|
|
|
|
|
|
canvas.width = gridSize * tileCount; |
|
|
canvas.height = gridSize * tileCount; |
|
|
|
|
|
|
|
|
function initGame() { |
|
|
snake = [ |
|
|
{x: 10, y: 10}, |
|
|
{x: 9, y: 10}, |
|
|
{x: 8, y: 10} |
|
|
]; |
|
|
|
|
|
spawnFood(); |
|
|
direction = 'right'; |
|
|
nextDirection = 'right'; |
|
|
score = 0; |
|
|
scoreDisplay.textContent = score; |
|
|
highScoreDisplay.textContent = highScore; |
|
|
gameRunning = true; |
|
|
} |
|
|
|
|
|
|
|
|
function spawnFood() { |
|
|
food = { |
|
|
x: Math.floor(Math.random() * tileCount), |
|
|
y: Math.floor(Math.random() * tileCount) |
|
|
}; |
|
|
|
|
|
|
|
|
for (let segment of snake) { |
|
|
if (segment.x === food.x && segment.y === food.y) { |
|
|
return spawnFood(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function gameUpdate() { |
|
|
|
|
|
const head = {x: snake[0].x, y: snake[0].y}; |
|
|
|
|
|
direction = nextDirection; |
|
|
|
|
|
switch(direction) { |
|
|
case 'up': |
|
|
head.y--; |
|
|
break; |
|
|
case 'down': |
|
|
head.y++; |
|
|
break; |
|
|
case 'left': |
|
|
head.x--; |
|
|
break; |
|
|
case 'right': |
|
|
head.x++; |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) { |
|
|
gameOver(); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
for (let i = 0; i < snake.length; i++) { |
|
|
if (head.x === snake[i].x && head.y === snake[i].y) { |
|
|
gameOver(); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (head.x === food.x && head.y === food.y) { |
|
|
|
|
|
score++; |
|
|
scoreDisplay.textContent = score; |
|
|
|
|
|
|
|
|
if (score % 5 === 0) { |
|
|
speed += 0.5; |
|
|
} |
|
|
|
|
|
spawnFood(); |
|
|
} else { |
|
|
|
|
|
snake.pop(); |
|
|
} |
|
|
|
|
|
|
|
|
snake.unshift(head); |
|
|
|
|
|
|
|
|
draw(); |
|
|
} |
|
|
|
|
|
|
|
|
function draw() { |
|
|
|
|
|
ctx.fillStyle = '#0f0f1a'; |
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height); |
|
|
|
|
|
|
|
|
ctx.strokeStyle = 'rgba(0, 247, 255, 0.1)'; |
|
|
ctx.lineWidth = 1; |
|
|
|
|
|
for (let i = 0; i < tileCount; i++) { |
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(i * gridSize, 0); |
|
|
ctx.lineTo(i * gridSize, canvas.height); |
|
|
ctx.stroke(); |
|
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(0, i * gridSize); |
|
|
ctx.lineTo(canvas.width, i * gridSize); |
|
|
ctx.stroke(); |
|
|
} |
|
|
|
|
|
|
|
|
for (let segment of snake) { |
|
|
ctx.fillStyle = '#00f7ff'; |
|
|
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize); |
|
|
|
|
|
|
|
|
ctx.shadowColor = '#00f7ff'; |
|
|
ctx.shadowBlur = 10; |
|
|
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize, gridSize); |
|
|
ctx.shadowBlur = 0; |
|
|
} |
|
|
|
|
|
|
|
|
const head = snake[0]; |
|
|
ctx.fillStyle = '#00ffaa'; |
|
|
ctx.fillRect(head.x * gridSize, head.y * gridSize, gridSize, gridSize); |
|
|
ctx.shadowColor = '#00ffaa'; |
|
|
ctx.shadowBlur = 15; |
|
|
ctx.fillRect(head.x * gridSize, head.y * gridSize, gridSize, gridSize); |
|
|
ctx.shadowBlur = 0; |
|
|
|
|
|
|
|
|
ctx.fillStyle = '#ff00f7'; |
|
|
ctx.beginPath(); |
|
|
ctx.arc( |
|
|
food.x * gridSize + gridSize / 2, |
|
|
food.y * gridSize + gridSize / 2, |
|
|
gridSize / 2, |
|
|
0, |
|
|
Math.PI * 2 |
|
|
); |
|
|
ctx.fill(); |
|
|
|
|
|
|
|
|
ctx.shadowColor = '#ff00f7'; |
|
|
ctx.shadowBlur = 15; |
|
|
ctx.fill(); |
|
|
ctx.shadowBlur = 0; |
|
|
} |
|
|
|
|
|
|
|
|
function gameOver() { |
|
|
gameRunning = false; |
|
|
clearInterval(gameLoop); |
|
|
|
|
|
|
|
|
if (score > highScore) { |
|
|
highScore = score; |
|
|
localStorage.setItem('highScore', highScore); |
|
|
highScoreDisplay.textContent = highScore; |
|
|
} |
|
|
|
|
|
finalScoreDisplay.textContent = `SCORE: ${score}`; |
|
|
gameOverScreen.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function startGame() { |
|
|
startScreen.classList.add('hidden'); |
|
|
gameOverScreen.classList.add('hidden'); |
|
|
initGame(); |
|
|
draw(); |
|
|
|
|
|
gameLoop = setInterval(gameUpdate, 1000 / speed); |
|
|
} |
|
|
|
|
|
|
|
|
startBtn.addEventListener('click', startGame); |
|
|
restartBtn.addEventListener('click', startGame); |
|
|
|
|
|
|
|
|
document.addEventListener('keydown', (e) => { |
|
|
if (!gameRunning) return; |
|
|
|
|
|
switch(e.key) { |
|
|
case 'ArrowUp': |
|
|
if (direction !== 'down') nextDirection = 'up'; |
|
|
break; |
|
|
case 'ArrowDown': |
|
|
if (direction !== 'up') nextDirection = 'down'; |
|
|
break; |
|
|
case 'ArrowLeft': |
|
|
if (direction !== 'right') nextDirection = 'left'; |
|
|
break; |
|
|
case 'ArrowRight': |
|
|
if (direction !== 'left') nextDirection = 'right'; |
|
|
break; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
upBtn.addEventListener('click', () => { |
|
|
if (gameRunning && direction !== 'down') nextDirection = 'up'; |
|
|
}); |
|
|
|
|
|
downBtn.addEventListener('click', () => { |
|
|
if (gameRunning && direction !== 'up') nextDirection = 'down'; |
|
|
}); |
|
|
|
|
|
leftBtn.addEventListener('click', () => { |
|
|
if (gameRunning && direction !== 'right') nextDirection = 'left'; |
|
|
}); |
|
|
|
|
|
rightBtn.addEventListener('click', () => { |
|
|
if (gameRunning && direction !== 'left') nextDirection = 'right'; |
|
|
}); |
|
|
|
|
|
|
|
|
draw(); |
|
|
}); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=chakkale/neonsnake" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |