본문 바로가기
개발기록/Javascript

[JS] 자바스크립트 스와이프 벽돌깨기 게임 만들기 - 1

by spectrum20 2022. 6. 12.

심심풀이로 하기 좋은 벽돌깨기게임을 만들어보았다

생각나는데로 코드를 짜느라 수정이 많이 필요한 스파게티 코드지만

그건 나중으로 미루고, 여태까지 만든것을 일단 올린다!

(오류가 많아서, 앞으로도 계속해서 수정될 예정이다......!)

 

 

1. 캔버스 만들기

<body>
    <canvas id="brick_break" width="500" height="500" style="border: 1px solid black;"></canvas>
</body>

<script>
        var canvas = document.getElementById('brick_break');
        var ctx = canvas.getContext('2d');
        var rect = canvas.getBoundingClientRect();
</script>
body 태그 안에 canvas 태그를 삽입해주었다
다음으로, script 태그 안에서 getElementById 함수로 canvas태그에 접근해주고
canvas 태그를 조작하기 위한 getContext 함수를 불러와준다

 

터치조작을 위해, canvas의 위치값을 알 수 있는 getBoundingClientRect함수도 불러와주었다

 

Html 코드는 끝이났다!

이제부터는 script 태그 안의 코드만 만들어주면 된다

 
 

2.  공 위치 컨트롤, 공 그리기

        var ballRadius = 10;
        var ball = {
            drawable: false
        }
        var x = canvas.width / 2;
        var y = canvas.height - 30;
        
        
// 공 그리기
	function drawBall() {
            ctx.beginPath();
            ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
            ctx.fillStyle = "green";
            ctx.fill();
            ctx.closePath();
        }

이제 공을 컨트롤하기 위한 변수를 만들어준다

나는 공의 반지름을 10으로 정했다

공의 움직을 제어하기 위한 ball.drawable 속성을 만들어주고,

공의 x위치와 y위치를 제어하기 위한 변수도 만들어주고, 공의 첫 시작위치를 대략적으로 설정해주었다

 

다음으로, 공을 그리기 위한 drawBall() 함수를 만들어준다

drawBall() 함수를 실행시키면, 아래와 같이 지정된 위치에 녹색의 공이 그려진다

 

 

3. 공 튀기기

 //// 공튀기기
        var dx = 1;
        var dy = 1;

		//// 공 이동
        function moveBall() {
            x += dx;
            y += dy;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            drawBall();
            if (y + dy < ballRadius || y + dy > canvas.height - ballRadius) {
                dy = -dy;
            }
            if (x + dx < ballRadius || x + dx > canvas.width - ballRadius) {
                dx = -dx;
            }
            ///// 공 멈추기
            if (y > canvas.height - ballRadius * 2) {
                ball.drawable = false;
            }
        }

공의 현재 위치 변수인 x, y를 변화시켜주기 위한 dx, dy 변수를 만들어준다

값은 대충 1로 설정해주었다(이 값이 커질수록 공이 이동하는 속도가 빨라진다)

 

다음으로, 공을 이동시키기위한 moveBall()함수를 만들어주었다

moveBall()함수를 실행시키면, 공의 현재위치 변수 x,y에 dx,dy값을 더해서 새로운 좌표를 넣어준다

다음으로 clearRect를 실행시켜서 공이 이동할때마다 이전 위치의 공을 지워준다

그 다음, drawBall()함수를 실행시키면, 이전 좌표의 공은 지워지고 새로운 좌표에 공이 그려진다

 

if 조건문은 공이 canvas밖으로 빠져나가는 것을 막아준다

공의 좌표가 canvas의 좌표보다 커지면 dx 또는 dy의 값을 (-)로 바꿔서 공의 이동방향을 바꿔준다

 

공이 이동하다가 바닥으로 떨어졌을 때, 움직임을 멈추기 위해서

ball.drawable = false를 실행시켜준다

 

 

4. 함수 실행

공을 이동시키는 함수 drawBall()과 moveBall()을 만들었으니, 실행시켜보자

        let timer = setInterval(() => {
            if (ball.drawable) {
                moveBall();
            }
            else {
                drawBall();
            }
        }, 10);

