본문 바로가기
JAVASCRIPT

[JavaScript]2d 벽돌깨기 게임

by w1z 2024. 3. 24.

요새 자바스크립트 공부중

재밌다 ㅋ.ㅋ

전엔 자바스크립트 너무 싫었는데ㅠ 요샌 재밌넹

가볍게 만들 수 있고 프로젝트도 빨리 끝나다보니 완성된 결과물을 빨리 볼 수있어서 간결하다는 느낌을 받는다

요건 소스 먼저 만들고 하나씩 뜯어보는 중 

참고링크가 공부에 많은 도움이 된다 

참고링크 :

https://developer.mozilla.org/ko/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript/Create_the_Canvas_and_draw_on_it

 

캔버스 생성과 그리기 - 게임 개발 | MDN

이 강의는 게임 개발 캔버스 튜토리얼의 10단계 중 첫 번째 과정입니다. Gamedev-Canvas-workshop/lesson1.html에서 이 강의의 완성된 코드를 볼 수 있습니다.

developer.mozilla.org

 

 

설명이 아주 잘 되어있다 

 

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Gamedev Canvas Workshop</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      canvas {
        background: #eee;
        display: block;
        margin: 0 auto;
      }
      button {
        display: block;
    }
    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="480" height="320"></canvas>
    <button id="runButton">Start game</button>
    <script>
        const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const ballRadius = 10;

let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;

const paddleHeight = 10;
const paddleWidth = 75;

let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;

const brickRowCount = 5;
const brickColumnCount = 3;
const brickWidth = 75;
const brickHeight = 20;
const brickPadding = 10;
const brickOffsetTop = 30;
const brickOffsetLeft = 30;

let score = 0;
let lives = 3;

