JavaScript 를 활용한 공튀기기

TK·2021년 1월 21일
1
post-thumbnail

JavaScript 기초문법을 이용한 공튀기기 구현

예전에 작업하던 프로젝트에서 python 서버와 클라이언트 간 통신을 위해 JS 와 JQuery를 사용했던 적이 있다.

그때는 JS를 주로 GET, POST, Ajax, Websocket 을 활용하여 통신목적으로만 활용했었고, 기초적인 문법 지식도 부족했던 때라 무턱대고 따라하기 바빴었다.

백엔드 개발자이지만, 나중에 프론트와 협업할 때 JS의 기초를 알고 있으면 전반적인 프로젝트에 대한 높은 이해도를 가질 수 있을 것 같아서 클론코딩을 통해 간단한 공튀기기를 구현 해보기로 했다.


Ball class 작성하기

공튀기기를 구현하려면 뭐가 필요할까?
가장 먼저 공이 필요할 것이고 그 다음 그 공이 움직이는 공간이 필요할 것이다.

그래서 가장먼저 Ball class 를 작성해보았다.

export class Ball {
    constructor(stageWidth, stageHeight, radius, speed) { // 공의 위치가 스테이지에 랜덤으로 위치할 수 있게 클래스 초기함수(class constructor) 정의
        this.radius = radius; // 공의 반지름
        this.vx = speed; // x 축 이동속도
        this.vy = speed; // y 축 이동속도

        const diameter = this.radius * 2; // 공의 지름
        this.x = this.radius + (Math.random() * (stageWidth - diameter)); // x축 랜덤지정
        this.y = this.radius + (Math.random() * (stageHeight - diameter)); // y축 랜덤지정
    }
}
  • Ball class는 메인 파일인 index.html 파일에서 import 할 것이기 때문에
    클래스 앞에 먼저 export 선언을 해주었다.

  • constructor 메서드는 class 로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드이며, python 의 __init__ 생성자와 역할이 비슷한 초기함수라고 생각하면 될 것 같다.

  • this 는 일반적으로 메소드를 호출한 객체가 저장되어 있는 속성이다. 우리가 추가적으로 값을 수정하지 않는 이상 this.radius 의 값과 Ball.radius 의 값은 같을 것이다. python의 self 와 비슷하다.

공의 반지름 및 x축과 y축의 이동속도를 설정하고,
x, y 의 좌표를 랜덤하게 정해서 시작할 때 공의 위치를 랜덤하게 설정한다.

이 때 x, y 좌표는 공의 중심점의 좌표이기 때문에
화면의 길이와 높이에서 각각 원의 지름을 뺀 범위 내에서
좌표를 랜덤 설정해야 공이 화면 바깥으로 벗어나지 않는다.


draw(ctx, stageWidth, stageHeight) { // 캔버스 context 에 그림을 그릴 수 있는 함수
        this.x += this.vx; // (공의 이전 위치 + 공의 속력) 만큼 좌표가 이동
        this.y += this.vy;

        this.bounceWindow(stageWidth, stageHeight);
        ctx.fillStyle = '#fdd700' // 공의 색깔
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); // 원을 그리는 함수
        ctx.fill();
    }

Ball.draw() 함수는 공의 위치를 그리는 함수이며 canvas context 에 그림을 그린다.

x, y 좌표에 각각 공의 속력을 더해줘서 공이 이동하는 것 처럼 보이게끔 했다.
공의 색깔은 ctxfillStyle 메소드를 통해 노란색으로 정해보았다.

그리고 x, y 좌표를 기반으로 원을 만들어야 하기 때문에 ctx.arc로 원을 만들고
그 안을 fill()로 채워주었다.


bounceWindow(stageWidth, stageHeight) { // 공이 화면 모서리에 닿으면 반대로 움직이게 하는 함수
        const minX = this.radius; // x, y 좌표의 최소값 최대값은 반지름 길이를 고려해야 함
        const maxX = stageWidth - this.radius;
        const minY = this.radius;
        const maxY = stageHeight - this.radius;

        // 방향전환 조건문
        if (this.x <= minX || this.x >= maxX) { // x 가 최소, 최대값 범위 밖이면 vx 에 -1 을 곱함 '||' 은 or 연산자
            this.vx *= -1;
            this.x += this.vx;
        } else if (this.y <=minY || this.y >= maxY) { // y 가 최소, 최대값 범위 밖이면 vy에 -1을 곱함
            this.vy *= -1;
            this.y += this.vy;
        }
    }

Ball.bounceWindow() 는 공이 화면 모서리에 닿았을 때 반대로 움직이게 하도록 하는 함수이다.

반지름의 길이를 고려해 x, y 좌표의 최소값 최대값을 설정했으며,
원의 중심인 x, y 좌표가 각각

