ybnb 提交于 2026-10-17 02:00:00 · 分支 main
· 父提交
vgwpWNdh
新增
game.html
@@ -0,0 +1,311 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+<title>贪吃蛇小游戏</title>
+<style>
+ :root{
+ --bg:#0b1220; --panel:#111c30; --line:rgba(148,163,184,.16);
+ --txt:#e2e8f0; --muted:#94a3b8; --accent:#34d399; --food:#fb7185;
+ }
+ *{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
+ html,body{margin:0;padding:0;}
+ body{
+ min-height:100vh; background:radial-gradient(1200px 600px at 50% -10%,#16233d,#0b1220 60%);
+ color:var(--txt); font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
+ display:flex; flex-direction:column; align-items:center; justify-content:flex-start;
+ padding:16px 14px 28px; gap:12px;
+ }
+ h1{font-size:20px; margin:6px 0 0; letter-spacing:.5px; font-weight:600;}
+ .hud{
+ width:100%; max-width:460px; display:flex; align-items:center; justify-content:space-between;
+ gap:10px; font-size:15px; color:var(--muted);
+ }
+ .hud b{color:var(--txt); font-size:18px; font-variant-numeric:tabular-nums;}
+ .board{
+ position:relative; width:min(92vw,440px); aspect-ratio:1/1;
+ border-radius:16px; overflow:hidden; background:#0f172a;
+ box-shadow:0 18px 40px rgba(0,0,0,.45), inset 0 0 0 1px var(--line);
+ touch-action:none;
+ }
+ canvas{display:block; width:100%; height:100%;}
+ .overlay{
+ position:absolute; inset:0; display:flex; flex-direction:column;
+ align-items:center; justify-content:center; gap:12px; text-align:center;
+ background:rgba(9,14,26,.82); backdrop-filter:blur(3px); padding:20px;
+ transition:opacity .15s ease;
+ }
+ .overlay.hidden{display:none;}
+ .overlay h2{margin:0; font-size:22px; font-weight:600;}
+ .overlay p{margin:0; font-size:14px; color:var(--muted); line-height:1.6; max-width:280px;}
+ button{
+ font:inherit; color:#04231a; background:var(--accent); border:0; border-radius:12px;
+ padding:11px 22px; font-weight:600; cursor:pointer; transition:transform .08s ease, filter .15s ease;
+ }
+ button:active{transform:scale(.97);}
+ button.ghost{background:#1e293b; color:var(--txt);}
+ .pad{
+ display:grid; grid-template-columns:repeat(3,60px); grid-template-rows:repeat(2,54px);
+ gap:8px; margin-top:2px;
+ }
+ .pad button{
+ background:#1b2740; color:var(--txt); border:1px solid var(--line);
+ border-radius:12px; font-size:20px; padding:0; display:flex; align-items:center; justify-content:center;
+ }
+ .pad button:active{background:#243455;}
+ .pad .up{grid-column:2; grid-row:1;}
+ .pad .left{grid-column:1; grid-row:2;}
+ .pad .down{grid-column:2; grid-row:2;}
+ .pad .right{grid-column:3; grid-row:2;}
+ .hint{margin:2px 0 0; font-size:12.5px; color:var(--muted); text-align:center; line-height:1.7; max-width:440px;}
+ .back{font-size:13px; color:var(--muted); text-decoration:none;}
+ .back:hover{color:var(--txt);}
+</style>
+</head>
+<body>
+ <h1>贪吃蛇</h1>
+ <div class="hud">
+ <span>得分 <b id="score">0</b></span>
+ <span>最高 <b id="best">0</b></span>
+ </div>
+
+ <div class="board" id="board">
+ <canvas id="cv"></canvas>
+ <div class="overlay" id="overlay">
+ <h2 id="ovTitle">贪吃蛇</h2>
+ <p id="ovText">用方向键或滑动控制蛇,吃到食物得分,撞墙或撞到自己就结束。</p>
+ <button id="startBtn">开始游戏</button>
+ </div>
+ </div>
+
+ <div class="pad" id="pad">
+ <button class="up" data-dir="up" aria-label="上">▲</button>
+ <button class="left" data-dir="left" aria-label="左">◀</button>
+ <button class="down" data-dir="down" aria-label="下">▼</button>
+ <button class="right" data-dir="right" aria-label="右">▶</button>
+ </div>
+
+ <p class="hint">键盘:方向键 / WASD 移动,空格暂停。手机:在棋盘上滑动,或点下方方向键。</p>
+ <a class="back" href="index.html">← 返回首页</a>
+
+<script>
+(function () {
+ 'use strict';
+
+ var COLS = 20, ROWS = 20;
+ var cv = document.getElementById('cv');
+ var ctx = cv.getContext('2d');
+ var board = document.getElementById('board');
+ var scoreEl = document.getElementById('score');
+ var bestEl = document.getElementById('best');
+ var overlay = document.getElementById('overlay');
+ var ovTitle = document.getElementById('ovTitle');
+ var ovText = document.getElementById('ovText');
+ var startBtn = document.getElementById('startBtn');
+
+ var state = 'ready'; // ready | running | paused | over
+ var snake, dir, nextDir, food, score;
+ var best = 0;
+ var moveInterval = 130; // 每步毫秒数,随得分加快
+ var acc = 0, last = 0, rafId = 0;
+ var cell = 20, cssSize = 0;
+
+ try {
+ var stored = localStorage.getItem('snake_best');
+ if (stored) { best = parseInt(stored, 10) || 0; }
+ } catch (e) {}
+ bestEl.textContent = String(best);
+
+ function resize() {
+ var rect = board.getBoundingClientRect();
+ var size = Math.floor(Math.min(rect.width, rect.height));
+ if (!size || size < 80) { size = 300; }
+ var dpr = window.devicePixelRatio || 1;
+ cv.width = Math.floor(size * dpr);
+ cv.height = Math.floor(size * dpr);
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+ cssSize = size;
+ cell = size / COLS;
+ draw();
+ }
+
+ function reset() {
+ snake = [{ x: 10, y: 10 }, { x: 9, y: 10 }, { x: 8, y: 10 }];
+ dir = { x: 1, y: 0 };
+ nextDir = { x: 1, y: 0 };
+ score = 0;
+ moveInterval = 130;
+ acc = 0; last = 0;
+ scoreEl.textContent = '0';
+ placeFood();
+ draw();
+ }
+
+ function placeFood() {
+ var free = [];
+ for (var y = 0; y < ROWS; y++) {
+ for (var x = 0; x < COLS; x++) {
+ var hit = false;
+ for (var i = 0; i < snake.length; i++) {
+ if (snake[i].x === x && snake[i].y === y) { hit = true; break; }
+ }
+ if (!hit) { free.push({ x: x, y: y }); }
+ }
+ }
+ if (!free.length) { return; }
+ food = free[Math.floor(Math.random() * free.length)];
+ }
+
+ function setDir(d) {
+ if (d.x === -dir.x && d.y === -dir.y) { return; } // 不能直接反向
+ if (d.x === -nextDir.x && d.y === -nextDir.y) { return; } // 同一帧内不能连转两次
+ nextDir = { x: d.x, y: d.y };
+ }
+
+ function step() {
+ dir = nextDir;
+ var head = { x: snake[0].x + dir.x, y: snake[0].y + dir.y };
+
+ if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) { gameOver(); return; }
+ for (var i = 0; i < snake.length - 1; i++) {
+ if (snake[i].x === head.x && snake[i].y === head.y) { gameOver(); return; }
+ }
+
+ snake.unshift(head);
+ if (head.x === food.x && head.y === food.y) {
+ score++;
+ scoreEl.textContent = String(score);
+ if (score > best) {
+ best = score;
+ bestEl.textContent = String(best);
+ try { localStorage.setItem('snake_best', String(best)); } catch (e) {}
+ }
+ moveInterval = Math.max(60, 130 - score * 4);
+ placeFood();
+ } else {
+ snake.pop();
+ }
+ }
+
+ function loop(ts) {
+ if (state !== 'running') { return; }
+ if (!last) { last = ts; }
+ var dt = ts - last;
+ last = ts;
+ acc += dt;
+ var guard = 0;
+ while (acc >= moveInterval && state === 'running') {
+ acc -= moveInterval;
+ step();
+ guard++;
+ if (guard > 8) { acc = 0; break; }
+ }
+ draw();
+ if (state === 'running') { rafId = requestAnimationFrame(loop); }
+ }
+
+ function showOverlay(title, text, btn) {
+ ovTitle.textContent = title;
+ ovText.textContent = text;
+ startBtn.textContent = btn;
+ overlay.classList.remove('hidden');
+ }
+ function hideOverlay() { overlay.classList.add('hidden'); }
+
+ function startGame() {
+ reset();
+ state = 'running';
+ hideOverlay();
+ last = 0; acc = 0;
+ rafId = requestAnimationFrame(loop);
+ }
+ function pauseGame() {
+ if (state !== 'running') { return; }
+ state = 'paused';
+ if (rafId) { cancelAnimationFrame(rafId); }
+ showOverlay('已暂停', '当前得分 ' + score + ',按空格或点下面的按钮继续。', '继续');
+ }
+ function resumeGame() {
+ if (state !== 'paused') { return; }
+ state = 'running';
+ hideOverlay();
+ last = 0; acc = 0;
+ rafId = requestAnimationFrame(loop);
+ }
+ function gameOver() {
+ state = 'over';
+ if (rafId) { cancelAnimationFrame(rafId); }
+ draw();
+ showOverlay('游戏结束', '本局得分 ' + score + ',最高 ' + best + '。', '再玩一次');
+ }
+
+ startBtn.addEventListener('click', function () {
+ if (state === 'paused') { resumeGame(); }
+ else { startGame(); }
+ });
+
+ document.addEventListener('keydown', function (e) {
+ var k = e.key;
+ var d = null;
+ if (k === 'ArrowUp' || k === 'w' || k === 'W') { d = { x: 0, y: -1 }; }
+ else if (k === 'ArrowDown' || k === 's' || k === 'S') { d = { x: 0, y: 1 }; }
+ else if (k === 'ArrowLeft' || k === 'a' || k === 'A') { d = { x: -1, y: 0 }; }
+ else if (k === 'ArrowRight' || k === 'd' || k === 'D') { d = { x: 1, y: 0 }; }
+ else if (k === ' ' || k === 'Spacebar') {
+ e.preventDefault();
+ if (state === 'running') { pauseGame(); }
+ else if (state === 'paused') { resumeGame(); }
+ else { startGame(); }
+ return;
+ }
+ if (d) {
+ if (k.indexOf('Arrow') === 0) { e.preventDefault(); }
+ if (state === 'ready' || state === 'over') { startGame(); }
+ if (state === 'running') { setDir(d); }
+ }
+ });
+
+ var DIRS = { up: { x: 0, y: -1 }, down: { x: 0, y: 1 }, left: { x: -1, y: 0 }, right: { x: 1, y: 0 } };
+ Array.prototype.forEach.call(document.querySelectorAll('.pad button'), function (btn) {
+ btn.addEventListener('click', function (e) {
+ e.preventDefault();
+ var d = DIRS[btn.getAttribute('data-dir')];
+ if (!d) { return; }
+ if (state === 'ready' || state === 'over') { startGame(); }
+ if (state === 'running') { setDir(d); }
+ });
+ });
+
+ var touchStart = null;
+ board.addEventListener('touchstart', function (e) {
+ if (e.touches.length === 1) {
+ touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY };
+ }
+ }, { passive: true });
+ board.addEventListener('touchmove', function (e) { e.preventDefault(); }, { passive: false });
+ board.addEventListener('touchend', function (e) {
+ if (!touchStart) { return; }
+ var t = e.changedTouches[0];
+ var dx = t.clientX - touchStart.x;
+ var dy = t.clientY - touchStart.y;
+ touchStart = null;
+ var adx = Math.abs(dx), ady = Math.abs(dy);
+ if (Math.max(adx, ady) < 24) { return; }
+ if (state === 'ready' || state === 'over') { startGame(); }
+ if (state === 'running') {
+ if (adx > ady) { setDir({ x: dx > 0 ? 1 : -1, y: 0 }); }
+ else { setDir({ x: 0, y: dy > 0 ? 1 : -1 }); }
+ }
+ }, { passive: true });
+
+ window.addEventListener('resize', resize);
+ window.addEventListener('orientationchange', function () { setTimeout(resize, 200); });
+
+ reset();
+ resize();
+ showOverlay('贪吃蛇', '用方向键或滑动控制蛇,吃到食物得分,撞墙或撞到自己就结束。', '开始游戏');
+})();
+</script>
+</body>
+</html>
修改
index.html
@@ -1 +1,2 @@
<h1>Hello from AI</h1>
+<p><a href="game.html">来玩个小游戏:贪吃蛇 →</a></p>