let 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 };
  }
}

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);

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) {
  let relativeX = e.clientX - canvas.offsetLeft;
  if (relativeX > 0 && relativeX < canvas.width) {
    paddleX = relativeX - paddleWidth / 2;
  }
}
function collisionDetection() {
  for (let c = 0; c < brickColumnCount; c++) {
    for (let r = 0; r < brickRowCount; r++) {
      let 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;
          score++;
          if (score == brickRowCount * brickColumnCount) {
            alert("YOU WIN, CONGRATS!");
            document.location.reload();
          }
        }
      }
    }
  }
}

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 (let c = 0; c < brickColumnCount; c++) {
    for (let r = 0; r < brickRowCount; r++) {
      if (bricks[c][r].status == 1) {
        const brickX = r * (brickWidth + brickPadding) + brickOffsetLeft;
        const brickY = c * (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 drawScore() {
  ctx.font = "16px Arial";
  ctx.fillStyle = "#0095DD";
  ctx.fillText("Score: " + score, 8, 20);
}
function drawLives() {
  ctx.font = "16px Arial";
  ctx.fillStyle = "#0095DD";
  ctx.fillText("Lives: " + lives, canvas.width - 65, 20);
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawBricks();
  drawBall();
  drawPaddle();
  drawScore();
  drawLives();
  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 {
      lives--;
      if (!lives) {
        alert("GAME OVER");
        document.location.reload();
      } else {
        x = canvas.width / 2;
        y = canvas.height - 30;
        dx = 3;
        dy = -3;
        paddleX = (canvas.width - paddleWidth) / 2;
      }
    }
  }

  if (rightPressed && paddleX < canvas.width - paddleWidth) {
    paddleX += 7;
  } else if (leftPressed && paddleX > 0) {
    paddleX -= 7;
  }

  x += dx;
  y += dy;
  requestAnimationFrame(draw);
}

document.getElementById("runButton").addEventListener("click", function () {
  draw();
  this.disabled = true;
});


    </script>
  </body>
</html>

 

 

개인 주석 단 내용 (나혼자 알아보기위함) 

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Gamedev Canvas Workshop</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      canvas {
        background: #eee;
        display: block;
        margin: 0 auto;
      }
      button {
        display: block;
    }
    </style>
  </head>
  <body>
    <canvas id="myCanvas" width="480" height="320"></canvas>
    <button id="runButton">Start game</button>
    <script>
        /* 그림을 그릴 캔버스 생성*/
    const canvas = document.getElementById("myCanvas");
        /* 캔버스 안에 실질적으로 조종할 객체 생성
        이걸  2D rendering context 라고 하나봄;; ㅋㅋ 잘은 모르겠다 일단 그렇다고함 ㅇㅇ*/
    const ctx = canvas.getContext("2d");
        /* 만들어질 게임 공의 반지름 설정 */
    const ballRadius = 10;
        /* 만들어질 게임 공의 x와 y좌표(공의 중심) */
    let x = canvas.width / 2;
    let y = canvas.height - 30;
        /* 공 움직임 표현을 위해서 프레임마다 더해줄 값 생성  */
    let dx = 2;
    let dy = -2;

    const paddleHeight = 10;
    const paddleWidth = 75;

    let paddleX = (canvas.width - paddleWidth) / 2;
    let rightPressed = false;
    let leftPressed = false;

    const brickRowCount = 5;
    const brickColumnCount = 3;
    const brickWidth = 75;
    const brickHeight = 20;
    const brickPadding = 10;
    const brickOffsetTop = 30;
    const brickOffsetLeft = 30;

    let score = 0;
    let lives = 3;

    let bricks = [];
        /* bricks[] 배열을 이차원 배열로 만들기 */
    for (let c = 0; c < brickColumnCount; c++) {
    bricks[c] = [];
    for (let r = 0; r < brickRowCount; r++) {
        /* 배열안 요소에 x,y status 값 선언*/
        bricks[c][r] = { x: 0, y: 0, status: 1 };
    }
    }
    /* 키보드 이벤트 
       keydown : 키보드 누를 때
       keyup : 키보드 뗄 때 
       mousemove : 마우스 움직일 때
    */ 
    document.addEventListener("keydown", keyDownHandler, false);
    document.addEventListener("keyup", keyUpHandler, false);
    document.addEventListener("mousemove", mouseMoveHandler, false);

    /* 키보드 누를 때 움직이기 true */
    function keyDownHandler(e) {
    if (e.key == "Right" || e.key == "ArrowRight") {
        rightPressed = true;
    } else if (e.key == "Left" || e.key == "ArrowLeft") {
        leftPressed = true;
    }
    }
    /* 키보드 뗄 때 움직이기 false */
    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) {
    /* e.clientX 는 브라우저에서 보여지는 영역을 기준의 좌표 
       마우스 움직임에 따라 패들 위치 움직이기 */
    let relativeX = e.clientX - canvas.offsetLeft;
    if (relativeX > 0 && relativeX < canvas.width) {
        paddleX = relativeX - paddleWidth / 2;
    }
    }
    /* 벽돌과 부딪혔을때 기능*/
    function collisionDetection() {
    for (let c = 0; c < brickColumnCount; c++) {
        for (let r = 0; r < brickRowCount; r++) {
        let b = bricks[c][r];
        if (b.status == 1) {
            /* 공위치가 벽돌좌표가 겹치면 */
            if (
            x > b.x &&
            x < b.x + brickWidth &&
            y > b.y &&
            y < b.y + brickHeight
            ) {
            /* 공의 방향틀어주기 , 벽돌 지우기, 총점수 +1 추가하기 */
            dy = -dy;
            b.status = 0;
            score++;
            /* 총점수: 만점 이면 경고창띄우고 리로드 */
            if (score == brickRowCount * brickColumnCount) {
                alert("YOU WIN, CONGRATS!");
                document.location.reload();
            }
            }
        }
        }
    }
    }
    /* 공 그리기 기능 */
    function drawBall() {
        /* 그림 그린다는 선언 */ 
        ctx.beginPath();
        /* (동그라미도형) 공의 x,y 좌표, 공의 지름, 공 그릴때 시작과 끝각도 ,(생략된 옵션 param: 그리는방향 false true 도 있음)*/
        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() {
        /* 열:5  행:3 벽돌그리기 반복문 */
        for (let c = 0; c < brickColumnCount; c++) {
            for (let r = 0; r < brickRowCount; r++) {
            if (bricks[c][r].status == 1) {
                /* 벽돌 한개씩 for문 돌리면서 좌표 설정*/
                const brickX = r * (brickWidth + brickPadding) + brickOffsetLeft;
                const brickY = c * (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 drawScore() {
    ctx.font = "16px Arial";
    ctx.fillStyle = "#0095DD";
    ctx.fillText("Score: " + score, 8, 20);
    }
    function drawLives() {
    ctx.font = "16px Arial";
    ctx.fillStyle = "#0095DD";
    ctx.fillText("Lives: " + lives, canvas.width - 65, 20);
    }
    /*공 움직이기 기능: 게임 start 기능과 같음 
      start할때부터 움직여야하므로, 
      그래서 이게 시작기능이라고 보고 여기에 모든 기능 떄려넣기 */
    function draw() {
        /*공 움직임 흔적 지우기*/
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBricks();
        drawBall();
        drawPaddle();
        drawScore();
        drawLives();
        collisionDetection();
        /* 튕겨내기 위한 코드
           캔버스에는 총 4개의 벽이 존재함 
           공을 그리는 매 프레임마다 벽에 닿았는지 확인해야함
           벽에 닿았다면 방향을 반대로 틀어줘야함
           틀어주면서 또 공을 그려야함 */

        /* x값을 +값 (오른쪽) 오른쪽벽에 닿았다?  그럼 x값을 -값(왼쪽)으로  */
        if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
            dx = -dx;
        }
        /* y값을 +값 (윗쪽) 상단벽에 닿았다? 그럼 y값을 -값(아래쪽)으로 */
        if (y + dy < ballRadius) {
            dy = -dy;
        } else if (y + dy > canvas.height - ballRadius) {
            if (x > paddleX && x < paddleX + paddleWidth) {
            dy = -dy;
            } else {
            lives--;
            if (!lives) {
                alert("GAME OVER");
                document.location.reload();
            } else {
                x = canvas.width / 2;
                y = canvas.height - 30;
                dx = 3;
                dy = -3;
                paddleX = (canvas.width - paddleWidth) / 2;
            }
            }
        }
        /* 오른쪽 키보드 누를때 패들 7픽셀씩 이동 */
        if (rightPressed && paddleX < canvas.width - paddleWidth) {
            paddleX += 7;
        /* 왼쪽 키보드 누를때 */
        } else if (leftPressed && paddleX > 0) {
            paddleX -= 7;
        }

        x += dx;
        y += dy;
        requestAnimationFrame(draw);
    }

    document.getElementById("runButton").addEventListener("click", function () {
    draw();
    this.disabled = true;
    });


    </script>
  </body>
</html>

'JAVASCRIPT' 카테고리의 다른 글

[JavaScript]카드 짝 맞추기 게임  (0) 2024.03.24
고인물 테스트 사이트  (0) 2024.03.17