[벽돌깨기]html 캔버스로 게임 만들기 - 7(벽돌 충돌감지)
만든 벽돌들 사이로 공이 빠져나가는 모습이 보입니다
공과 캔버스는 충돌처리가 되어있지만 벽돌은 충돌처리가 없습니다
그래서 공이 벽돌들을 그냥 지나치고 있습니다
충돌 감지를 통해서 공과 벽돌(블록)이 부딪히면 벽돌이 사라지는 것을 구현해보겠습니다
블록 충돌 감지
반복문을 통해서 블록들이 공과 충돌했는지 확인해주는 함수를 만듭니다
function collisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
// calculations
}
}
}
collisionDetection() 함수는 충돌 감지하는 함수로 주석처리된 calculations 부분에서 충돌했을 경우
해당 블록을 어떻게 처리할지 결정합니다
패들(Paddle)때와 마찬가지로 공의 좌표와 블록의 좌표가 겹치는 여부에 따라서 충돌 여부가 결정됩니다
충돌 조건은 아래와 같은 4가지입니다
- 공의 x 좌표는 벽돌의 x 좌표보다 커야 한다.
- 공의 x 좌표는 벽돌의 x 좌표 + 가로길이보다 작아야 한다.
- 공의 y 좌표는 벽돌의 y 좌표보다 커야 한다.
- 공의 y 좌표는 벽돌의 y 좌표 + 높이보다 작아야 한다.
4가지 조건을 충족시키는 코드를 만들어보겠습니다
function collisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
}
}
}
}
collisionDetection 함수를 keyUpHandler() 함수 아래에 추가합니다
공이 벽돌을 만나면 방향이 바뀌게 됩니다
블록 충돌 후 사라지게 만들기
이제 충돌한 블록이 사라지게 하는 방법입니다
현재는 블록과 충돌하면 공은 방향만 바꾸기 때문입니다
var bricks = [];
for(var c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(var r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
기존에 벽돌 만드는 for문을 수정합니다
다른 부분은 bricks[c][r] = { x: 0, y: 0, status: 1 }; 이 부분으로 status 속성 추가로 만들어 줍니다
그리고 drawBricks() 함수도 수정합니다
status가 1인 경우에만 블록을 그리고 아닌 경우 블록을 그리지 않습니다
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var 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 = '#0095DD';
ctx.fill();
ctx.closePath();
}
}
}
}
이대로 실행하면 게임은 변화가 없습니다
이유는 collisionDetection 함수도 status를 반영해야 하기 때문입니다
var b = bricks[c][r]; 아래에 새로운 조건문을 하나 더 만듭니다
조건 "status == 1"이 충족할 경우 공의 방향을 바꾸고 status를 0으로 만듭니다
그러면 벽돌과 공이 충돌할 경우 벽돌은 사라지고 공은 방향을 바꾸게 됩니다
function collisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
b.status = 0;
}
}
}
}
}
collisionDetection 함수의 위치는 draw() 함수의 drawPaddle() 아래에 위치하면 됩니다
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
collisionDetection();
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if(y + dy < ballRadius) {
dy = -dy;
}
else if(y + dy > canvas.height-ballRadius) {
if(x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
}
else {
clearInterval(game);
alert('GAME OVER');
document.location.reload();
}
}
블록과 부딪힌 공은 방향을 바꾸고 블록은 사라지게 구현했습니다
요약
코드 전체
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>벽돌깨기</title>
<style>
* { padding: 0; margin: 0; }
canvas { background: #eee; display: block; margin: 0 auto; }
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 2;
var dy = -2;
var ballRadius = 10;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width-paddleWidth)/2;
var rightPressed = false;
var leftPressed = false;
var brickRowCount = 3;
var brickColumnCount = 5;
var brickWidth = 75;
var brickHeight = 20;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = 30;
var bricks = [];
for(var c=0; c<brickColumnCount; c++) {
bricks[c] = [];
for(var r=0; r<brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 };
}
}
document.addEventListener('keydown', keyDownHandler, false);
document.addEventListener('keyup', keyUpHandler, false);
function keyDownHandler(e) {
if(e.keyCode == 39) {
rightPressed = true;
}
else if(e.keyCode == 37) {
leftPressed = true;
}
}
function keyUpHandler(e) {
if(e.keyCode == 39) {
rightPressed = false;
}
else if(e.keyCode == 37) {
leftPressed = false;
}
}
function collisionDetection() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
var b = bricks[c][r];
if(b.status == 1) {
if(x > b.x && x < b.x+brickWidth && y > b.y && y < b.y+brickHeight) {
dy = -dy;
b.status = 0;
}
}
}
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = '#0095DD';
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height-paddleHeight, paddleWidth, paddleHeight);
ctx.fillStyle = '#0095DD';
ctx.fill();
ctx.closePath();
}
function drawBricks() {
for(var c=0; c<brickColumnCount; c++) {
for(var r=0; r<brickRowCount; r++) {
if(bricks[c][r].status == 1) {
var brickX = (c*(brickWidth+brickPadding))+brickOffsetLeft;
var 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 = '#0095DD';
ctx.fill();
ctx.closePath();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
drawBricks();
collisionDetection();
if(x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if(y + dy < ballRadius) {
dy = -dy;
} else if(y + dy > canvas.height-ballRadius) {
if(x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
clearInterval(game);
alert('GAME OVER');
document.location.reload();
}
}
if(rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
}
else if(leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
}
var game = setInterval(draw, 10);
</script>
</body>
</html>