timer라는 변수를 만들고, setInterval() 함수를 이용해서 0.01초마다 한번씩 moveBall() 또는 drawBall()함수를 실행시켜준다

(*** 공을 움직이려면 위에서 만든 변수 ball의 속성인 drawable이 true이어야 한다)

 

 

5. 마우스 클릭 컨트롤

마우스와 터치클릭을 이용해서 공의 경로를 설정해주기 위한 코드이다

클릭한 위치의 좌표정보를 가진 pos 변수를 만든다

다음으로, addEventListener 함수를 활용해서, mouse 컨트롤과 touch 컨트롤을 할 수 있는 기능들을 만들어 준다

 var pos = {
            point: false,
            x: -1,
            y: -1
        }
        canvas.addEventListener("mousedown", listener);
        canvas.addEventListener("mousemove", listener);
        canvas.addEventListener("mouseup", listener);
        canvas.addEventListener("mouseout", listener);

        canvas.addEventListener("touchstart", listener);
        canvas.addEventListener("touchmove", listener);
        canvas.addEventListener("touchend", listener);


    function listener(e) {
        switch (e.type) {
            case "mousedown":
                if (!ball.drawable)
                    pos.point = true;
            case "mousemove":
                if (pos.point) {
                    pos.x = e.offsetX;
                    pos.y = e.offsetY;
                    drawLine(e);
                }
                break;
            case "mouseout":
                break;
            case "mouseup":
                if (pos.point)
                    pointEnd();
                break;
            case "touchstart":
                if (!ball.drawable)
                    pos.point = true;
            case "touchmove":
                if (pos.point) {
                    pos.x = e.touches[0].pageX - rect.left;
                    pos.y = e.touches[0].pageY - rect.top;
                    drawLine(e);
                }
                break;
            case "touchend":
                if (pos.point)
                    pointEnd();
                break;
            default:
        }
    }

 

 

6. 선 그리기

mousedown+mousemove 또는 touchstart+touchmove를 했을 때의 기능을 만들어준다

마우스 또는 터치로 특정 위치를 클릭했을 때, 공의 현재 위치와 클릭된 위치를 잇는 직선을 그어주는 기능이다

(두 점을 잇는 직선을 어떻게 그려야 할지 몰라서, 중학생때 배운 직선의 방정식을 구하는 식을 이용했다

이렇게 그리는 게 맞는지는 모르겠지만, 어쨌든 코드는 돌아가니까.......^^?)

    function drawLine(e) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBall();
        a = (y - pos.y) / (pos.x - x);   // 기울기
        b = y - (a * x);  // 절편
        ctx.beginPath();
        ctx.moveTo(x, y);  // 현재 공 위치
        if (pos.x > x) {
            ctx.lineTo(canvas.width, y - a * (canvas.width - x));   //마우스 클릭 위치
        } else {
            ctx.lineTo(0, y - a * (-x));   //마우스 클릭 위치
        }
        ctx.closePath();
        ctx.setLineDash([5])
        ctx.stroke();
    }

clearRect로 캔버스를 지워주고, 공을 그려준다

다음으로 두 점을 잇는 직선의 기울기(a)와 y절편(b)를 구해준다

 

ctx.beginPath() 함수로, 라인을 그릴 것임을 알려주고

ctx.moveTo() 함수로 라인의 첫 시작점을 알려준다(공의 위치이다)

ctx.lineTo() 함수에는 클릭한 위치를 지나는 직선을 그어줄 점의 위치를 알려준다

 

다음으로, 직선을 점선모양으로 만들기 위해 ctx.setLineDash()함수를 사용해주었다

(점선을 촘촘하게 그리고 싶으면 array의 숫자를 작게 하면 된다)

마지막으로 ctx.stroke()를 이용해서 점섬을 그려준다

 

 

7. mouseup 시, 날아가는 공 만들기

이제 마우스를 놓았을 때, 공이 날아가는 pointEnd() 함수를 만들어준다

        function pointEnd() {
            pos.point = false;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            a_square = a*a;
            x_prime = Math.sqrt(16/(a_square+1));
            if (pos.x > x) {
                dx = x_prime;
                dy = -x_prime*a;
            } else {
                dx = -x_prime;
                dy = x_prime*a;
            }
            ball.drawable = true;
        }

