공 움직이는거 이해하는데 정말 오래 걸렸다...ㅠ
paddel때도 그랬지만, 중요한건 공 자체가 스스로 움직인다고 생각 하는 것이 아니고, ball의 x,y,velocityX,velocityY 가 어떤 상황에 어떻게 바뀌어야 하는지를 머리속에 잘 집어 넣는 것이 중요했다. 그리고 그냥 글로만 이해 하는것보단 그림 그려가면서 이해하는것이 더 이해가 잘 됐다.
const ball = {
x: canvas.width / 2, //300
y: canvas.height / 2, //200
radius: 10,
velocityX: 5, //속도 velocity = speed + direction
velocityY: 5,
speed: 7,
color: "WHITE",
};
ball.x += ball.velocityX; // X+ 이것만 실행 하면 오른쪽 수직방향으로 공이 쭉 움직인다.
ball.y += ball.velocityY; // Y+
공이 오른쪽 수평으로 움직이기 하려면 X 의 위치가 계속 증감 하도록 해야 한다. 공이 아래쪽 수직 방향으로 움직이게 하려면 Y의 값을 계속 해서 증감 시켜 주면 되는 것이다.
velocity는 speed + direction 이다. 따라서 위 처럼 코드를 주면 공이 처음으로 움직이는 방향은 보라색으로 표시되는 부분이다. (X+, Y+ 이기 때문에)
위 사진의 화살표와 함께 있는 X+-,Y+- 를 머리 속에 잘 넣어 두어야 한다...
if (ball.y + ball.radius >= canvas.height || ball.y - ball.radius <= 0) {
//ball.y + ball.radius 값은 bottom of ball 임.
ball.velocityY = -ball.velocityY;
} //공의 top 과 bottom 이 캔버스의 bottom과 top 에 부딪힐 경우 velocityY 의 값이 reverse 되어야 함.
위의 사진의 경우에서 공은 다시 보라색 화살표 방향으로 위치가 바뀌어야 한다.
1) Y의 값이 reverse 되어야 함.
공이 처음 움직이도록 했던 방향은 X+,Y+ 이고 그 다음으로 가야 할 방향은 X+,Y-이다.
공이 계속 해서 top, bottom 을 치는 모습을 생각 해보면 X의 +,-는 바뀌지 않고 Y만 reverse 된다는 것을 알 수 있다.
2) top and bottom of ball 알기
공의 bottom,top 을 먼저 구해야 하는데, 이건 말로 설명하는것보다 그림으로 그리고 이해하는것이 더 쉬웠다.
위 사진에서 보듯 ball.y + ball.radius
는 공의 bottom 이 되고, ball.y - ball.radius
는 공의 top 부분이 된다.
3) 공과 캔버스의 높이를 이해하기
공은 canvas의 bottom(즉, canvas.height)보다 클 경우 Y가 reverse 되고, 공의 top 이 0보다 작을 경우 ball.velocityY가 -ball.velocityY로 reverse 된다.
function collision(b, p) {
p.top = p.y;
p.bottom = p.y + p.height;
p.left = p.x;
p.right = p.x + p.width;
b.top = b.y - b.radius;
b.bottom = b.y + b.radius;
b.left = b.x - b.radius;
b.right = b.x + b.radius;
return (
b.right > p.left && b.bottom > p.top && b.left < p.right && b.top < p.bottom
);
}
// paddle이 user 패들인지 com 인지 확인
let player = ball.x + ball.radius < canvas.width / 2 ? user : com;
if (collision(ball, player)) {
// ball.y랑 paddel의 y 값이 똑같아진다. (즉, 볼이 패들을 치는 위치)
let collidePoint = ball.y - (player.y + player.height / 2);
//값을 -1,0,1로 만들어줌
collidePoint = collidePoint / (player.y + player.height / 2);
//튕겨나가는 위치
let angle = (Math.PI / 4) * collidePoint;
let direction = ball.x + ball.radius < canvas.width / 2 ? 1 : -1;
ball.velocityX = Math.cos(angle) * ball.speed * direction;
ball.velocityY = Math.sin(angle) * ball.speed;
ball.speed += 0.2;
}
1) 공과 패들이 닿았는지 확인 하기
공이 패들에 부딪힐때만의 상황을 true 로 받아서 true 일 경우에만 공의 위치가 바뀌도록(튕겨나가도록) 만들어 준다.
우선, 공과 패들이 서로 맞닿았는지를 확인하기 위해서 패들과 공의 좌,우,위,아래를 비교하고, 공과 패들의 위치를 서로 비교한다.
공과 패들의 위치를 비교할땐, 캔버스 안에 패들이 오른쪽과 왼쪽에 있는 경우를 각각 상상해서 공의 위치가 더 커야 하는지 작아야하는지 비교하는 것이 간단하다.
2) 공이 패들에 닿고 튕겨 나감.
2.1) 공이 user와 com의 paddle 중 어떤 paddle을 쳤는지 확인 하기.
let player = ball.x + ball.radius < canvas.width / 2 ? user : com;
collision
함수의 parameter 인 player가 user와 com 중 어떤 것인지 확인한다.
ball.x + ball.radius < canvas.width / 2
는 공이 왼쪽 user 쪽에 있는 경우를 가리킨다.
2.2) 공이 패들의 어느 부분을 쳤는지 확인하기
let collidePoint = ball.y - (player.y + player.height / 2);
공이 패들의 어느부분을 쳤는지 알려준다.
패들의 중앙 부분을 기점으로 공이 패들의 어느지점을 쳤는지에 따라서 값이 변한다.
player.height / 2
값은 항상 같고 player.y
와 ball.y
의 값이 계속 해서 변하면서 공이 패들의 어느 부분과 닿았는를 알 수 있고, 이를 공이 튕겨 나가는 기준점으로 삼을 수 있다.
2.3) 공이 튕겨 나가는 위치 (velocixy 구하기)
let angle = (Math.PI / 4) * collidePoint;
let direction = ball.x + ball.radius < canvas.width / 2 ? 1 : -1;
ball.velocityX = Math.cos(angle) * ball.speed * direction;
ball.velocityY = Math.sin(angle) * ball.speed;
공이 패들에 맞고 향해야 할 방향(velocityX,Y)를 구해줘야한다.
Math. cos,sin을 통해 구할 수 있다.
let direction = ball.x + ball.radius < canvas.width / 2 ? 1 : -1;
direction
은 user paddel 칠땐, 공이 x+ 방향(오른쪽)으로 가야되고, com paddle 치면 왼쪽(X-)로 가야 한다는 것을 정의한것이다. user 가 공을 칠땐 velocityX는 항상 +이기 때문에 넣어 준것이다.
공이 중앙에서 시작해서 갈 방향이 새로고침 할 때마다 랜덤으로 바뀌게 하려면 어떻게 해야 할까요?