snake / index.html
DavidHouse2024's picture
Add 2 files
f400424 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Game - Direction Pad Control</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.game-container {
position: relative;
background-color: #1a202c;
border-radius: 0.5rem;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
}
.game-grid {
display: grid;
grid-template-columns: repeat(var(--grid-size), 1fr);
grid-template-rows: repeat(var(--grid-size), 1fr);
gap: 1px;
background-color: #2d3748;
}
.grid-cell {
aspect-ratio: 1/1;
background-color: #1a202c;
position: relative;
}
.snake {
background-color: #48bb78;
border-radius: 20%;
transition: all 0.1s ease;
z-index: 2;
position: relative;
}
.snake-head {
background-color: #38a169;
border-radius: 30%;
}
.food {
background-color: #f56565;
border-radius: 50%;
animation: pulse 1.5s infinite;
z-index: 2;
position: relative;
}
@keyframes pulse {
0% { transform: scale(0.95); }
50% { transform: scale(1.1); }
100% { transform: scale(0.95); }
}
.game-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(26, 32, 44, 0.9);
border-radius: 0.5rem;
z-index: 10;
}
.dpad-container {
position: relative;
width: 150px;
height: 150px;
margin: 20px auto;
}
.dpad-outer {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: rgba(72, 187, 120, 0.1);
border: 2px solid rgba(72, 187, 120, 0.3);
}
.dpad-center {
position: absolute;
width: 50px;
height: 50px;
background: rgba(72, 187, 120, 0.2);
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.dpad-direction {
position: absolute;
width: 40px;
height: 40px;
background: rgba(72, 187, 120, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 16px;
transition: all 0.1s ease;
cursor: pointer;
}
.dpad-direction:active {
transform: scale(1.3);
background: rgba(72, 187, 120, 0.6);
}
.dpad-up {
top: 10px;
left: 50%;
transform: translateX(-50%);
}
.dpad-down {
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
.dpad-left {
left: 10px;
top: 50%;
transform: translateY(-50%);
}
.dpad-right {
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.mobile-controls {
display: none;
}
@media (max-width: 640px) {
.mobile-controls {
display: block;
}
.desktop-instructions {
display: none;
}
}
</style>
</head>
<body class="bg-gray-900 min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-md w-full">
<h1 class="text-3xl font-bold text-center text-green-400 mb-2">🐍 方向键控制贪吃蛇</h1>
<div class="flex justify-between items-center mb-4">
<div class="text-white font-semibold">
分数: <span id="score" class="text-green-400">0</span>
</div>
<div class="text-white font-semibold">
最高分: <span id="high-score" class="text-yellow-400">0</span>
</div>
</div>
<div class="game-container">
<div id="game-board" class="game-grid w-full" style="--grid-size: 20;"></div>
<div id="game-overlay" class="game-overlay">
<h2 class="text-3xl font-bold text-white mb-4" id="overlay-title">方向键控制贪吃蛇</h2>
<p class="text-gray-300 mb-6 text-center px-4" id="overlay-message">
使用键盘方向键或下方控制盘控制蛇的移动方向
</p>
<button id="start-btn" class="bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-6 rounded-full transition">
开始游戏
</button>
</div>
</div>
<!-- Direction pad for mobile/touch devices -->
<div class="mobile-controls">
<div class="dpad-container">
<div class="dpad-outer"></div>
<div class="dpad-center"></div>
<div class="dpad-direction dpad-up" id="btn-touch-up">
<i class="fas fa-arrow-up"></i>
</div>
<div class="dpad-direction dpad-down" id="btn-touch-down">
<i class="fas fa-arrow-down"></i>
</div>
<div class="dpad-direction dpad-left" id="btn-touch-left">
<i class="fas fa-arrow-left"></i>
</div>
<div class="dpad-direction dpad-right" id="btn-touch-right">
<i class="fas fa-arrow-right"></i>
</div>
</div>
</div>
<div class="mt-4 text-center text-gray-400 text-sm desktop-instructions">
<p>使用键盘方向键控制方向 | 按空格键暂停</p>
</div>
</div>
<script>
// Game configuration
const config = {
gridSize: 20,
initialSpeed: 150,
speedIncrease: 5,
maxSpeed: 80,
minSpeed: 40
};
// Game state
let snake = [];
let food = {};
let direction = {x: 1, y: 0}; // Initial direction: right
let nextDirection = {x: 1, y: 0};
let score = 0;
let highScore = localStorage.getItem('snakeHighScore') || 0;
let gameInterval;
let isPaused = false;
let gameStarted = false;
let gameSpeed = config.initialSpeed;
// DOM elements
const gameBoard = document.getElementById('game-board');
const gameOverlay = document.getElementById('game-overlay');
const overlayTitle = document.getElementById('overlay-title');
const overlayMessage = document.getElementById('overlay-message');
const startBtn = document.getElementById('start-btn');
const scoreElement = document.getElementById('score');
const highScoreElement = document.getElementById('high-score');
// Touch direction buttons
const btnTouchUp = document.getElementById('btn-touch-up');
const btnTouchDown = document.getElementById('btn-touch-down');
const btnTouchLeft = document.getElementById('btn-touch-left');
const btnTouchRight = document.getElementById('btn-touch-right');
// Initialize the game
function initGame() {
// Set high score
highScoreElement.textContent = highScore;
// Create grid cells
gameBoard.innerHTML = '';
gameBoard.style.setProperty('--grid-size', config.gridSize);
for (let i = 0; i < config.gridSize * config.gridSize; i++) {
const cell = document.createElement('div');
cell.classList.add('grid-cell');
cell.dataset.index = i;
// Get position from index
const x = i % config.gridSize;
const y = Math.floor(i / config.gridSize);
cell.dataset.x = x;
cell.dataset.y = y;
gameBoard.appendChild(cell);
}
// Event listeners
startBtn.addEventListener('click', startGame);
document.addEventListener('keydown', handleKeyDown);
// Touch direction event listeners
btnTouchUp.addEventListener('mousedown', () => changeDirection(0, -1));
btnTouchUp.addEventListener('touchstart', (e) => {
e.preventDefault();
changeDirection(0, -1);
});
btnTouchDown.addEventListener('mousedown', () => changeDirection(0, 1));
btnTouchDown.addEventListener('touchstart', (e) => {
e.preventDefault();
changeDirection(0, 1);
});
btnTouchLeft.addEventListener('mousedown', () => changeDirection(-1, 0));
btnTouchLeft.addEventListener('touchstart', (e) => {
e.preventDefault();
changeDirection(-1, 0);
});
btnTouchRight.addEventListener('mousedown', () => changeDirection(1, 0));
btnTouchRight.addEventListener('touchstart', (e) => {
e.preventDefault();
changeDirection(1, 0);
});
// Show start screen
showOverlay('方向键控制贪吃蛇', '使用键盘方向键或下方控制盘控制蛇的移动方向', '开始游戏');
}
// Change direction
function changeDirection(x, y) {
// Prevent 180-degree turn (snake can't reverse direction)
if ((x !== -direction.x || y !== -direction.y) &&
(x !== 0 || y !== 0)) { // Ignore if both are 0
nextDirection = {x, y};
}
}
// Start the game
function startGame() {
// Reset game state
snake = [
{x: 5, y: 10},
{x: 4, y: 10},
{x: 3, y: 10}
];
direction = {x: 1, y: 0}; // Initial direction: right
nextDirection = {x: 1, y: 0};
score = 0;
gameSpeed = config.initialSpeed;
scoreElement.textContent = score;
// Generate first food
generateFood();
// Hide overlay
hideOverlay();
// Start game loop
if (gameInterval) clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, gameSpeed);
gameStarted = true;
isPaused = false;
}
// Game loop
function gameLoop() {
if (isPaused) return;
// Update direction at the start of each move
direction = {...nextDirection};
// Move snake
const head = {...snake[0]};
head.x += direction.x;
head.y += direction.y;
// Check for collisions
if (
head.x < 0 || head.x >= config.gridSize ||
head.y < 0 || head.y >= config.gridSize ||
snake.some((segment, index) => index > 0 && segment.x === head.x && segment.y === head.y)
) {
gameOver();
return;
}
// Add new head
snake.unshift(head);
// Check for food
if (head.x === food.x && head.y === food.y) {
// Increase score
score += 10;
scoreElement.textContent = score;
// Increase speed (up to a max)
if (gameSpeed > config.maxSpeed) {
gameSpeed -= config.speedIncrease;
clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, gameSpeed);
}
// Generate new food
generateFood();
} else {
// Remove tail if no food eaten
snake.pop();
}
// Render the game
renderGame();
}
// Render the game
function renderGame() {
// Clear all cells
document.querySelectorAll('.grid-cell').forEach(cell => {
cell.classList.remove('snake', 'snake-head', 'food');
});
// Render snake
snake.forEach((segment, index) => {
const cellIndex = segment.y * config.gridSize + segment.x;
const cell = document.querySelector(`.grid-cell[data-index="${cellIndex}"]`);
if (cell) {
cell.classList.add(index === 0 ? 'snake-head' : 'snake');
}
});
// Render food
const foodIndex = food.y * config.gridSize + food.x;
const foodCell = document.querySelector(`.grid-cell[data-index="${foodIndex}"]`);
if (foodCell) {
foodCell.classList.add('food');
}
}
// Generate food at random position
function generateFood() {
let newFood;
do {
newFood = {
x: Math.floor(Math.random() * config.gridSize),
y: Math.floor(Math.random() * config.gridSize)
};
} while (snake.some(segment => segment.x === newFood.x && segment.y === newFood.y));
food = newFood;
}
// Game over
function gameOver() {
clearInterval(gameInterval);
gameStarted = false;
// Update high score if needed
if (score > highScore) {
highScore = score;
localStorage.setItem('snakeHighScore', highScore);
highScoreElement.textContent = highScore;
}
showOverlay('游戏结束', `你的分数: ${score}`, '再玩一次');
}
// Handle keyboard input
function handleKeyDown(e) {
// Only process if game is started or starting the game
if (!gameStarted && e.key === 'Enter') {
startGame();
return;
}
if (!gameStarted) return;
switch (e.key) {
case ' ':
togglePause();
break;
case 'ArrowUp':
changeDirection(0, -1);
break;
case 'ArrowDown':
changeDirection(0, 1);
break;
case 'ArrowLeft':
changeDirection(-1, 0);
break;
case 'ArrowRight':
changeDirection(1, 0);
break;
}
}
// Toggle pause
function togglePause() {
if (!gameStarted) return;
isPaused = !isPaused;
if (isPaused) {
showOverlay('游戏暂停', '按空格键继续', '继续');
} else {
hideOverlay();
}
}
// Show overlay
function showOverlay(title, message, buttonText) {
overlayTitle.textContent = title;
overlayMessage.textContent = message;
startBtn.textContent = buttonText;
gameOverlay.style.display = 'flex';
}
// Hide overlay
function hideOverlay() {
gameOverlay.style.display = 'none';
}
// Initialize the game when the page loads
window.addEventListener('DOMContentLoaded', initGame);
</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=DavidHouse2024/snake2" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>