Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Mobile Snake Game</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: #1a1a1a;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
touch-action: none; /* Prevents screen scrolling/bouncing during swipes */
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#scoreBoard {
font-size: 24px;
margin-bottom: 10px;
font-weight: bold;
}
#gameContainer {
position: relative;
}
canvas {
background-color: #000;
display: block;
max-width: 90vw;
max-height: 70vh;
border: 4px solid #4CAF50;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
}
#gameOverScreen {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 4px;
}
#gameOverScreen h2 {
color: #ff3333;
font-size: 32px;
margin-bottom: 15px;
}
#restartBtn {
padding: 12px 24px;
font-size: 18px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: bold;
}
#restartBtn:active {
background-color: #45a049;
}
</style>
</head>
<body>
<div id="scoreBoard">Score: <span id="score">0</span></div>
<div id="gameContainer">
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="gameOverScreen">
<h2>Game Over</h2>
<button id="restartBtn">Play Again</button>
</div>
</div>
<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const scoreElement = document.getElementById("score");
const gameOverScreen = document.getElementById("gameOverScreen");
const restartBtn = document.getElementById("restartBtn");
const gridSize = 20;
const tileCount = canvas.width / gridSize;
let snake = [{ x: 10, y: 10 }];
let food = { x: 5, y: 5 };
let dx = 1; // Starting direction: Right
let dy = 0;
let score = 0;
let gameInterval;
const gameSpeed = 100; // Lower is faster (in milliseconds)
// Start Game
function startGame() {
snake = [{ x: 10, y: 10 }];
dx = 1;
dy = 0;
score = 0;
scoreElement.innerText = score;
gameOverScreen.style.display = "none";
generateFood();
clearInterval(gameInterval);
gameInterval = setInterval(updateGame, gameSpeed);
}
// Main Game Loop
function updateGame() {
moveSnake();
if (checkCollision()) {
endGame();
return;
}
checkFoodConsumption();
draw();
}
// Move Snake Head and Body
function moveSnake() {
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head);
snake.pop();
}
// Check Wall or Self Collisions
function checkCollision() {
const head = snake[0];
// Wall Hits
if (head.x < 0 || head.x >= tileCount || head.y < 0 || head.y >= tileCount) {
return true;
}
// Self Hits
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
return true;
}
}
return false;
}
// Food Logic
function checkFoodConsumption() {
const head = snake[0];
if (head.x === food.x && head.y === food.y) {
score += 10;
scoreElement.innerText = score;
growSnake();
generateFood();
}
}
function growSnake() {
// Duplicate tail position to add a segment
const tail = { ...snake[snake.length - 1] };
snake.push(tail);
}
function generateFood() {
food.x = Math.floor(Math.random() * tileCount);
food.y = Math.floor(Math.random() * tileCount);
// Ensure food doesn't spawn inside the snake
for (let segment of snake) {
if (food.x === segment.x && food.y === segment.y) {
generateFood();
break;
}
}
}
// Rendering Graphics
function draw() {
// Clear Grid
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Snake
snake.forEach((segment, index) => {
ctx.fillStyle = index === 0 ? "#4CAF50" : "#81C784"; // Darker green for head
ctx.fillRect(segment.x * gridSize, segment.y * gridSize, gridSize - 2, gridSize - 2);
});
// Draw Food
ctx.fillStyle = "#FF5722";
ctx.fillRect(food.x * gridSize, food.y * gridSize, gridSize - 2, gridSize - 2);
}
function endGame() {
clearInterval(gameInterval);
gameOverScreen.style.display = "flex";
}
// Controls Processing Core Logic
function handleInput(key) {
if (key === "ArrowUp" && dy === 0) { dx = 0; dy = -1; }
if (key === "ArrowDown" && dy === 0) { dx = 0; dy = 1; }
if (key === "ArrowLeft" && dx === 0) { dx = -1; dy = 0; }
if (key === "ArrowRight" && dx === 0) { dx = 1; dy = 0; }
}
// Desktop Keyboard Event Listener
document.addEventListener("keydown", (e) => {
handleInput(e.key);
});
// Mobile Touch Swipe Handling
let startX = 0;
let startY = 0;
document.addEventListener("touchstart", (e) => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
}, { passive: true });
document.addEventListener("touchend", (e) => {
let endX = e.changedTouches[0].clientX;
let endY = e.changedTouches[0].clientY;
let dxTouch = endX - startX;
let dyTouch = endY - startY;
const threshold = 30; // Minimum swipe distance in pixels
if (Math.abs(dxTouch) > Math.abs(dyTouch)) {
if (Math.abs(dxTouch) > threshold) {
if (dxTouch > 0) handleInput("ArrowRight");
else handleInput("ArrowLeft");
}
} else {
if (Math.abs(dyTouch) > threshold) {
if (dyTouch > 0) handleInput("ArrowDown");
else handleInput("ArrowUp");
}
}
}, { passive: true });
// Event listener for mobile restart button
restartBtn.addEventListener("click", startGame);
restartBtn.addEventListener("touchstart", (e) => {
e.stopPropagation(); // Prevents touch events from triggering a swipe actions
startGame();
});
// Launch Game Loop on Load
startGame();
</script>
</body>
</html>
1
1
8KB
8KB
55.0ms
192.0ms
55.0ms