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> |