console
<!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,viewport-fit=cover">
<meta name="theme-color" content="#05101a">
<title>小鲸鱼大冒险</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body {
width: 100%; height: 100%; overflow: hidden;
background: #05101a; touch-action: none;
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
user-select: none; -webkit-user-select: none;
}
canvas { display: block; width: 100vw; height: 100vh; }
#ui {
position: fixed; inset: 0; display: flex; flex-direction: column;
align-items: center; justify-content: center; color: #fff;
background: rgba(5, 16, 26, 0.85); backdrop-filter: blur(4px);
transition: opacity 0.3s; pointer-events: none; opacity: 1;
}
#ui.hide { opacity: 0; pointer-events: none; }
#ui h1 { font-size: 28px; margin-bottom: 10px; color: #8FE3FF; text-shadow: 0 2px 10px rgba(143, 227, 255, 0.3); }
#ui p { font-size: 15px; color: #9cb8cc; margin-bottom: 30px; text-align: center; line-height: 1.6; padding: 0 20px; }
#ui button {
padding: 14px 40px; font-size: 18px; font-weight: bold; color: #05101a;
background: linear-gradient(135deg, #8FE3FF, #2AA0DC);
border: none; border-radius: 30px; box-shadow: 0 10px 20px rgba(42, 160, 220, 0.4);
pointer-events: auto; cursor: pointer; transition: transform 0.1s;
}
#ui button:active { transform: scale(0.95); }
#score-display {
position: fixed; top: 40px; left: 0; width: 100%; text-align: center;
font-size: 32px; font-weight: bold; color: #fff; pointer-events: none;
text-shadow: 0 2px 10px rgba(0,0,0,0.8); z-index: 10;
}
#final-score { font-size: 20px; color: #FFD700; margin-bottom: 25px; }
</style>
</head>
<body>
<canvas id="c"></canvas>
<div id="score-display">0</div>
<div id="ui">
<h1 id="ui-title">小鲸鱼大冒险</h1>
<p id="ui-desc">手指按住屏幕左右滑动<br>躲避海胆,吃星星!</p>
<div id="final-score"></div>
<button id="start-btn">开始游戏</button>
</div>
<script>
(function(){
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
const ui = document.getElementById('ui');
const uiTitle = document.getElementById('ui-title');
const uiDesc = document.getElementById('ui-desc');
const btn = document.getElementById('start-btn');
const finalScoreEl = document.getElementById('final-score');
const scoreDisplay = document.getElementById('score-display');
let vw, vh, dpr;
function resize() {
vw = window.innerWidth; vh = window.innerHeight;
dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = vw * dpr; canvas.height = vh * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
window.addEventListener('resize', resize);
resize();
// 游戏状态
let playing = false;
let score = 0;
let speed = 4;
let time = 0;
// 玩家鲸鱼
const player = { x: vw / 2, y: vh - 120, r: 25, targetX: vw / 2 };
// 物体数组
let stars = [];
let enemies = [];
// 触摸控制
function handleMove(e) {
if (!playing) return;
let clientX = e.touches ? e.touches[0].clientX : e.clientX;
player.targetX = clientX;
}
canvas.addEventListener('touchstart', handleMove, { passive: false });
canvas.addEventListener('touchmove', handleMove, { passive: false });
canvas.addEventListener('mousedown', handleMove);
canvas.addEventListener('mousemove', e => { if(e.buttons === 1) handleMove(e); });
// 开始游戏
btn.addEventListener('click', () => {
ui.classList.add('hide');
playing = true;
score = 0;
speed = 4;
time = 0;
stars = [];
enemies = [];
player.x = vw / 2;
player.targetX = vw / 2;
scoreDisplay.innerText = '0';
lastTime = performance.now();
requestAnimationFrame(loop);
});
// 画鲸鱼
function drawWhale(x, y, r) {
ctx.save();
ctx.translate(x, y);
// 尾巴
ctx.fillStyle = '#2AA0DC';
ctx.beginPath();
ctx.moveTo(0, r * 0.5);
ctx.quadraticCurveTo(-r * 1.5, r * 1.2, -r * 1.2, r * 0.5);
ctx.quadraticCurveTo(-r * 0.5, r * 0.8, 0, r * 0.8);
ctx.quadraticCurveTo(r * 0.5, r * 0.8, r * 1.2, r * 0.5);
ctx.quadraticCurveTo(r * 1.5, r * 1.2, 0, r * 0.5);
ctx.fill();
// 身体
const grad = ctx.createLinearGradient(0, -r, 0, r);
grad.addColorStop(0, '#A5E7FF');
grad.addColorStop(1, '#2AA0DC');
ctx.fillStyle = grad;
ctx.beginPath();
ctx.ellipse(0, 0, r, r * 1.2, 0, 0, Math.PI * 2);
ctx.fill();
// 肚皮
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.ellipse(0, r * 0.3, r * 0.7, r * 0.8, 0, 0, Math.PI * 2);
ctx.fill();
// 眼睛
ctx.fillStyle = '#12324a';
ctx.beginPath();
ctx.arc(-r * 0.4, -r * 0.2, r * 0.12, 0, Math.PI * 2);
ctx.arc(r * 0.4, -r * 0.2, r * 0.12, 0, Math.PI * 2);
ctx.fill();
// 腮红
ctx.fillStyle = 'rgba(255, 145, 170, 0.6)';
ctx.beginPath();
ctx.ellipse(-r * 0.6, r * 0.1, r * 0.15, r * 0.08, 0, 0, Math.PI * 2);
ctx.ellipse(r * 0.6, r * 0.1, r * 0.15, r * 0.08, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// 画星星
function drawStar(x, y, r) {
ctx.save();
ctx.translate(x, y);
ctx.fillStyle = '#FFD700';
ctx.shadowColor = '#FFD700';
ctx.shadowBlur = 15;
ctx.beginPath();
for (let i = 0; i < 5; i++) {
ctx.lineTo(Math.cos((18 + i * 72) / 180 * Math.PI) * r,
-Math.sin((18 + i * 72) / 180 * Math.PI) * r);
ctx.lineTo(Math.cos((54 + i * 72) / 180 * Math.PI) * r * 0.4,
-Math.sin((54 + i * 72) / 180 * Math.PI) * r * 0.4);
}
ctx.closePath();
ctx.fill();
ctx.restore();
}
// 画海胆
function drawUrchin(x, y, r) {
ctx.save();
ctx.translate(x, y);
ctx.fillStyle = '#2a0b2e';
ctx.strokeStyle = '#6b1f6e';
ctx.lineWidth = 3;
// 刺
ctx.beginPath();
for (let i = 0; i < 12; i++) {
const angle = (i / 12) * Math.PI * 2;
ctx.moveTo(Math.cos(angle) * r * 0.8, Math.sin(angle) * r * 0.8);
ctx.lineTo(Math.cos(angle) * r * 1.4, Math.sin(angle) * r * 1.4);
}
ctx.stroke();
// 身体
ctx.beginPath();
ctx.arc(0, 0, r, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// 碰撞检测
function checkCollision(c1, c2) {
const dx = c1.x - c2.x;
const dy = c1.y - c2.y;
const dist = Math.sqrt(dx * dx + dy * dy);
return dist < (c1.r + c2.r) * 0.8; // 留点容错率
}
// 游戏结束
function gameOver() {
playing = false;
uiTitle.innerText = "游戏结束";
uiDesc.innerText = "不小心撞到海胆啦!";
finalScoreEl.innerText = "得分:" + score;
btn.innerText = "再试一次";
ui.classList.remove('hide');
// 手机震动反馈(如果支持)
if (navigator.vibrate) navigator.vibrate([100, 50, 100]);
}
// 主循环
let lastTime = 0;
let spawnTimer = 0;
function loop(now) {
if (!playing) return;
const dt = Math.min(0.05, (now - lastTime) / 1000);
lastTime = now;
time += dt;
spawnTimer += dt;
// 难度随时间增加
speed = 4 + time * 0.2;
// 更新玩家位置 (平滑跟随手指)
player.x += (player.targetX - player.x) * 10 * dt;
// 限制在屏幕内
if (player.x < player.r) player.x = player.r;
if (player.x > vw - player.r) player.x = vw - player.r;
// 生成物体
if (spawnTimer > 0.6) {
spawnTimer = 0;
const isEnemy = Math.random() < 0.65; // 65%概率生成敌人
const x = Math.random() * (vw - 80) + 40;
if (isEnemy) {
enemies.push({ x, y: -50, r: 18, speed: speed * 1.1 });
} else {
stars.push({ x, y: -50, r: 15, speed: speed });
}
}
// 更新星星
for (let i = stars.length - 1; i >= 0; i--) {
const s = stars[i];
s.y += s.speed * 60 * dt;
if (s.y > vh + 50) {
stars.splice(i, 1);
} else if (checkCollision(player, s)) {
score += 10;
scoreDisplay.innerText = score;
stars.splice(i, 1);
}
}
// 更新敌人
for (let i = enemies.length - 1; i >= 0; i--) {
const e = enemies[i];
e.y += e.speed * 60 * dt;
if (e.y > vh + 50) {
enemies.splice(i, 1);
} else if (checkCollision(player, e)) {
gameOver();
return;
}
}
// --- 渲染 ---
// 背景
ctx.fillStyle = '#05101a';
ctx.fillRect(0, 0, vw, vh);
// 背景水泡
ctx.fillStyle = 'rgba(255,255,255,0.05)';
for (let i = 0; i < 10; i++) {
const bx = (i * 123) % vw;
const by = vh - ((time * 40 + i * 80) % (vh + 100));
ctx.beginPath();
ctx.arc(bx, by, 10 + i * 2, 0, Math.PI * 2);
ctx.fill();
}
stars.forEach(s => drawStar(s.x, s.y, s.r));
enemies.forEach(e => drawUrchin(e.x, e.y, e.r));
// 玩家稍微有点上下浮动
const bobY = player.y + Math.sin(time * 6) * 3;
drawWhale(player.x, bobY, player.r);
requestAnimationFrame(loop);
}
})();
</script>
</body>
</html>