minX < x < MaxX, minY < y < MaxY

위의 조건을 만족하지 못하면 공의 방향을 바꾸도록 설정했다.

x 좌표가 x 축 범위 바깥이면 x 축 이동속도가 반대로 되어야 하기 때문에, this.vx *= -1 을 해주었고,
y 좌표도 똑같이 범위가 y 축 바깥이면 this.vy *= -1 을 해주었다.

위의 조건문은 if, else if 구문으로 작성했다.


App class 작성하기

App 클래스에서는 앞서 작성한 Ball 클래스를 import 하여 메인 기능들을 작성했다.

import {
    Ball
} from "./ball.js";


class App {
    constructor() {
        // 자바스크립트 constructor 는 App instance 가 생성될 때 변수들의 데이터값을 초기화, 파이썬의 __init__ 메소드와 비슷한 역할
        // this 역시 파이썬의 self 와 비슷한 역할
        this.canvas = document.createElement('canvas'); // canvas object 생성
        this.ctx = this.canvas.getContext('2d');

        document.body.appendChild(this.canvas); // canvas 를 html body 에 삽입

        window.addEventListener('resize', this.resize.bind(this), false);
        this.resize();

        this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 15); // 공의 사이즈와 움직이는 스피드 세팅

        window.requestAnimationFrame(this.animate.bind(this)); // 공을 화면에 끊임없이 계속 그려주는 역할
    }

    resize() {
        // 스크린 사이즈는 가변적이기 때문에 사이즈를 조정하는 함수를 생성
        this.stageWidth = document.body.clientWidth;
        this.stageHeight = document.body.clientHeight;

        this.canvas.width = this.stageWidth * 2;
        this.canvas.height = this.stageHeight * 2;
        this.ctx.scale(2, 2);
    }

    animate(t) { // 애니메이션을 실제로 구동시키는 함수
        window.requestAnimationFrame(this.animate.bind(this));

        this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight); // 공이 이동한 이전 프레임을 지움

        this.ball.draw(this.ctx, this.stageWidth, this.stageHeight) // 공의 위치를 그려줌
    }
}

window.onload = () => {
    new App();
}

공이 이동하는 프레임을 그리기 위해 꼭 알아야 할 것이 바로 htmlcanvas 태그이다.

다음은 canvas 에 대한 설명이다.

<canvas> 엘리먼트는 고정 크기의 드로잉 영역을 생성하고 하나 이상의 렌더링 컨텍스(rendering contexts)를 노출하여, 출력할 컨텐츠를 생성하고 다루게 됩니다.
캔버스는 처음에 비어있습니다. 무언가를 표시하기 위해서, 어떤 스크립트가 랜더링 컨텍스트에 접근하여 그리도록 할 필요가 있습니다. <canvas> 요소는 getContext() 메서드를 이용해서, 랜더링 컨텍스트와 (렌더링 컨텍스트의) 그리기 함수들을 사용할 수 있습니다. getContext() 메서드는 렌더링 컨텍스트 타입을 지정하는 하나의 파라메터를 가집니다. CanvasRenderingContext2D을 얻기위해 "2d"로 지정합니다.

var canvas = document.getElementById('tutorial');
var ctx = canvas.getContext('2d');

첫 번째 줄의 스크립트는 document.getElementById() 메서드를 호출하여 <canvas> 요소를 표시할 DOM을 검색합니다. 요소가 있으면 getContext() 메서드를 사용하여 드로잉 컨텍스트에 액세스 할 수 있습니다.

출처: https://developer.mozilla.org/ko/docs/Web/HTML/Canvas/Tutorial/Basic_usage

초기 생성자를 통해 canvasctx 를 세팅하고 html body 부분에 canvas를 append 해준다.

그리고 공의 사이즈와 공의 이동속도를 세팅하고 window.requestAnimationFrame(this.animate.bind(this)); 함수를 호출하여 App 클래스가 호출되면 공을 화면에 끊임없이 그려주도록 해준다.

  • resize() 함수는 사용자의 화면이 가변적이기 때문에 처음에 화면을 조정하기 위한 것이다.

  • animate() 함수 내부에서는 this.ball.draw() 함수로 공의 위치를 그리고, this.ctx.clearRect() 를 통해 공이 이동한 이전 프레임을 지운다.

마지막으로

window.onload = () => {
  new App();
}

화면이 로드 되면 앱을 실행시킨다.

다음은 코드 실행 결과이다.
https://taekhyang.github.io/wecode-preliminary-study/javascript_ball_collision/

출처 : Youtube Interactive Developer

https://www.youtube.com/watch?v=sLCiI6d5vTM&t=321s

ps. ctrl + 마우스휠 을 이용해 화면 사이즈를 변경해보자.
profile
Backend Developer

0개의 댓글