먼저, clearRect함수로 캔버스를 깨끗하게 지워준다

다음으로, 클릭한 방향으로 공의 좌표를 변경시켜주기 위해 dx, dy변수를 설정해준다

 

대충, 한번 공이 이동할 때의 거리가 4가 되게 하는 dx, dy를 만들고 싶었다

(역시나 더 좋은 방법이 있을것 같지만,,,, 암튼 코드는 생각한데로 돌아간다^^)

++ 공의 속도를 높이고 싶으면 x_prime 변수의 숫자 16을 바꾸면 된다

 

dx, dy를 설정해 주었으니, ball.drawable = true로 바꿔서 공이 날아가게 해준다

 

 

 

8. 완성코드

아래는 완성 코드이다

 

<body>
    <canvas id="brick_break" width="500" height="500" style="border: 1px solid black;"></canvas>
</body>

<script>
    var canvas = document.getElementById('brick_break');
    var ctx = canvas.getContext('2d');
    var rect = canvas.getBoundingClientRect();


    var ballRadius = 10;
    var ball = {
        drawable: false
    }
    var x = canvas.width / 2;
    var y = canvas.height - 30;


    // 공 그리기
    function drawBall() {
        ctx.beginPath();
        ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
        ctx.fillStyle = "green";
        ctx.fill();
        ctx.closePath();
    }


    var pos = {
        point: false,
        x: -1,
        y: -1
    }

    //// 공튀기기
    var dx = 1;
    var dy = 1;

    //// 공 이동
    function moveBall() {
        x += dx;
        y += dy;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBall();
        if (y + dy < ballRadius || y + dy > canvas.height - ballRadius) {
            dy = -dy;
        }
        if (x + dx < ballRadius || x + dx > canvas.width - ballRadius) {
            dx = -dx;
        }
        ///// 공 멈추기
        if (y > canvas.height - ballRadius * 2) {
            ball.drawable = false;
        }
    }

    let timer = setInterval(() => {
        if (ball.drawable) {
            moveBall();
        }
        else {
            drawBall();
        }
    }, 10);



    canvas.addEventListener("mousedown", listener);
    canvas.addEventListener("mousemove", listener);
    canvas.addEventListener("mouseup", listener);
    canvas.addEventListener("mouseout", listener);

    canvas.addEventListener("touchstart", listener);
    canvas.addEventListener("touchmove", listener);
    canvas.addEventListener("touchend", listener);


   function listener(e) {
        switch (e.type) {
            case "mousedown":
                if (!ball.drawable)
                    pos.point = true;
            case "mousemove":
                if (pos.point) {
                    pos.x = e.offsetX;
                    pos.y = e.offsetY;
                    drawLine(e);
                }
                break;
            case "mouseout":
                break;
            case "mouseup":
                if (pos.point)
                    pointEnd();
                break;
            case "touchstart":
                if (!ball.drawable)
                    pos.point = true;
            case "touchmove":
                if (pos.point) {
                    pos.x = e.touches[0].pageX - rect.left;
                    pos.y = e.touches[0].pageY - rect.top;
                    drawLine(e);
                }
                break;
            case "touchend":
                if (pos.point)
                    pointEnd();
                break;
            default:
        }
    }


    function drawLine(e) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        drawBall();
        a = (y - pos.y) / (pos.x - x);   // 기울기
        b = y - (a * x);  // 절편
        ctx.beginPath();
        ctx.moveTo(x, y);  // 현재 공 위치
        if (pos.x > x) {
            ctx.lineTo(canvas.width, y - a * (canvas.width - x));   //마우스 클릭 위치
        } else {
            ctx.lineTo(0, y - a * (-x));   //마우스 클릭 위치
        }
        ctx.closePath();
        ctx.setLineDash([5])
        ctx.stroke();
    }

    function pointEnd() {
        pos.point = false;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        a_square = a * a;
        x_prime = Math.sqrt(16 / (a_square + 1));
        if (pos.x > x) {
            dx = x_prime;
            dy = -x_prime * a;
        } else {
            dx = -x_prime;
            dy = x_prime * a;
        }
        ball.drawable = true;
    }

</script>
반응형

댓글