예전에 작업하던 프로젝트에서 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 좌표에 각각 공의 속력을 더해줘서 공이 이동하는 것 처럼 보이게끔 했다.
공의 색깔은 ctx
의 fillStyle
메소드를 통해 노란색으로 정해보았다.
그리고 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();
}
공이 이동하는 프레임을 그리기 위해 꼭 알아야 할 것이 바로 html
의 canvas
태그이다.
다음은 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
초기 생성자를 통해 canvas
와 ctx
를 세팅하고 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