酷炫飞翔的小鸟
script dog · v1.0.0 · 独立窗口 · 7 次安装
简介
像素飞翔的小鸟,AI助手生成
脚本源码
const GAME_WIDTH = 800;
const GAME_HEIGHT = 600;
const BIRD_X = 112;
const BIRD_WIDTH = 48;
const BIRD_HEIGHT = 36;
const PIPE_WIDTH = 76;
const GAP = 180;
const css = `
.flappy-stage {
position: relative;
width: 800px;
height: 600px;
margin: 24px auto;
overflow: hidden;
border-radius: 26px;
border: 1px solid rgba(120, 255, 255, 0.28);
background:
radial-gradient(ellipse at 50% 18%, rgba(140, 220, 255, 0.14), transparent 48%),
linear-gradient(180deg, #020617 0%, #05132f 48%, #071b34 100%);
box-shadow:
0 24px 80px rgba(0, 0, 0, 0.45),
0 0 60px rgba(0, 229, 255, 0.10),
inset 0 0 80px rgba(0, 0, 0, 0.30);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "PingFang SC", "Microsoft YaHei", monospace;
user-select: none;
touch-action: none;
}
.flappy-stage * {
box-sizing: border-box;
}
.flappy-bg {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
}
.flappy-grid {
position: absolute;
inset: 0;
z-index: 1;
opacity: 0.06;
pointer-events: none;
background-image:
linear-gradient(rgba(0, 245, 255, 0.8) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 245, 255, 0.8) 1px, transparent 1px);
background-size: 40px 40px;
transform: perspective(720px) rotateX(64deg) translateY(235px) scaleX(1.8);
-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 36%, #000 100%);
mask-image: linear-gradient(to bottom, transparent 0, #000 36%, #000 100%);
}
.flappy-horizon {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 230px;
z-index: 2;
pointer-events: none;
background:
radial-gradient(ellipse at 50% 120%, rgba(0, 229, 255, 0.16), transparent 60%),
linear-gradient(to top, rgba(0, 255, 200, 0.06), transparent 70%);
}
.flappy-cloud {
position: absolute;
border-radius: 999px;
background: rgba(255, 255, 255, 0.07);
box-shadow: 0 0 34px rgba(255, 255, 255, 0.05);
filter: blur(0.4px);
animation: flappyDrift linear infinite;
}
@keyframes flappyDrift {
from { transform: translateX(-260px); }
to { transform: translateX(860px); }
}
.flappy-bird {
position: absolute;
left: 112px;
top: 300px;
width: 48px;
height: 36px;
z-index: 8;
transform-origin: 50% 50%;
filter: drop-shadow(0 0 14px rgba(255, 207, 58, 0.55));
pointer-events: none;
}
.flappy-wing {
transform-origin: 18px 18px;
animation: flappyWing 0.14s infinite alternate ease-in-out;
}
@keyframes flappyWing {
from { transform: rotate(-8deg) translateY(-2px); }
to { transform: rotate(12deg) translateY(2px); }
}
.flappy-pipe {
position: absolute;
width: 76px;
z-index: 5;
pointer-events: none;
border: 1px solid rgba(140, 255, 255, 0.38);
background:
linear-gradient(90deg, rgba(8, 45, 41, 1) 0%, rgba(41, 190, 125, 1) 46%, rgba(10, 64, 67, 1) 100%);
box-shadow:
0 0 28px rgba(0, 229, 255, 0.16),
inset 0 0 22px rgba(255, 255, 255, 0.08),
inset -4px 0 14px rgba(0, 0, 0, 0.30),
inset 4px 0 14px rgba(255, 255, 255, 0.06);
}
.flappy-pipe::before {
content: "";
position: absolute;
inset: 3px;
border-radius: 6px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.14), transparent 28%, rgba(0, 0, 0, 0.22));
opacity: 0.55;
}
.flappy-pipe-cap {
position: absolute;
height: 26px;
border-radius: 12px;
border: 1px solid rgba(160, 255, 255, 0.58);
background:
linear-gradient(90deg, rgba(10, 45, 41, 1) 0%, rgba(50, 240, 160, 1) 45%, rgba(10, 55, 62, 1) 100%);
box-shadow:
0 0 22px rgba(0, 229, 255, 0.26),
inset 0 0 16px rgba(255, 255, 255, 0.14),
inset 0 -3px 10px rgba(0, 0, 0, 0.32);
}
.flappy-pipe-top .flappy-pipe-cap {
bottom: -13px;
}
.flappy-pipe-bottom .flappy-pipe-cap {
top: -13px;
}
.flappy-score {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 12;
min-width: 84px;
text-align: center;
font-size: 52px;
font-weight: 900;
letter-spacing: 2px;
color: #ffffff;
text-shadow:
0 0 12px rgba(0, 245, 255, 0.95),
0 0 30px rgba(255, 43, 214, 0.45),
0 4px 0 rgba(0, 0, 0, 0.25);
pointer-events: none;
}
.flappy-pop {
animation: flappyPop 0.32s cubic-bezier(0.2, 0.9, 0.2, 1);
}
@keyframes flappyPop {
0% { transform: translateX(-50%) scale(0.72) translateY(-8px); opacity: 0.2; }
55% { transform: translateX(-50%) scale(1.18) translateY(0); }
100% { transform: translateX(-50%) scale(1) translateY(0); opacity: 1; }
}
.flappy-hud {
position: absolute;
top: 18px;
left: 18px;
z-index: 12;
display: flex;
flex-direction: column;
gap: 8px;
font-size: 13px;
letter-spacing: 1.5px;
color: rgba(170, 245, 255, 0.72);
pointer-events: none;
}
.flappy-hud b {
color: #ffffff;
text-shadow: 0 0 10px rgba(0, 245, 255, 0.65);
}
.flappy-particle {
position: absolute;
z-index: 6;
width: 6px;
height: 6px;
border-radius: 50%;
pointer-events: none;
background: radial-gradient(circle, #ffffff, #00e5ff 55%, rgba(0, 229, 255, 0));
box-shadow: 0 0 12px rgba(0, 229, 255, 0.75);
}
.flappy-flash {
position: absolute;
inset: 0;
z-index: 18;
opacity: 0;
pointer-events: none;
background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.85), rgba(255, 43, 214, 0.35), transparent 68%);
mix-blend-mode: screen;
transition: opacity 0.18s ease;
}
.flappy-flash-active {
opacity: 0.72;
transition: opacity 0s;
}
.flappy-shake {
animation: flappyShake 0.34s ease;
}
@keyframes flappyShake {
0% { transform: translate(0, 0); }
18% { transform: translate(-10px, 4px) rotate(-0.6deg); }
36% { transform: translate(9px, -6px) rotate(0.8deg); }
58% { transform: translate(-6px, 3px); }
78% { transform: translate(4px, -2px); }
100% { transform: translate(0, 0); }
}
.flappy-overlay {
position: absolute;
inset: 0;
z-index: 30;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 18px;
padding: 30px;
color: #ffffff;
text-align: center;
background:
radial-gradient(circle at 50% 42%, rgba(10, 24, 48, 0.76), rgba(0, 0, 0, 0.88));
backdrop-filter: blur(9px);
-webkit-backdrop-filter: blur(9px);
opacity: 1;
visibility: visible;
transition: opacity 0.28s ease, visibility 0.28s ease;
}
.flappy-hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.flappy-title {
margin: 0;
font-size: 56px;
font-weight: 900;
letter-spacing: 8px;
background: linear-gradient(90deg, #00f5ff, #ff2bd6, #ffe259, #00f5ff);
background-size: 300% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: #eafcff;
filter: drop-shadow(0 0 18px rgba(0, 245, 255, 0.45));
animation: flappyTitleFlow 4s linear infinite;
}
@keyframes flappyTitleFlow {
from { background-position: 0% 50%; }
to { background-position: 300% 50%; }
}
.flappy-subtitle {
font-size: 17px;
letter-spacing: 4px;
color: rgba(180, 255, 255, 0.82);
text-shadow: 0 0 14px rgba(0, 229, 255, 0.28);
}
.flappy-result {
font-size: 24px;
font-weight: 700;
letter-spacing: 2px;
color: #eaffff;
text-shadow: 0 0 16px rgba(255, 43, 214, 0.32);
}
.flappy-result b {
color: #ffe259;
text-shadow: 0 0 18px rgba(255, 226, 89, 0.55);
}
.flappy-btn {
position: relative;
isolation: isolate;
min-width: 190px;
padding: 16px 42px;
margin-top: 4px;
border: 1px solid rgba(0, 245, 255, 0.72);
border-radius: 999px;
outline: none;
cursor: pointer;
overflow: hidden;
color: #eaffff;
font-size: 18px;
font-weight: 800;
letter-spacing: 4px;
background:
linear-gradient(180deg, rgba(0, 245, 255, 0.20), rgba(255, 43, 214, 0.13));
box-shadow:
0 0 24px rgba(0, 245, 255, 0.18),
inset 0 0 18px rgba(255, 255, 255, 0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}
.flappy-btn::before {
content: "";
position: absolute;
inset: -2px;
z-index: -1;
opacity: 0.55;
filter: blur(14px);
background: linear-gradient(90deg, #00f5ff, #ff2bd6, #ffe259, #00f5ff);
background-size: 300% 100%;
animation: flappyBtnGlow 4s linear infinite;
}
.flappy-btn::after {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background: rgba(2, 8, 20, 0.72);
border-radius: inherit;
}
@keyframes flappyBtnGlow {
from { background-position: 0% 50%; }
to { background-position: 300% 50%; }
}
.flappy-btn:hover {
transform: translateY(-2px);
box-shadow:
0 0 38px rgba(0, 245, 255, 0.30),
0 0 80px rgba(255, 43, 214, 0.12),
inset 0 0 22px rgba(255, 255, 255, 0.10);
background:
linear-gradient(180deg, rgba(0, 245, 255, 0.28), rgba(255, 43, 214, 0.18));
}
.flappy-btn:active {
transform: translateY(1px) scale(0.98);
}
@media (max-width: 860px) {
.flappy-stage {
width: calc(100vw - 28px);
height: 66vh;
min-height: 520px;
}
.flappy-title {
font-size: 38px;
letter-spacing: 3px;
}
.flappy-score {
font-size: 38px;
}
}
`;
if (!window.__flappyCssInjected) {
if (typeof Dog !== "undefined" && Dog.addStyle) {
Dog.addStyle(css);
} else {
const style = document.createElement("style");
style.id = "flappy-css";
style.textContent = css;
document.head.appendChild(style);
}
window.__flappyCssInjected = true;
}
const gameContainer = document.createElement("div");
gameContainer.className = "flappy-stage";
gameContainer.id = "flappy-game-container";
(document.body || document.documentElement).appendChild(gameContainer);
const bgLayer = document.createElement("div");
bgLayer.className = "flappy-bg";
gameContainer.appendChild(bgLayer);
const gridLayer = document.createElement("div");
gridLayer.className = "flappy-grid";
gameContainer.appendChild(gridLayer);
const horizon = document.createElement("div");
horizon.className = "flappy-horizon";
gameContainer.appendChild(horizon);
for (let i = 0; i < 7; i++) {
const cloud = document.createElement("div");
cloud.className = "flappy-cloud";
cloud.style.width = (90 + Math.random() * 150) + "px";
cloud.style.height = (18 + Math.random() * 28) + "px";
cloud.style.left = (Math.random() * GAME_WIDTH) + "px";
cloud.style.top = (26 + Math.random() * 280) + "px";
cloud.style.opacity = (0.035 + Math.random() * 0.07).toFixed(2);
cloud.style.animationDuration = (18 + Math.random() * 28) + "s";
cloud.style.animationDelay = (-Math.random() * 40) + "s";
bgLayer.appendChild(cloud);
}
const hud = document.createElement("div");
hud.className = "flappy-hud";
gameContainer.appendChild(hud);
const bestDisplay = document.createElement("div");
bestDisplay.id = "flappy-best";
bestDisplay.innerHTML = "最佳 <b>0</b>";
hud.appendChild(bestDisplay);
const speedDisplay = document.createElement("div");
speedDisplay.id = "flappy-speed";
speedDisplay.innerHTML = "速度 <b>280</b>";
hud.appendChild(speedDisplay);
const bird = document.createElement("div");
bird.className = "flappy-bird";
bird.innerHTML = `
<svg viewBox="0 0 52 36" width="100%" height="100%">
<defs>
<linearGradient id="flappyBirdGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#fff383"/>
<stop offset="55%" stop-color="#ffcf3a"/>
<stop offset="100%" stop-color="#ff9f2f"/>
</linearGradient>
<filter id="flappyBirdGlow" x="-70%" y="-70%" width="240%" height="240%">
<feDropShadow dx="0" dy="0" stdDeviation="3.2" flood-color="#ffcf3a" flood-opacity="0.82"/>
</filter>
</defs>
<ellipse cx="24" cy="19" rx="19.5" ry="14" fill="url(#flappyBirdGrad)" filter="url(#flappyBirdGlow)"/>
<ellipse cx="32" cy="25" rx="14" ry="7" fill="#ff5f7e" opacity="0.28"/>
<path d="M13 20 Q22 34 36 24 Q26 29 13 20" fill="#fff4b6" opacity="0.62"/>
<circle cx="34.2" cy="13" r="6.8" fill="#ffffff" stroke="#1a1a1a" stroke-width="1.1"/>
<circle cx="35.8" cy="12.6" r="2.5" fill="#0f0f0f"/>
<circle cx="36.8" cy="11.4" r="0.9" fill="#ffffff"/>
<path d="M43 16 L54.5 18.6 L43.5 23 Z" fill="#ff6f00"/>
<path d="M43 16 L54.5 18.6 L45.5 20.8 Z" fill="#ffb300"/>
<g class="flappy-wing">
<path d="M19 19 Q26 30 35 23 Q27 24 19 19" fill="#fff38c" stroke="#d6a200" stroke-width="0.6"/>
</g>
</svg>
`;
gameContainer.appendChild(bird);
const scoreDisplay = document.createElement("div");
scoreDisplay.className = "flappy-score";
scoreDisplay.textContent = "0";
gameContainer.appendChild(scoreDisplay);
const flash = document.createElement("div");
flash.className = "flappy-flash";
gameContainer.appendChild(flash);
const overlay = document.createElement("div");
overlay.className = "flappy-overlay";
const overlayTitle = document.createElement("div");
overlayTitle.className = "flappy-title";
overlayTitle.textContent = "飞翔的小鸟";
const overlayTip = document.createElement("div");
overlayTip.className = "flappy-subtitle";
overlayTip.textContent = "空格 / 点击跳跃,穿过霓虹管道";
const overlayResult = document.createElement("div");
overlayResult.className = "flappy-result";
overlayResult.textContent = "";
const startButton = document.createElement("button");
startButton.className = "flappy-btn";
startButton.textContent = "开始游戏";
overlay.appendChild(overlayTitle);
overlay.appendChild(overlayTip);
overlay.appendChild(overlayResult);
overlay.appendChild(startButton);
gameContainer.appendChild(overlay);
function getBest() {
try {
if (typeof Dog !== "undefined" && Dog.getValue) {
const value = Dog.getValue("flappy-bird-best-score", "0");
return parseInt(String(value), 10) || 0;
}
} catch (e) {
return 0;
}
return 0;
}
function saveBest(value) {
try {
if (typeof Dog !== "undefined" && Dog.setValue) {
Dog.setValue("flappy-bird-best-score", String(value));
}
} catch (e) {
return;
}
}
const gameState = {
score: 0,
best: getBest(),
isPlaying: false,
speed: 280,
gravity: 1750,
jumpForce: -520
};
const birdPhysics = {
velocity: 0,
position: GAME_HEIGHT / 2 - BIRD_HEIGHT / 2
};
const obstacles = [];
const particles = [];
let rafId = null;
let lastFrameTime = 0;
function updateHUD() {
bestDisplay.innerHTML = "最佳 <b>" + gameState.best + "</b>";
speedDisplay.innerHTML = "速度 <b>" + Math.round(gameState.speed) + "</b>";
}
function updateScore() {
scoreDisplay.textContent = gameState.score;
scoreDisplay.classList.remove("flappy-pop");
void scoreDisplay.offsetWidth;
scoreDisplay.classList.add("flappy-pop");
gameState.speed = Math.min(470, 280 + gameState.score * 7);
updateHUD();
}
function clearObstacles() {
for (let i = obstacles.length - 1; i >= 0; i--) {
gameContainer.removeChild(obstacles[i].top);
gameContainer.removeChild(obstacles[i].bottom);
}
obstacles.length = 0;
}
function clearParticles() {
for (let i = particles.length - 1; i >= 0; i--) {
gameContainer.removeChild(particles[i].el);
}
particles.length = 0;
}
function createPipeCap() {
const cap = document.createElement("div");
cap.className = "flappy-pipe-cap";
cap.style.width = (PIPE_WIDTH + 14) + "px";
cap.style.left = "-7px";
return cap;
}
function spawnObstacle() {
if (!gameState.isPlaying) return;
const minTop = 78;
const maxTop = GAME_HEIGHT - GAP - 92;
const topHeight = minTop + Math.random() * (maxTop - minTop);
const bottomTop = topHeight + GAP;
const bottomHeight = GAME_HEIGHT - bottomTop;
const x = GAME_WIDTH + PIPE_WIDTH / 2;
const topPipe = document.createElement("div");
topPipe.className = "flappy-pipe flappy-pipe-top";
topPipe.style.left = x + "px";
topPipe.style.top = "0";
topPipe.style.height = topHeight + "px";
topPipe.appendChild(createPipeCap());
const bottomPipe = document.createElement("div");
bottomPipe.className = "flappy-pipe flappy-pipe-bottom";
bottomPipe.style.left = x + "px";
bottomPipe.style.bottom = "0";
bottomPipe.style.height = bottomHeight + "px";
bottomPipe.appendChild(createPipeCap());
gameContainer.appendChild(topPipe);
gameContainer.appendChild(bottomPipe);
obstacles.push({
top: topPipe,
bottom: bottomPipe,
passed: false,
x: x,
topHeight: topHeight,
bottomTop: bottomTop
});
}
function createJumpParticles() {
const count = 7;
for (let i = 0; i < count; i++) {
const el = document.createElement("div");
el.className = "flappy-particle";
const size = 3 + Math.random() * 5;
el.style.width = size + "px";
el.style.height = size + "px";
const x = BIRD_X + BIRD_WIDTH - 6;
const y = birdPhysics.position + BIRD_HEIGHT / 2;
particles.push({
el: el,
x: x,
y: y,
vx: -70 - Math.random() * 120,
vy: -25 + Math.random() * 130,
t: 0,
life: 0.42 + Math.random() * 0.35
});
gameContainer.appendChild(el);
}
}
function updateParticles(dt) {
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.t += dt;
if (p.t >= p.life) {
gameContainer.removeChild(p.el);
particles.splice(i, 1);
continue;
}
p.x += p.vx * dt;
p.y += p.vy * dt + 45 * dt * dt;
p.el.style.left = p.x + "px";
p.el.style.top = p.y + "px";
p.el.style.opacity = Math.max(0, 1 - p.t / p.life).toFixed(2);
p.el.style.transform = "scale(" + Math.max(0.2, 1 - p.t / p.life).toFixed(2) + ")";
}
}
function updateBird(dt) {
if (!gameState.isPlaying) return;
birdPhysics.velocity += gameState.gravity * dt;
birdPhysics.position += birdPhysics.velocity * dt;
if (birdPhysics.position < -BIRD_HEIGHT / 2) {
birdPhysics.position = -BIRD_HEIGHT / 2;
birdPhysics.velocity = 0;
}
if (birdPhysics.position > GAME_HEIGHT - BIRD_HEIGHT) {
birdPhysics.position = GAME_HEIGHT - BIRD_HEIGHT;
gameOver();
return;
}
bird.style.top = birdPhysics.position + "px";
const angle = Math.max(-18, Math.min(82, birdPhysics.velocity * 0.058));
bird.style.transform = "rotate(" + angle + "deg)";
}
function checkCollision(obstacle) {
const birdRect = {
left: BIRD_X + 7,
right: BIRD_X + BIRD_WIDTH - 7,
top: birdPhysics.position + 7,
bottom: birdPhysics.position + BIRD_HEIGHT - 7
};
const pipeLeft = obstacle.x;
const pipeRight = obstacle.x + PIPE_WIDTH;
const hasHorizontalOverlap =
birdRect.right > pipeLeft &&
birdRect.left < pipeRight;
if (!hasHorizontalOverlap) return false;
return birdRect.top < obstacle.topHeight || birdRect.bottom > obstacle.bottomTop;
}
function updateObstacles(dt) {
for (let i = obstacles.length - 1; i >= 0; i--) {
const obstacle = obstacles[i];
obstacle.x -= gameState.speed * dt;
obstacle.top.style.left = obstacle.x + "px";
obstacle.bottom.style.left = obstacle.x + "px";
if (checkCollision(obstacle)) {
gameOver();
return;
}
if (!obstacle.passed && obstacle.x + PIPE_WIDTH < BIRD_X + BIRD_WIDTH / 2) {
obstacle.passed = true;
gameState.score++;
updateScore();
}
if (obstacle.x + PIPE_WIDTH < -30) {
gameContainer.removeChild(obstacle.top);
gameContainer.removeChild(obstacle.bottom);
obstacles.splice(i, 1);
}
}
if (obstacles.length === 0 || obstacles[obstacles.length - 1].x < GAME_WIDTH - 255) {
spawnObstacle();
}
}
function gameLoop(timestamp) {
if (!gameState.isPlaying) return;
if (!lastFrameTime) lastFrameTime = timestamp;
const dt = Math.min((timestamp - lastFrameTime) / 1000, 0.05);
lastFrameTime = timestamp;
if (dt <= 0) {
rafId = requestAnimationFrame(gameLoop);
return;
}
updateBird(dt);
if (!gameState.isPlaying) return;
updateObstacles(dt);
if (!gameState.isPlaying) return;
updateParticles(dt);
rafId = requestAnimationFrame(gameLoop);
}
function startLoop() {
if (rafId) cancelAnimationFrame(rafId);
lastFrameTime = 0;
rafId = requestAnimationFrame(gameLoop);
}
function flap() {
if (!gameState.isPlaying) return;
birdPhysics.velocity = gameState.jumpForce;
createJumpParticles();
}
function startGame() {
if (gameState.isPlaying) return;
gameState.isPlaying = true;
gameState.score = 0;
gameState.speed = 280;
birdPhysics.velocity = 0;
birdPhysics.position = GAME_HEIGHT / 2 - BIRD_HEIGHT / 2;
scoreDisplay.textContent = "0";
bird.style.top = birdPhysics.position + "px";
bird.style.transform = "rotate(0deg)";
clearObstacles();
clearParticles();
overlay.classList.add("flappy-hidden");
overlayResult.textContent = "";
startButton.textContent = "重新开始";
updateHUD();
spawnObstacle();
startLoop();
}
function gameOver() {
if (!gameState.isPlaying) return;
gameState.isPlaying = false;
if (rafId) {
cancelAnimationFrame(rafId);
rafId = null;
}
let isNewBest = false;
if (gameState.score > gameState.best) {
gameState.best = gameState.score;
isNewBest = true;
saveBest(gameState.best);
}
flash.classList.add("flappy-flash-active");
gameContainer.classList.add("flappy-shake");
setTimeout(function () {
flash.classList.remove("flappy-flash-active");
}, 110);
setTimeout(function () {
gameContainer.classList.remove("flappy-shake");
}, 360);
overlayTitle.textContent = isNewBest ? "新纪录" : "游戏结束";
overlayTip.textContent = isNewBest ? "你刷新了最佳成绩,继续挑战" : "保持节奏,再试一次";
overlayResult.innerHTML = "得分 <b>" + gameState.score + "</b> · 最佳 <b>" + gameState.best + "</b>";
startButton.textContent = "重新开始";
overlay.classList.remove("flappy-hidden");
updateHUD();
try {
if (typeof Dog !== "undefined" && Dog.notification) {
Dog.notification("游戏结束!最终得分: " + gameState.score + ",最佳: " + gameState.best, "飞翔的小鸟");
}
} catch (e) {
return;
}
}
startButton.addEventListener("click", function (e) {
e.stopPropagation();
startGame();
});
gameContainer.addEventListener("pointerdown", function (e) {
if (e.target.closest(".flappy-btn")) return;
if (!gameState.isPlaying) {
startGame();
} else {
flap();
}
e.preventDefault();
});
document.addEventListener("keydown", function (e) {
const isActionKey =
e.code === "Space" ||
e.code === "Enter" ||
e.code === "ArrowUp" ||
e.key === " " ||
e.key === "Enter" ||
e.key === "ArrowUp";
if (!isActionKey) return;
e.preventDefault();
if (!gameState.isPlaying) {
startGame();
} else {
flap();
}
});
bird.style.top = birdPhysics.position + "px";
updateHUD();