[clone coding] 화면에 튕기는 공 만들기
in Algorithm
공을 그릴때는 항상 중심점을 기준으로 공의 x, y 값을 잡는다.
그래서 공을 그리게 되면 그 중심점이 공의 실제 위치가 된다.
반지름이 굉장히 중요하다. x,y 값에다가 반지름을 더해주고 빼주어야 실제로 공이 튕길때 브라우저에 닿았는지 알 수 있기 때문이다
그래서 공에 x,y값을 비교하는 것이 아니라 반지름을 더한 값 혹은 뺀 값을 이렇게 정의해서 공이 실제로 어디에 닿았는지를 판단한다.
브라우저상에서 공에 움직임을 주게 되면 x,y에다가 vx, vy라는 임의의 값을 더해주게 되는데
그러면 x값과 y값이 계속 증가하거나 감소할테니 움직임을 가지고 그릴 수 있게 되는 것이다.
그렇게 공이 vx와 vy를 따라서 움직이게 된다.
x값을 반지름 값과 비교해서 좌상귀가 브라우저의 0,0인데 x가 0에 닿았는지 확인한다.
만약에 닿았다고 치면 vx, 즉 x가 닿았으니까 이 vx에 -1을 곱해준다.
그러면 y값은 증가하고 x값은 감소하는 그런 움직임을 가지게 된다.
그때 x값에 -1을 곱해주면 x값이 다시 증가하게 되니 x가 왼쪽으로 움직이던 것이 오른쪽으로 움직이게 되고
y는 위로 가고, 이런식으로 공이 그려지게 된다.
그래서 튕겨지는 것처럼 보이는거다.
y값이 0에 닿았다면 증가하던 y값을 반대로 감소하게 만들어준다 =>공이 아래로 움직임
//index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script type="module" src="app.js"></script>
</body>
</html>
//style.css
*{
outline:0;
margin:0;
padding:0;
}
html{
width:100%;
height:100%;
}
body{
width:100%;
height:100%;
background-color: #c5beaf;
}
canvas{
width:100%;
height:100%;
}
//app.js
import {Ball} from './ball.js';
import {Block} from './block.js';
class App{
constructor(){
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
document.body.appendChild(this.canvas);
//항상 리사이즈 이벤트를 걸어두고 시작을 한다고 하심. 현재 내가 만들고자 하는 애니메이션의 크기를 아는 것이 굉장히 중요한데, 리사이트 이벤트를 걸어놓고 스크린 사이즈를 가지고 와서 애니메이션을 정의해 준다는 것이다.
//브라우저가 가변적이기 떄문에 스크린사이즈 가져오는 함수를 먼저 정의해주고 작업하는게 나중을 위해서라도 더 낫다
window.addEventListener('resize', this.resize.bind(this), false);
this.resize();
//Ball을 import한 후 작성한다. import한 ball을 가지고 draw함수를 이용해서 그린다.
this.ball = new Ball(this.stageWidth, this.stageHeight, 60, 15); //반지름을 60으로, 속도를 15로
//Block을 import한 후 작성한다. import한 block을 가지고 draw함수를 이용해서 그린다.
this.block = new Block(700, 30, 50, 450); //넓이 700, 높이 30, 그리고 300/450의 위치로 만들어준다
//requestAnimationFrame을 걸고, 거기에 애니메이션을 실제로 구동시키는 함수를 만들어준다
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));
//이렇게까지만하면 애니메이션이라, 계속 뭔가를 생성하는데, 생성하기 이전의 프레임을 지워줄 필요가 있다.
//clearRect를 하고나면 공이 튕기는 걳처럼 보이는데, 이전그림과 다음그림을 번갈아 가면서 보여주는 것일뿐인데 튕기는 것처럼 보인다
//이전에 생성되었던것을 지워줌으로써 애니메이션이 움직이는 것처럼 보여지는 것이다
this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight);
//Block을 import한 후 작성한다.
//ball의 위치를 파악해서 반사값을 정의해준다
this.block.draw(this.ctx);
//Ball을 import한 후 작성한다.
this.ball.draw(this.ctx, this.stageWidth, this.stageHeight, this.block);
}
}
window.onload = () =>{
new App();
}
ball.js
export class Ball{
//stage사이즈를 가져오고 반지름, 속도를 가져온다
constructor(stageWidth, stageHeight, radius, speed){
this.radius = radius;
//vx와 vy는 x,y좌표값을 움직이는 속도라고 정해준다
this.vx = speed;
this.vy = speed;
//스테이지에 랜덤으로 위치할 수 있게 함수를 정의 해주었다
const diameter = this.radius * 2;
this.x = diameter + (Math.random() * (stageWidth - diameter) );
this.y = diameter + (Math.random() * (stageHeight - diameter) );
}
//그리고 draw함수를 하나 만들고 context를 가져온다. 그러면 캔버스 context에 그림을 그릴 수 있는 함수가 완성된다
//+)블럭을 만든 후에 공이 블럭에 튕기도록 만들어야하기 때문에 block을 매개변수로 받아준다
draw(ctx, stageWidth, stageHeight, block){
//x,y값에 vx,vy값을 더해줘서 공이 움직이도록 만들어졌다
this.x += this.vx;
this.y += this.vy;
this.bounceWindow(stageWidth, stageHeight);
//ball의 위치를 파악해서 반사값을 정의해준다
// console.log(block)
this.bounceBlock(block);
//공에 색을 정하고 그림 그리기 시작
ctx.fillStyle = '#6495ED';
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
ctx.fill();
}
//스테이지상에 닿았는지를 판단하는 함수.
bounceWindow(stageWidth, stageHeight){
const minX = this.radius;
const maxX = stageWidth - this.radius;
const minY = this.radius;
const maxY = stageHeight - this.radius;
//닿았다면 반대로 튕기는 기능. 공이 어디에 있는지 판단을 하고vx, vy에 -1을 곱해줘서 반대로 움직이도록 한다
if(this.x <= minX || this.x >= maxX){
this.vx *= -1;
this.x += this.vx;
}else if(this.y <= minY || this.y >= maxY){
this.vy *= -1;
this.y += this.vy;
}
}
//블럭에 튕기게 하기 위해 만들어준 함수. 윈도우가 아니라 가운데에 있는 block에 닿았는지를 판단해야한다
bounceBlock(block){
const minX = block.x = this.radius;
const maxX = block.maxX + this.radius;
const minY = block.y - this.radius;
const maxY = block.maxY - this.radius;
//vx와 vy에 -1을 곱해주는 방식으로 공이 튕기는 걸 만든다
//양옆에 충돌하는지 위아래 충돌하는지 판단하기 위해서는 ball의 좌표와 block의 좌표를 비교해서 어느값이 가장 근접한지를 찾으면 위치를 알 수 있다
//값이 정의되면 이제 vx나 vy에 -1을 곱해준다
if(this.x > minX && this.x < maxX && this.y > minY && this.y < maxY){
const x1 = Math.abs(minX - this.x);
const x2 = Math.abs(this.x - maxX);
const y1 = Math.abs(minY - this.y);
const y2 = Math.abs(this.y - maxY);
const min1 = Math.min(x1, x2);
const min2 = Math.min(y1, y2);
const min = Math.min(min1, min2);
if(min == min1){
this.vx *= -1;
this.x += this.vx;
}else if(min == min2){
this.vy *= -1;
this.y += this.vy;
}
}
}
}
block.js
export class Block{
//이제는 벽을 하나 만들어서 튕기는것처럼 보이는걸 만들어줄거다
constructor(width, height, x, y){
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.maxX = width + x;
this.maxY = height + y;
}
//공을 추적하기 위해서 maximum값도 정의해준다. draw함수에 실제로 그려지는 걸 만드어준다
draw(ctx){
const xGap = 80;
const yGap = 60;
ctx.fillStyle = '#7393B3';
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fill();
//재미를 위해 블럭 밑에 그림자 넣기
ctx.fillStyle = '#190f3a';
ctx.beginPath();
ctx.moveTo(this.maxX, this.maxY);
ctx.lineTo(this.maxX - xGap, this.maxY + yGap);
ctx.lineTo(this.x - xGap, this.maxY + yGap);
ctx.lineTo(this.x, this.maxY);
ctx.fill();
//재미를 위해 블럭 옆에도 그림자넣기
ctx.fillStyle = '#9d0919';
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x, this.maxY);
ctx.lineTo(this.x - xGap, this.maxY + yGap);
ctx.lineTo(this.x - xGap, this.maxY + yGap - this.height);
ctx.fill();
}
}
좌표값만 잘 알고 있으면 그리기 쉬운게 캔버스라고 한다..ㄷㄷㄷ..
그니까 그 좌표를 어떻게 해야 잘 알수있는데요….ㅠ_ㅠ
역시 천재들은 다르구먼…흐규흐규
근데 슨생님 말씀대로했는데 뭔가 살짝 찐따남
진짜 고대로했는데… block튕기는곳이 살짝 찐따나긴하지만… 된다는것에 의의를 두것다!
interative developer 김종민 슨생님 유튜브보고 고대로 따라함 😎
https://www.youtube.com/watch?v=sLCiI6d5vTM&list=PLGf_tBShGSDNGHhFBT4pKFRMpiBrZJXCm&index=8