构建一个完整的打砖块街机游戏,使用HTML
来自 Wikiprompt,自由的提示词百科全书
构建一个完整的打砖块街机游戏,使用HTML 一个全面的提示,用于在单个HTML文件中生成一个功能完整的Breakout游戏,涵盖游戏玩法、控制、视觉效果、音频和技术细节。
提示词内容收藏
🌐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>NEON BREAKOUT</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
body {
background: #0a0f1e;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Segoe UI', system-ui, sans-serif;
touch-action: none;
overflow: hidden;
}
.game-container {
background: #0f172a;
border-radius: 2rem;
padding: 1.5rem;
box-shadow: 0 0 40px rgba(0, 200, 255, 0.3), 0 0 80px rgba(0, 100, 255, 0.2);
border: 1px solid #2a3a5e;
}
canvas {
display: block;
width: 100%;
max-width: 900px;
height: auto;
border-radius: 1rem;
background: #0b1120;
box-shadow: inset 0 0 30px #00000066, 0 0 20px #00a6ff40;
cursor: none;
touch-action: none;
}
.hud {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0.2rem 1rem 0.2rem;
color: #b0e0ff;
font-weight: 600;
letter-spacing: 1px;
text-shadow: 0 0 8px #00a6ff;
}
.hud span {
background: #1e293b;
padding: 0.4rem 1.2rem;
border-radius: 3rem;
font-size: 1.1rem;
border: 1px solid #3b5a8a;
box-shadow: 0 0 10px #00a6ff33;
}
.hud .level-badge {
background: #1e293b;
padding: 0.4rem 1.2rem;
border-radius: 3rem;
font-size: 1.1rem;
border: 1px solid #ffaa33;
color: #ffd966;
text-shadow: 0 0 8px #ffaa33;
}
.hud .lives {
color: #ff6b9d;
text-shadow: 0 0 12px #ff4d7d;
font-size: 1.4rem;
}
.hud .score {
color: #7df9ff;
}
.controls-hint {
color: #5f7a9f;
font-size: 0.8rem;
text-align: center;
margin-top: 0.8rem;
letter-spacing: 1px;
border-top: 1px solid #1e2f4a;
padding-top: 0.8rem;
}
.controls-hint kbd {
background: #1e293b;
padding: 0.1rem 0.6rem;
border-radius: 0.5rem;
border: 1px solid #3b5a8a;
color: #aad0ff;
}
</style>
</head>
<body>
<div class="game-container">
<div class="hud">
<span class="score">SCORE: 0</span>
<span class="level-badge">LEVEL 1</span>
<span class="lives">❤️❤️❤️</span>
</div>
<canvas id="gameCanvas" width="900" height="600"></canvas>
<div class="controls-hint">
<kbd>←</kbd> <kbd>→</kbd> / mouse · <kbd>SPACE</kbd> launch · <kbd>P</kbd> pause
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.querySelector('.score');
const levelEl = document.querySelector('.level-badge');
const livesEl = document.querySelector('.lives');
// ---------- GAME STATE ----------
let gameState = 'START'; // START, PLAYING, PAUSED, GAMEOVER, WIN
let score = 0;
let lives = 3;
let level = 1;
let ballSpeedMultiplier = 1.0;
let particles = [];
let powerUpActive = { multiBall: false, widePaddle: false, slowBall: false };
let powerUpTimers = { multiBall: 0, widePaddle: 0, slowBall: 0 };
let frameCount = 0;
// paddle
const paddle = {
x: 450, y: 560, w: 120, h: 14,
color: '#00e5ff'
};
const defaultPaddleWidth = 120;
let mouseX = 450;
// ball(s)
let balls = [];
const ballRadius = 8;
const defaultBallSpeed = 5.5;
// bricks
let bricks = [];
const brickRows = 6;
const brickCols = 10;
const brickWidth = 70;
const brickHeight = 22;
const brickPadding = 8;
const brickOffsetTop = 60;
const brickOffsetLeft = 40;
// input
let keys = { left: false, right: false };
// ---------- AUDIO (synthetic) ----------
function playTone(freq, duration = 0.1, type = 'square', volume = 0.15) {
try {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type;
osc.frequency.value = freq;
gain.gain.value = volume;
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
setTimeout(() => {
osc.stop();
audioCtx.close();
}, duration * 1000);
} catch (e) { /* audio not critical */ }
}
function soundBounce() { playTone(220, 0.08, 'triangle', 0.1); }
function soundBrick() { playTone(520, 0.1, 'square', 0.12); }
function soundGameOver() { playTone(120, 0.5, 'sawtooth', 0.2); }
function soundWin() { playTone(880, 0.3, 'sine', 0.15); setTimeout(() => playTone(1100, 0.3, 'sine', 0.15), 150); }
// ---------- BRICK LAYOUT ----------
function createBricks() {
bricks = [];
const colors = ['#ff4d6d', '#ff9e4d', '#ffd166', '#6bffb8', '#4dc9ff', '#c77dff'];
for (let r = 0; r < brickRows; r++) {
for (let c = 0; c < brickCols; c++) {
const x = brickOffsetLeft + c * (brickWidth + brickPadding);
const y = brickOffsetTop + r * (brickHeight + brickPadding);
bricks.push({
x, y, w: brickWidth, h: brickHeight,
color: colors[r % colors.length],
alive: true,
glow: 0.6 + Math.random() * 0.4
});
}
}
}
// ---------- BALLS ----------
function createBall(x = paddle.x, y = paddle.y - 20, speed = defaultBallSpeed) {
return {
x, y,
vx: (Math.random() > 0.5 ? 1 : -1) * speed * 0.6,
vy: -speed,
radius: ballRadius,
trail: []
};
}
function resetBalls() {
balls = [createBall()];
powerUpActive.multiBall = false;
powerUpTimers.multiBall = 0;
}
// ---------- PARTICLES ----------
function spawnBrickParticles(brick) {
for (let i = 0; i < 14; i++) {
particles.push({
x: brick.x + brick.w / 2,
y: brick.y + brick.h / 2,
vx: (Math.random() - 0.5) * 8,
vy: (Math.random() - 0.5) * 8 - 2,
life: 1.0,
size: 3 + Math.random() * 5,
color: brick.color
});
}
}
function updateParticles() {
particles = particles.filter(p => p.life > 0);
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
p.vy += 0.1;
p.life -= 0.02;
});
}
// ---------- COLLISION & GAME LOGIC ----------
function ballWallCollision(ball) {
if (ball.x - ball.radius < 0) { ball.x = ball.radius; ball.vx = -ball.vx; soundBounce(); }
if (ball.x + ball.radius > canvas.width) { ball.x = canvas.width - ball.radius; ball.vx = -ball.vx; soundBounce(); }
if (ball.y - ball.radius < 0) { ball.y = ball.radius; ball.vy = -ball.vy; soundBounce(); }
}
function ballPaddleCollision(ball) {
if (ball.y + ball.radius >= paddle.y && ball.y + ball.radius <= paddle.y + paddle.h + 8 &&
ball.x >= paddle.x - ball.radius && ball.x <= paddle.x + paddle.w + ball.radius && ball.vy > 0) {
// angle based on hit position
const hitPos = (ball.x - paddle.x) / paddle.w; // 0..1
const angle = (hitPos - 0.5) * Math.PI * 0.7;
const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
ball.vx = Math.sin(angle) * speed;
ball.vy = -Math.abs(Math.cos(angle) * speed);
ball.y = paddle.y - ball.radius - 1;
soundBounce();
}
}
function ballBrickCollision(ball) {
for (let i = 0; i < bricks.length; i++) {
const b = bricks[i];
if (!b.alive) continue;
if (ball.x + ball.radius > b.x && ball.x - ball.radius < b.x + b.w &&
ball.y + ball.radius > b.y && ball.y - ball.radius < b.y + b.h) {
b.alive = false;
score += 10;
updateScore();
spawnBrickParticles(b);
soundBrick();
// simple reflection
const overlapLeft = (ball.x + ball.radius) - b.x;
const overlapRight = (b.x + b.w) - (ball.x - ball.radius);
const overlapTop = (ball.y + ball.radius) - b.y;
const overlapBottom = (b.y + b.h) - (ball.y - ball.radius);
const minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);
if (minOverlap === overlapLeft || minOverlap === overlapRight) {
ball.vx = -ball.vx;
} else {
ball.vy = -ball.vy;
}
// power-up chance (10%)
if (Math.random() < 0.1) {
applyRandomPowerUp();
}
break;
}
}
}
function applyRandomPowerUp() {
const r = Math.random();
if (r < 0.33) {
// multi-ball
if (balls.length < 4) {
const newBall = createBall(balls[0].x, balls[0].y, defaultBallSpeed * 0.9);
newBall.vx = -balls[0].vx * 0.8;
newBall.vy = -balls[0].vy * 0.9;
balls.push(newBall);
powerUpActive.multiBall = true;
powerUpTimers.multiBall = 300;
}
} else if (r < 0.66) {
// wide paddle
paddle.w = defaultPaddleWidth * 1.5;
powerUpActive.widePaddle = true;
powerUpTimers.widePaddle = 400;
} else {
// slow ball
balls.forEach(b => { b.vx *= 0.7; b.vy *= 0.7; });
powerUpActive.slowBall = true;
powerUpTimers.slowBall = 300;
}
}
function updatePowerUps() {
if (powerUpTimers.widePaddle > 0) {
powerUpTimers.widePaddle--;
if (powerUpTimers.widePaddle === 0) {
paddle.w = defaultPaddleWidth;
powerUpActive.widePaddle = false;
}
}
if (powerUpTimers.multiBall > 0) {
powerUpTimers.multiBall--;
if (powerUpTimers.multiBall === 0) {
powerUpActive.multiBall = false;
}
}
if (powerUpTimers.slowBall > 0) {
powerUpTimers.slowBall--;
if (powerUpTimers.slowBall === 0) {
powerUpActive.slowBall = false;
}
}
}
// ---------- UI UPDATES ----------
function updateScore() {
scoreEl.textContent = `SCORE: ${score}`;
}
function updateLives() {
const hearts = lives > 0 ? '❤️'.repeat(lives) + '🖤'.repeat(Math.max(0, 3 - lives)) : '💔';
livesEl.innerHTML = hearts;
}
function updateLevel() {
levelEl.textContent = `LEVEL ${level}`;
}
// ---------- GAME STATE ----------
function resetGame() {
score = 0;
lives = 3;
level = 1;
ballSpeedMultiplier = 1.0;
updateScore();
updateLives();
updateLevel();
createBricks();
resetBalls();
paddle.x = canvas.width / 2 - paddle.w / 2;
paddle.w = defaultPaddleWidth;
powerUpActive = { multiBall: false, widePaddle: false, slowBall: false };
powerUpTimers = { multiBall: 0, widePaddle: 0, slowBall: 0 };
particles = [];
gameState = 'START';
}
function checkWin() {
if (bricks.every(b => !b.alive)) {
gameState = 'WIN';
soundWin();
}
}
function checkGameOver() {
if (lives <= 0) {
gameState = 'GAMEOVER';
soundGameOver();
}
}
// ---------- UPDATE ----------
function update() {
if (gameState !== 'PLAYING') return;
// paddle movement
if (keys.left) paddle.x -= 8;
if (keys.right) paddle.x += 8;
paddle.x = Math.max(0, Math.min(canvas.width - paddle.w, paddle.x));
// mouse follow
if (mouseX > 0 && mouseX < canvas.width) {
paddle.x = Math.max(0, Math.min(canvas.width - paddle.w, mouseX - paddle.w / 2));
}
// update balls
balls.forEach(ball => {
ball.x += ball.vx;
ball.y += ball.vy;
ballWallCollision(ball);
ballPaddleCollision(ball);
ballBrickCollision(ball);
});
// remove balls that fell below
const before = balls.length;
balls = balls.filter(b => b.y < canvas.height + 30);
if (balls.length < before) {
// lost a ball
if (balls.length === 0) {
lives--;
updateLives();
if (lives > 0) {
resetBalls();
gameState = 'START';
} else {
checkGameOver();
}
}
}
updateParticles();
updatePowerUps();
checkWin();
}
// ---------- RENDER ----------
function drawBackground() {
const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
grad.addColorStop(0, '#0b1120');
grad.addColorStop(1, '#0f1a2f');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// subtle grid
ctx.strokeStyle = '#1e2f4a22';
ctx.lineWidth = 0.5;
for (let i = 0; i < canvas.width; i += 40) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, canvas.height);
ctx.stroke();
}
for (let i = 0; i < canvas.height; i += 40) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(canvas.width, i);
ctx.stroke();
}
}
function drawBricks() {
bricks.forEach(b => {
if (!b.alive) return;
ctx.shadowColor = b.color;
ctx.shadowBlur = 15;
ctx.fillStyle = b.color;
ctx.beginPath();
ctx.roundRect(b.x, b.y, b.w, b.h, 6);
ctx.fill();
// inner glow
ctx.shadowBlur = 5;
ctx.fillStyle = 'rgba(255,255,255,0.2)';
ctx.beginPath();
ctx.roundRect(b.x + 2, b.y + 2, b.w - 4, b.h - 4, 4);
ctx.fill();
});
ctx.shadowBlur = 0;
}
function drawPaddle() {
ctx.shadowColor = '#00e5ff';
ctx.shadowBlur = 20;
ctx.fillStyle = '#00e5ff';
ctx.beginPath();
ctx.roundRect(paddle.x, paddle.y, paddle.w, paddle.h, 8);
ctx.fill();
ctx.shadowBlur = 0;
// paddle core
ctx.fillStyle = '#aaf0ff';
ctx.beginPath();
ctx.roundRect(paddle.x + 4, paddle.y + 2, paddle.w - 8, paddle.h - 4, 6);
ctx.fill();
}
function drawBalls() {
balls.forEach(b => {
ctx.shadowColor = '#ffd966';
ctx.shadowBlur = 20;
ctx.fillStyle = '#ffd966';
ctx.beginPath();
ctx.arc(b.x, b.y, b.radius, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
ctx.fillStyle = '#fff7d6';
ctx.beginPath();
ctx.arc(b.x - 2, b.y - 2, b.radius * 0.5, 0, Math.PI * 2);
ctx.fill();
});
}
function drawParticles() {
particles.forEach(p => {
ctx.globalAlpha = Math.max(0, p.life);
ctx.fillStyle = p.color;
ctx.shadowColor = p.color;
ctx.shadowBlur = 10;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1;
ctx.shadowBlur = 0;
}
function drawOverlays() {
ctx.fillStyle = 'rgba(0,0,0,0.6)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 36px "Segoe UI", sans-serif';
ctx.textAlign = 'center';
ctx.shadowColor = '#00a6ff';
ctx.shadowBlur = 20;
if (gameState === 'START') {
ctx.fillText('🚀 PRESS SPACE', canvas.width/2, canvas.height/2 - 20);
ctx.font = '20px sans-serif';
ctx.fillText('move: mouse / ← →', canvas.width/2, canvas.height/2 + 30);
} else if (gameState === 'PAUSED') {
ctx.fillText('⏸ PAUSED', canvas.width/2, canvas.height/2);
ctx.font = '20px sans-serif';
ctx.fillText('press P to resume', canvas.width/2, canvas.height/2 + 40);
} else if (gameState === 'GAMEOVER') {
ctx.fillStyle = '#ff6b6b';
ctx.fillText('GAME OVER', canvas.width/2, canvas.height/2 - 20);
ctx.font = '24px sans-serif';
ctx.fillStyle = '#fff';
ctx.fillText(`score: ${score} · press SPACE`, canvas.width/2, canvas.height/2 + 30);
} else if (gameState === 'WIN') {
ctx.fillStyle = '#ffd966';
ctx.fillText('🏆 YOU WIN!', canvas.width/2, canvas.height/2 - 20);
ctx.font = '24px sans-serif';
ctx.fillStyle = '#fff';
ctx.fillText(`score: ${score} · press SPACE`, canvas.width/2, canvas.height/2 + 30);
}
ctx.shadowBlur = 0;
}
// roundRect helper
CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
this.moveTo(x + r, y);
this.lineTo(x + w - r, y);
this.quadraticCurveTo(x + w, y, x + w, y + r);
this.lineTo(x + w, y + h - r);
this.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
this.lineTo(x + r, y + h);
this.quadraticCurveTo(x, y + h, x, y + h - r);
this.lineTo(x, y + r);
this.quadraticCurveTo(x, y, x + r, y);
return this;
};
function render() {
drawBackground();
drawBricks();
drawPaddle();
drawBalls();
drawParticles();
if (gameState === 'START' || gameState === 'PAUSED' || gameState === 'GAMEOVER' || gameState === 'WIN') {
drawOverlays();
}
}
// ---------- GAME LOOP ----------
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
// ---------- EVENT LISTENERS ----------
function handleKeyDown(e) {
const key = e.key;
if (key === 'ArrowLeft') { keys.left = true; e.preventDefault(); }
if (key === 'ArrowRight') { keys.right = true; e.preventDefault(); }
if (key === ' ') {
e.preventDefault();
if (gameState === 'START') {
gameState = 'PLAYING';
// launch ball if needed
if (balls.length === 0) resetBalls();
balls.forEach(b => { if (b.vy === 0) b.vy = -defaultBallSpeed; });
} else if (gameState === 'GAMEOVER' || gameState === 'WIN') {
resetGame();
gameState = 'START';
}
}
if (key === 'p' || key === 'P') {
if (gameState === 'PLAYING') gameState = 'PAUSED';
else if (gameState === 'PAUSED') gameState = 'PLAYING';
}
}
function handleKeyUp(e) {
if (e.key === 'ArrowLeft') keys.left = false;
if (e.key === 'ArrowRight') keys.right = false;
}
function handleMouseMove(e) {
const rect = canvas.getBoundingClientRect();
const scaleX = canvas.width / rect.width;
mouseX = (e.clientX - rect.left) * scaleX;
}
function handleTouchMove(e) {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
mouseX = (touch.clientX - rect.left) * (canvas.width / rect.width);
}
// ---------- INIT ----------
function init() {
resetGame();
gameState = 'START';
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('touchmove', handleTouchMove, { passive: false });
canvas.addEventListener('touchstart', (e) => e.preventDefault());
gameLoop();
}
init();
})();
</script>
</body>
</html>
用法
此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论