test / index.html
nkp2mr's picture
Add 2 files
a0bb411 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Breakout Game | 打砖块</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
font-family: 'Press Start 2P', cursive;
overflow: hidden;
background: linear-gradient(135deg, #1a1a2e, #16213e);
}
#gameCanvas {
box-shadow: 0 0 20px rgba(0, 255, 255, 0.5);
border-radius: 8px;
}
.brick {
transition: all 0.2s ease;
}
.brick:hover {
transform: scale(1.05);
}
.power-up {
animation: float 2s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.game-over {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
</style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center text-white">
<div class="text-center mb-6">
<h1 class="text-4xl md:text-5xl mb-4 text-cyan-400">BREAKOUT</h1>
<h2 class="text-xl md:text-2xl text-cyan-300">打砖块游戏</h2>
</div>
<div class="relative">
<canvas id="gameCanvas" width="800" height="500" class="bg-gray-900"></canvas>
<!-- Start Screen -->
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 p-8 rounded-lg">
<h3 class="text-3xl text-cyan-400 mb-6">BREAKOUT</h3>
<p class="text-lg mb-8 text-center">Use mouse or arrow keys to move the paddle.<br>Break all bricks to win!</p>
<button id="startButton" class="px-8 py-3 bg-cyan-600 hover:bg-cyan-500 rounded-lg text-lg font-bold transition-all transform hover:scale-105">
START GAME
</button>
<div class="mt-6 text-sm text-gray-400">
<p>Controls: ← → or Mouse</p>
<p class="mt-2">Press P to pause</p>
</div>
</div>
<!-- Game Over Screen -->
<div id="gameOverScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-80 p-8 rounded-lg">
<h3 class="text-3xl text-red-500 mb-4 game-over">GAME OVER</h3>
<p id="finalScore" class="text-xl mb-6">Score: 0</p>
<button id="restartButton" class="px-8 py-3 bg-cyan-600 hover:bg-cyan-500 rounded-lg text-lg font-bold transition-all transform hover:scale-105">
PLAY AGAIN
</button>
</div>
<!-- Win Screen -->
<div id="winScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-80 p-8 rounded-lg">
<h3 class="text-3xl text-green-400 mb-4">YOU WIN!</h3>
<p id="winScore" class="text-xl mb-6">Score: 0</p>
<button id="nextLevelButton" class="px-8 py-3 bg-green-600 hover:bg-green-500 rounded-lg text-lg font-bold transition-all transform hover:scale-105 mb-4">
NEXT LEVEL
</button>
<button id="menuButton" class="px-8 py-3 bg-cyan-600 hover:bg-cyan-500 rounded-lg text-lg font-bold transition-all transform hover:scale-105">
MAIN MENU
</button>
</div>
<!-- Pause Screen -->
<div id="pauseScreen" class="absolute inset-0 hidden flex-col items-center justify-center bg-black bg-opacity-80 p-8 rounded-lg">
<h3 class="text-3xl text-yellow-400 mb-6">PAUSED</h3>
<button id="resumeButton" class="px-8 py-3 bg-cyan-600 hover:bg-cyan-500 rounded-lg text-lg font-bold transition-all transform hover:scale-105">
RESUME
</button>
</div>
</div>
<div class="mt-6 flex justify-between w-full max-w-2xl px-4">
<div class="bg-gray-800 bg-opacity-70 px-6 py-3 rounded-lg">
<span class="text-gray-400">Level:</span>
<span id="levelDisplay" class="ml-2 text-yellow-400">1</span>
</div>
<div class="bg-gray-800 bg-opacity-70 px-6 py-3 rounded-lg">
<span class="text-gray-400">Score:</span>
<span id="scoreDisplay" class="ml-2 text-green-400">0</span>
</div>
<div class="bg-gray-800 bg-opacity-70 px-6 py-3 rounded-lg">
<span class="text-gray-400">Lives:</span>
<span id="livesDisplay" class="ml-2 text-red-400">3</span>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const winScreen = document.getElementById('winScreen');
const pauseScreen = document.getElementById('pauseScreen');
const scoreDisplay = document.getElementById('scoreDisplay');
const livesDisplay = document.getElementById('livesDisplay');
const levelDisplay = document.getElementById('levelDisplay');
const finalScore = document.getElementById('finalScore');
const winScore = document.getElementById('winScore');
// Game state
let gameRunning = false;
let gamePaused = false;
let score = 0;
let lives = 3;
let level = 1;
// Paddle
const paddleHeight = 15;
const paddleWidth = 100;
let paddleX = (canvas.width - paddleWidth) / 2;
// Ball
const ballRadius = 10;
let ballX = canvas.width / 2;
let ballY = canvas.height - 30;
let ballSpeedX = 5;
let ballSpeedY = -5;
// Bricks
const brickRowCount = 5;
const brickColumnCount = 9;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 60;
const brickOffsetLeft = 30;
let bricks = [];
// Power-ups
const powerUps = [];
const powerUpTypes = ['extraLife', 'expandPaddle', 'slowBall', 'multiBall'];
// Initialize bricks
function initBricks() {
bricks = [];
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft;
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop;
// Different colors for different rows
let color;
switch(r) {
case 0: color = '#FF5252'; break;
case 1: color = '#FFD740'; break;
case 2: color = '#69F0AE'; break;
case 3: color = '#40C4FF'; break;
case 4: color = '#E040FB'; break;
}
// Higher rows give more points
const points = (brickRowCount - r) * 10;
// Some bricks are unbreakable
const unbreakable = Math.random() < 0.1 && r === 0;
bricks[c][r] = { x: brickX, y: brickY, width: brickWidth, height: brickHeight,
color: color, visible: true, points: points, unbreakable: unbreakable };
}
}
}
// Event listeners
document.addEventListener('keydown', keyDownHandler);
document.addEventListener('keyup', keyUpHandler);
document.addEventListener('mousemove', mouseMoveHandler);
document.getElementById('startButton').addEventListener('click', startGame);
document.getElementById('restartButton').addEventListener('click', startGame);
document.getElementById('nextLevelButton').addEventListener('click', nextLevel);
document.getElementById('menuButton').addEventListener('click', showMenu);
document.getElementById('resumeButton').addEventListener('click', togglePause);
// Keyboard controls
let rightPressed = false;
let leftPressed = false;
function keyDownHandler(e) {
if (e.key === 'Right' || e.key === 'ArrowRight') {
rightPressed = true;
} else if (e.key === 'Left' || e.key === 'ArrowLeft') {
leftPressed = true;
} else if (e.key === 'p' || e.key === 'P') {
togglePause();
}
}
function keyUpHandler(e) {
if (e.key === 'Right' || e.key === 'ArrowRight') {
rightPressed = false;
} else if (e.key === 'Left' || e.key === 'ArrowLeft') {
leftPressed = false;
}
}
// Mouse controls
function mouseMoveHandler(e) {
if (!gameRunning || gamePaused) return;
const relativeX = e.clientX - canvas.offsetLeft;
if (relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
// Keep paddle within canvas
if (paddleX < 0) {
paddleX = 0;
} else if (paddleX + paddleWidth > canvas.width) {
paddleX = canvas.width - paddleWidth;
}
}
}
// Collision detection
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
const brick = bricks[c][r];
if (brick.visible && !brick.unbreakable) {
if (ballX > brick.x && ballX < brick.x + brickWidth &&
ballY > brick.y && ballY < brick.y + brickHeight) {
ballSpeedY = -ballSpeedY;
brick.visible = false;
score += brick.points;
scoreDisplay.textContent = score;
// Chance to drop power-up
if (Math.random() < 0.2) {
spawnPowerUp(brick.x + brickWidth / 2, brick.y + brickHeight / 2);
}
// Check if all bricks are destroyed
if (checkWin()) {
showWinScreen();
}
}
}
}
}
}
// Power-up functions
function spawnPowerUp(x, y) {
const type = powerUpTypes[Math.floor(Math.random() * powerUpTypes.length)];
powerUps.push({
x: x,
y: y,
width: 20,
height: 20,
type: type,
speed: 2,
active: false
});
}
function activatePowerUp(powerUp) {
switch(powerUp.type) {
case 'extraLife':
lives++;
livesDisplay.textContent = lives;
break;
case 'expandPaddle':
paddleWidth = 150;
setTimeout(() => { paddleWidth = 100; }, 10000);
break;
case 'slowBall':
ballSpeedX *= 0.7;
ballSpeedY *= 0.7;
setTimeout(() => {
ballSpeedX /= 0.7;
ballSpeedY /= 0.7;
}, 8000);
break;
case 'multiBall':
// For simplicity, we'll just add score here
score += 50;
scoreDisplay.textContent = score;
break;
}
}
// Check if player won
function checkWin() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].visible && !bricks[c][r].unbreakable) {
return false;
}
}
}
return true;
}
// Draw functions
function drawBall() {
ctx.beginPath();
ctx.arc(ballX, ballY, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = '#FFFFFF';
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.roundRect(paddleX, canvas.height - paddleHeight - 10, paddleWidth, paddleHeight, 10);
ctx.fillStyle = '#40C4FF';
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) {
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].visible) {
ctx.beginPath();
ctx.roundRect(bricks[c][r].x, bricks[c][r].y, brickWidth, brickHeight, 4);
ctx.fillStyle = bricks[c][r].unbreakable ? '#555555' : bricks[c][r].color;
ctx.fill();
ctx.strokeStyle = '#000000';
ctx.stroke();
ctx.closePath();
// Draw crack pattern for unbreakable bricks
if (bricks[c][r].unbreakable) {
ctx.beginPath();
ctx.moveTo(bricks[c][r].x + 5, bricks[c][r].y + 5);
ctx.lineTo(bricks[c][r].x + brickWidth - 5, bricks[c][r].y + brickHeight - 5);
ctx.moveTo(bricks[c][r].x + brickWidth - 5, bricks[c][r].y + 5);
ctx.lineTo(bricks[c][r].x + 5, bricks[c][r].y + brickHeight - 5);
ctx.strokeStyle = '#333333';
ctx.lineWidth = 2;
ctx.stroke();
ctx.closePath();
}
}
}
}
}
function drawPowerUps() {
powerUps.forEach(powerUp => {
if (!powerUp.active) {
ctx.beginPath();
ctx.roundRect(powerUp.x - powerUp.width / 2, powerUp.y - powerUp.height / 2,
powerUp.width, powerUp.height, 4);
// Different colors for different power-ups
switch(powerUp.type) {
case 'extraLife': ctx.fillStyle = '#FF5252'; break;
case 'expandPaddle': ctx.fillStyle = '#69F0AE'; break;
case 'slowBall': ctx.fillStyle = '#40C4FF'; break;
case 'multiBall': ctx.fillStyle = '#E040FB'; break;
}
ctx.fill();
ctx.strokeStyle = '#FFFFFF';
ctx.stroke();
ctx.closePath();
// Draw icon
ctx.font = '12px Arial';
ctx.fillStyle = '#FFFFFF';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
let symbol = '';
switch(powerUp.type) {
case 'extraLife': symbol = '♥'; break;
case 'expandPaddle': symbol = '⇆'; break;
case 'slowBall': symbol = '⏱'; break;
case 'multiBall': symbol = '✦'; break;
}
ctx.fillText(symbol, powerUp.x, powerUp.y);
}
});
}
// Game control functions
function startGame() {
gameRunning = true;
gamePaused = false;
score = 0;
lives = 3;
level = 1;
scoreDisplay.textContent = score;
livesDisplay.textContent = lives;
levelDisplay.textContent = level;
initBricks();
resetBall();
startScreen.classList.add('hidden');
gameOverScreen.classList.add('hidden');
winScreen.classList.add('hidden');
pauseScreen.classList.add('hidden');
requestAnimationFrame(gameLoop);
}
function nextLevel() {
level++;
levelDisplay.textContent = level;
// Increase difficulty
ballSpeedX *= 1.1;
ballSpeedY *= 1.1;
initBricks();
resetBall();
winScreen.classList.add('hidden');
requestAnimationFrame(gameLoop);
}
function showMenu() {
winScreen.classList.add('hidden');
startScreen.classList.remove('hidden');
}
function togglePause() {
if (!gameRunning) return;
gamePaused = !gamePaused;
if (gamePaused) {
pauseScreen.classList.remove('hidden');
} else {
pauseScreen.classList.add('hidden');
requestAnimationFrame(gameLoop);
}
}
function showGameOver() {
gameRunning = false;
finalScore.textContent = `Score: ${score}`;
gameOverScreen.classList.remove('hidden');
}
function showWinScreen() {
gameRunning = false;
winScore.textContent = `Score: ${score}`;
winScreen.classList.remove('hidden');
}
function resetBall() {
ballX = canvas.width / 2;
ballY = canvas.height - 30;
ballSpeedX = 5 * (Math.random() > 0.5 ? 1 : -1);
ballSpeedY = -5;
paddleX = (canvas.width - paddleWidth) / 2;
}
// Main game loop
function gameLoop() {
if (!gameRunning || gamePaused) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw game elements
drawBricks();
drawBall();
drawPaddle();
drawPowerUps();
// Collision detection
collisionDetection();
// Ball movement
ballX += ballSpeedX;
ballY += ballSpeedY;
// Wall collision
if (ballX + ballSpeedX > canvas.width - ballRadius || ballX + ballSpeedX < ballRadius) {
ballSpeedX = -ballSpeedX;
}
if (ballY + ballSpeedY < ballRadius) {
ballSpeedY = -ballSpeedY;
} else if (ballY + ballSpeedY > canvas.height - ballRadius) {
// Paddle collision
if (ballX > paddleX && ballX < paddleX + paddleWidth) {
// Calculate bounce angle based on where ball hits paddle
const hitPosition = (ballX - (paddleX + paddleWidth / 2)) / (paddleWidth / 2);
const angle = hitPosition * Math.PI / 3; // Max 60 degrees
ballSpeedX = 5 * Math.sin(angle);
ballSpeedY = -5 * Math.cos(angle);
} else {
// Missed paddle
lives--;
livesDisplay.textContent = lives;
if (lives <= 0) {
showGameOver();
} else {
resetBall();
}
}
}
// Paddle movement
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
// Power-up movement and collision
for (let i = powerUps.length - 1; i >= 0; i--) {
const powerUp = powerUps[i];
if (!powerUp.active) {
powerUp.y += powerUp.speed;
// Check if power-up hits paddle
if (powerUp.y + powerUp.height / 2 > canvas.height - paddleHeight - 10 &&
powerUp.y - powerUp.height / 2 < canvas.height - 10 &&
powerUp.x + powerUp.width / 2 > paddleX &&
powerUp.x - powerUp.width / 2 < paddleX + paddleWidth) {
activatePowerUp(powerUp);
powerUps.splice(i, 1);
}
// Check if power-up falls off screen
if (powerUp.y > canvas.height) {
powerUps.splice(i, 1);
}
}
}
requestAnimationFrame(gameLoop);
}
// Initial setup
initBricks();
</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=nkp2mr/test" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>