Spaces:
Running
Running
| <html lang="ko"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>네온 벽돌깨기 게임</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| min-height: 100vh; | |
| background: #111; | |
| font-family: 'Noto Sans KR', sans-serif; | |
| color: #fff; | |
| } | |
| canvas { | |
| background: rgba(0,0,0,0.8); | |
| border-radius: 8px; | |
| box-shadow: 0 0 20px rgba(0,255,255,0.3); | |
| margin: 20px 0; | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 15px; | |
| margin-bottom: 20px; | |
| } | |
| button { | |
| padding: 12px 24px; | |
| font-size: 16px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| background: transparent; | |
| color: #fff; | |
| border: 2px solid #0ff; | |
| box-shadow: 0 0 10px #0ff; | |
| text-shadow: 0 0 5px #0ff; | |
| transition: all 0.3s; | |
| } | |
| button:hover { | |
| background: rgba(0,255,255,0.2); | |
| box-shadow: 0 0 20px #0ff; | |
| } | |
| .score { | |
| font-size: 24px; | |
| margin-bottom: 15px; | |
| color: #0ff; | |
| text-shadow: 0 0 10px #0ff; | |
| } | |
| .speed-control { | |
| margin: 15px 0; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .speed-control label { | |
| color: #0ff; | |
| text-shadow: 0 0 5px #0ff; | |
| } | |
| input[type="range"] { | |
| width: 200px; | |
| accent-color: #0ff; | |
| } | |
| .speed-value { | |
| color: #0ff; | |
| min-width: 30px; | |
| text-align: center; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="score">점수: <span id="score">0</span></div> | |
| <canvas id="gameCanvas" width="480" height="320"></canvas> | |
| <div class="speed-control"> | |
| <label>속도 조절:</label> | |
| <input type="range" id="speedControl" min="1" max="10" value="3"> | |
| <span class="speed-value" id="speedValue">3</span> | |
| </div> | |
| <div class="controls"> | |
| <button id="startBtn">시작하기</button> | |
| <button id="resetBtn">다시하기</button> | |
| </div> | |
| <script> | |
| const canvas = document.getElementById("gameCanvas"); | |
| const ctx = canvas.getContext("2d"); | |
| const startBtn = document.getElementById("startBtn"); | |
| const resetBtn = document.getElementById("resetBtn"); | |
| const scoreElement = document.getElementById("score"); | |
| const speedControl = document.getElementById("speedControl"); | |
| const speedValue = document.getElementById("speedValue"); | |
| let score = 0; | |
| let gameRunning = false; | |
| let baseSpeed = 3; | |
| const colors = [ | |
| '#ff0000', '#00ff00', '#0000ff', '#ff00ff', '#ffff00', '#00ffff' | |
| ]; | |
| const ball = { | |
| x: canvas.width / 2, | |
| y: canvas.height - 30, | |
| dx: baseSpeed, | |
| dy: -baseSpeed, | |
| radius: 7, | |
| color: '#fff' | |
| }; | |
| const paddle = { | |
| width: 75, | |
| height: 10, | |
| x: (canvas.width - 75) / 2, | |
| color: '#0ff' | |
| }; | |
| const brickRowCount = 5; | |
| const brickColumnCount = 8; | |
| const brickWidth = 50; | |
| const brickHeight = 20; | |
| const brickPadding = 10; | |
| const brickOffsetTop = 30; | |
| const brickOffsetLeft = 25; | |
| const bricks = []; | |
| for(let c=0; c<brickColumnCount; c++) { | |
| bricks[c] = []; | |
| for(let r=0; r<brickRowCount; r++) { | |
| bricks[c][r] = { | |
| x: 0, | |
| y: 0, | |
| status: 1, | |
| color: colors[Math.floor(Math.random() * colors.length)] | |
| }; | |
| } | |
| } | |
| let rightPressed = false; | |
| let leftPressed = false; | |
| function drawBall() { | |
| ctx.beginPath(); | |
| ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2); | |
| ctx.fillStyle = ball.color; | |
| ctx.fill(); | |
| ctx.shadowBlur = 15; | |
| ctx.shadowColor = ball.color; | |
| ctx.closePath(); | |
| } | |
| function drawPaddle() { | |
| ctx.beginPath(); | |
| ctx.rect(paddle.x, canvas.height-paddle.height, paddle.width, paddle.height); | |
| ctx.fillStyle = paddle.color; | |
| ctx.fill(); | |
| ctx.shadowBlur = 15; | |
| ctx.shadowColor = paddle.color; | |
| ctx.closePath(); | |
| } | |
| function drawBricks() { | |
| for(let c=0; c<brickColumnCount; c++) { | |
| for(let r=0; r<brickRowCount; r++) { | |
| if(bricks[c][r].status === 1) { | |
| const brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft; | |
| const brickY = (r*(brickHeight+brickPadding))+brickOffsetTop; | |
| bricks[c][r].x = brickX; | |
| bricks[c][r].y = brickY; | |
| ctx.beginPath(); | |
| ctx.rect(brickX, brickY, brickWidth, brickHeight); | |
| ctx.fillStyle = bricks[c][r].color; | |
| ctx.fill(); | |
| ctx.shadowBlur = 15; | |
| ctx.shadowColor = bricks[c][r].color; | |
| ctx.closePath(); | |
| } | |
| } | |
| } | |
| } | |
| function updateSpeed() { | |
| const newSpeed = parseInt(speedControl.value); | |
| speedValue.textContent = newSpeed; | |
| const speedMultiplier = newSpeed / baseSpeed; | |
| ball.dx = Math.sign(ball.dx) * Math.abs(baseSpeed * speedMultiplier); | |
| ball.dy = Math.sign(ball.dy) * Math.abs(baseSpeed * speedMultiplier); | |
| } | |
| speedControl.addEventListener('input', updateSpeed); | |
| function collisionDetection() { | |
| for(let c=0; c<brickColumnCount; c++) { | |
| for(let r=0; r<brickRowCount; r++) { | |
| const b = bricks[c][r]; | |
| if(b.status === 1) { | |
| if(ball.x > b.x && ball.x < b.x+brickWidth && ball.y > b.y && ball.y < b.y+brickHeight) { | |
| ball.dy = -ball.dy; | |
| b.status = 0; | |
| ball.color = b.color; | |
| score++; | |
| scoreElement.textContent = score; | |
| if(score === brickRowCount*brickColumnCount) { | |
| alert("축하합니다! 승리하셨습니다!"); | |
| gameRunning = false; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| function draw() { | |
| if (!gameRunning) return; | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| ctx.shadowBlur = 0; | |
| drawBricks(); | |
| drawBall(); | |
| drawPaddle(); | |
| collisionDetection(); | |
| if(ball.x + ball.dx > canvas.width-ball.radius || ball.x + ball.dx < ball.radius) { | |
| ball.dx = -ball.dx; | |
| } | |
| if(ball.y + ball.dy < ball.radius) { | |
| ball.dy = -ball.dy; | |
| } else if(ball.y + ball.dy > canvas.height-ball.radius) { | |
| if(ball.x > paddle.x && ball.x < paddle.x + paddle.width) { | |
| ball.dy = -ball.dy; | |
| } else { | |
| alert("게임 오버"); | |
| gameRunning = false; | |
| return; | |
| } | |
| } | |
| if(rightPressed && paddle.x < canvas.width-paddle.width) { | |
| paddle.x += 7; | |
| } | |
| else if(leftPressed && paddle.x > 0) { | |
| paddle.x -= 7; | |
| } | |
| ball.x += ball.dx; | |
| ball.y += ball.dy; | |
| requestAnimationFrame(draw); | |
| } | |
| function keyDownHandler(e) { | |
| if(e.key === "Right" || e.key === "ArrowRight") { | |
| rightPressed = true; | |
| } | |
| else if(e.key === "Left" || e.key === "ArrowLeft") { | |
| leftPressed = true; | |
| } | |
| } | |
| function keyUpHandler(e) { | |
| if(e.key === "Right" || e.key === "ArrowRight") { | |
| rightPressed = false; | |
| } | |
| else if(e.key === "Left" || e.key === "ArrowLeft") { | |
| leftPressed = false; | |
| } | |
| } | |
| function mouseMoveHandler(e) { | |
| const relativeX = e.clientX - canvas.offsetLeft; | |
| if(relativeX > 0 && relativeX < canvas.width) { | |
| paddle.x = relativeX - paddle.width/2; | |
| } | |
| } | |
| function resetGame() { | |
| score = 0; | |
| scoreElement.textContent = score; | |
| ball.x = canvas.width / 2; | |
| ball.y = canvas.height - 30; | |
| updateSpeed(); | |
| ball.color = '#fff'; | |
| paddle.x = (canvas.width - paddle.width) / 2; | |
| for(let c=0; c<brickColumnCount; c++) { | |
| for(let r=0; r<brickRowCount; r++) { | |
| bricks[c][r].status = 1; | |
| bricks[c][r].color = colors[Math.floor(Math.random() * colors.length)]; | |
| } | |
| } | |
| if (!gameRunning) { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| drawBricks(); | |
| drawBall(); | |
| drawPaddle(); | |
| } | |
| } | |
| document.addEventListener("keydown", keyDownHandler, false); | |
| document.addEventListener("keyup", keyUpHandler, false); | |
| document.addEventListener("mousemove", mouseMoveHandler, false); | |
| startBtn.addEventListener("click", () => { | |
| if (!gameRunning) { | |
| gameRunning = true; | |
| draw(); | |
| } | |
| }); | |
| resetBtn.addEventListener("click", () => { | |
| gameRunning = false; | |
| resetGame(); | |
| }); | |
| resetGame(); | |
| </script> | |
| </body> | |
| </html> |