탁구에서 공이 패들에서 튕겨 나올 때 공의 방향을 어떻게 계산합니까?


28

게임 개발에서이 Hello World-y 문제를 해결하려고 노력하고 있습니다. XNA에서 TicTacToe 게임을 만들었으므로 다음 단계는 브레이크 아웃 클론 일 것입니다.

게임 프로그래밍에 대해 전혀 모르고 심지어 어디에 수학을 적용해야하는지 명심하십시오 . 그래서이 질문을합니다.


질문 : 화면 하단의 패들에 공이 닿을 때 공이 어디에서 튀어 나올지 어떻게 알 수 있습니까?

나는 그것이 다음과 같을 것이라고 상상할 것이다.

  1. 들어오는 공에서 속도와 각도를 캡처합니다.
  2. 그것이 바에 닿은 곳 (왼쪽, 가장 오른쪽, 중앙)을 감지하고 그에 따라 외부 영역에 닿으면 더 빠른 속도를 제공합니다.
  3. 내가 붙어있는 곳입니다. 헤헤

통찰력이 있습니까? 나는 이것이 간단한 답변 유형의 질문이 아니라는 것을 알고 있지만 어느 시점에서 모든 사람이 직면하는 사람이라고 확신합니다.

이 웹 사이트에서 권장되는 Linear Algebra 책을 읽고 있지만 여기에 적용해야할지 모르겠습니다.


브레이크 아웃 전에 탁구를 작성하면 공, 벽 및 패들 클래스를 내보내고 다른 유형의 벽돌 및 파워 업과 함께 작동하도록 확장 할 수 있습니다. 또한, 나는 탁구보다 브레이크가 더 간단하다고 생각합니다.
시크릿

답변:


30

내 홈페이지 의 탁구에서 사용한 관련 논리 는 다음과 같습니다.

본질적으로 볼이 패들과 충돌 할 때 방향은 완전히 무시됩니다. 패들 중심에서 얼마나 멀리 떨어져 있는지에 따라 새로운 방향이 부여됩니다. 공이 중앙의 패들에 맞으면 정확히 수평으로 보내집니다. 가장자리에 맞으면 극단적 인 각도 (75도)로 날아갑니다. 그리고 항상 일정한 속도로 움직입니다.

var relativeIntersectY = (paddle1Y+(PADDLEHEIGHT/2)) - intersectY;

패들의 중간 Y 값을 취하고 공의 Y 교차점을 뺍니다. 패들이 높이가 10 픽셀 인 경우이 숫자는 -5와 5 사이입니다. 이제이 패들이 "패들 공간"에 있기 때문에 "상대적 교차"라고 부릅니다.

var normalizedRelativeIntersectionY = (relativeIntersectY/(PADDLEHEIGHT/2));
var bounceAngle = normalizedRelativeIntersectionY * MAXBOUNCEANGLE;

상대 교차점을 취해 패들 높이의 반으로 나눕니다. 이제 -5에서 5까지의 숫자는 -1에서 1까지의 10 진수입니다. 그것은 것 정규화 . 그런 다음 공을 튕길 수있는 최대 각도로 곱하십시오. 5 * Pi / 12 라디안 (75도)으로 설정했습니다.

ballVx = BALLSPEED*Math.cos(bounceAngle);
ballVy = BALLSPEED*-Math.sin(bounceAngle);

마지막으로 간단한 삼각법을 사용하여 새로운 공 속도를 계산하십시오.

이것은 효과가 아닐 수도 있고, 정규화 된 상대 교차점에 최대 속도를 곱하여 속도를 결정할 수도 있습니다. 이것은 공이 패들의 가장자리 근처에 닿으면 빨라지거나 중앙 가까이에 닿으면 느려집니다.


벡터의 모양이나 볼의 벡터 변수 (속도 및 방향)를 저장하는 방법에 대한 코드가 필요할 수 있습니다.

벡터는 속도와 방향을 모두 암시 적으로 포함합니다. 내 벡터를 "vx"및 "vy"로 저장합니다. 즉, x 방향의 속도와 y 방향의 속도입니다. 물리학 입문 과정을 수강하지 않았다면 다소 이질적인 것처럼 보일 수 있습니다.

내가 이렇게하는 이유는 프레임 당 계산이 줄어들 기 때문입니다. 모든 프레임에서 방금 수행 한 시간 x += vx * time;y += vy * time;마지막 프레임 이후 시간이 밀리 초 단위이므로 속도는 밀리 초당 픽셀로 표시됩니다.


공을 구부리는 능력을 구현하는 것과 관련하여 :

우선, 공을 쳤을 때 패들의 속도를 알아야합니다. 즉, 패들의 이력을 추적해야 패들의 과거 위치를 하나 이상 알 수 있으므로 현재 위치와 비교하여 움직일 수 있는지 확인할 수 있습니다. (위치 변경 / 시간 변경 = 속도; 두 개 이상의 위치와 해당 위치의 시간이 필요합니다)

이제 공 의 각속도 를 추적해야 합니다. 공의 이동 속도 를 실제로 나타내지 만 공의 실제 회전과 같습니다. 패들과 충돌 할 때 볼의 상대 위치에서 바운스 각도를 보간하는 방법과 유사하게 충돌시 패들 속도에서이 각속도 (또는 스핀)를 보간해야합니다. 바운스 각도로 단순히 스핀을 설정하는 대신 게임에서 잘 작동하는 경향이 있기 때문에 공의 기존 스핀에 더하거나 빼기를 원할 수 있습니다 (플레이어가 볼이 회전하는 것을 볼 수 있고 회전하게 함). 더 강하게 회전하거나 똑바로 움직 이도록 스핀에 대응하십시오).

그러나 이것이 가장 상식적이고 아마도 그것을 구현하는 가장 쉬운 방법이지만 바운스의 실제 물리는 물체에 부딪 치는 속도에만 의존하지는 않습니다. 표면에 비스듬히 치는 각속도 (스핀 없음)가없는 물체에는 스핀이 가해집니다. 이것은 더 나은 게임 메카닉으로 이어질 수 있습니다. 그래서 당신은 이것을 조사하고 싶을 것입니다. 그러나 나는 그 뒤에 물리가 확실하지 않기 때문에 그것을 설명하려고하지 않을 것입니다.


그 효과는 내가 추구하는 것입니다. 막대의 가장자리에 닿을 때 빠른 속도. 이것을 쓸 시간을내어 주셔서 감사합니다. 그래도 몇 가지 사항을 이해하는 데 어려움이 있습니다. 예를 들어, 첫 번째 스니퍼에서 'intersectY'는 무엇입니까? 또한 'paddle1Y'는 막대의 높이가 맞습니까?

교차는 공이 패들을 교차하는 위치입니다. 나는 솔직히 이해조차 못하는 너무 복잡한 계산을 수행하지만 본질적으로 충돌 할 때 공의 Y 값입니다. paddle1Y는 화면 상단에서 패들의 Y 값입니다. PADDLEHEIGHT는 패들의 높이 ( "바")입니다.
Ricket

"곡선"볼을 허용하려면 무엇을 추가해야합니까? 예를 들어, 공이 패들에 부딪 치려고 할 때 공을 곡선으로 만들기 위해 패들을 움직입니다. 다음과 같은 것 : en.wikipedia.org/wiki/Curve_ball
Zolomon

편집 내용을보고 여러분의 생각을 알려주십시오 (만약 무언가에 대한 더 많은 정보가 필요하면 무언가를 얻지 마십시오)
Ricket

감사! 훌륭한 대답, 나는 항상 그 효과를 달성하는 방법을 궁금해했습니다!
Zolomon

8

내가이 일을한지 오래되었지만, 나는 이것이 옳았다 고 생각합니다.

완벽한 충돌이 주어지면 반사각은 입사각과 같습니다.

패들의 정상을 알고 있습니다 (평평한 표면을 가정) : N 공의 원래 위치를 알고 있습니다 (시간 단계 시작시). P '충돌 지점을 알고 있습니다. C 세그먼트 P-> P'가 패들을 통과한다고 계산하면 새 반사 위치 (P '')는 다음과 같습니다.

P '+ 2 * (N * (P'도트 -N))

N * (P 'dot -N) 하위 표현식은 공이 이동 한 충돌의 법선을 따라 깊이를 계산합니다. 빼기 부호는 법선 방향 반대의 깊이를 확인하고 있다는 사실을 수정하는 것입니다.

부분 표현의 P '+ 2 * 부분은 충돌 깊이 위로 공을 충돌면 위로 2 배 뒤로 이동시킵니다.

완전 충돌보다 덜 충돌하려면 요소 2를 (1 + (1-k))로 변경하십시오. 여기서 k는 마찰 계수입니다. 완벽한 충돌의 ak 값은 0이며, 반사 각도는 들어오는 각도와 정확히 동일합니다. k 값이 1이면 공이 충돌면의 표면에 머무르는 충돌이 발생합니다.

새로운 속도 벡터 V '', 방향은 P ''-C입니다. 정규화하고 입력 속도를 곱하면 결과 속도 크기는 동일하지만 새로운 방향입니다. 결과 속도를 증가 시키거나 (l> 1) 감소시키는 (l <1) 계수 l을 곱하면 해당 속도로 원숭이를 움직일 수 있습니다.

요약:

P ''= P '+ (1-k) * (N * (P dot -N)) V' '= l * V * ((P' '-C) / | P' '-C |)

여기서 k와 l은 선택한 계수입니다.


5

반사는 "올바른"또는 "쉬운"수행 될 수 있습니다.

"올바른"방법은 벽에 수직 인 벡터를 계산하는 것입니다. 2D에서 그것은 매우 쉽고 아마 하드 코딩 할 수 있습니다. 이어서, 반사 단계는 본질적으로 모션의 "평행 한"구성 요소를 그대로두고 "수직"구성 요소를 반전시킨다. 웹에 대한 자세한 정보가있을 수 있으며 아마도 MathWorld에도 있습니다.

"쉬운"방법은 벽에 부딪 칠 때 X 또는 Y 모션을 무효화하는 것입니다. 측면 벽에 부딪히면 X를 무효화합니다. 상단에 부딪 치면 Y를 무효화합니다. 공의 속도를 높이려면 원하는 것을 늘리십시오. X 및 Y 속도를 곱하여 현재 방향으로 속도를 높이거나 한 축에서만 속도를 높일 수 있습니다.


위에서 설명한 "쉬운"방법과 "올바른"방법이 아닌가?
Tom

벽이 주요 축을 따라 있으면 정확히 동일합니다. 벽이 모두 X, Y 및 Z 축을 따라 있지 않으면 두 개가 완전히 다릅니다.
dash-tom-bang

5

나는 arkanoid-ish 게임을 직접 만들고 있는데 패들을 때릴 때 공이 어떻게 행동해야하는지에 대한 해결책은 sin / cos 접근법에 들어가는 것보다 훨씬 간단하고 빠르다고 생각합니다. 이 같은 게임. 내가하는 일은 다음과 같습니다.

  • 물론, 볼 속도가 시간에 따라 증가하기 때문에 정확한 충돌 감지를 유지하기 위해 x / y 전 / 후 단계를 보간하기 때문에 각 속도 성분을 형성된 벡터의 계수로 나눈 모든 "stepX"및 "stepY"를 반복합니다. 현재와 ​​미래의 공 위치.

  • 패들과의 충돌이 발생하면 Y 속도를 20으로 나눕니다.이 "20"은 공이 패들의 측면에 닿을 때 결과 최대 각도를 얻는 가장 편리한 값이지만, 원하는대로 변경할 수 있습니다. 당신의 요구는, 단지 몇 가지 가치를 가지고 당신을 위해 더 나은 선택하십시오. 이 숫자 (20)에 의해 초기 게임 속도 인 5의 속도를 나누면 0.25의 "반발 계수"를 얻습니다. 이 계산은 속도가 예를 들어 15가 될 수있는 최대 속도 값 (예 : 15/20 = 0.75)까지 시간이 증가 할 때 각도를 상당히 비례 적으로 유지합니다. 내 패들 x, y 좌표가 중간으로 처리된다는 것을 고려하면 (x와 y는 패들의 중심을 나타냄)이 결과에 볼 위치와 패들 위치의 차이를 곱합니다. 차이가 클수록 결과 귀각. 또한 미드 핸들 드 패들을 사용하면 중심을 계산할 걱정없이 볼이 치는쪽에 따라 x 증분에 대한 정확한 부호를 얻을 수 있습니다. 의사 코드에서 :

n = 0에서 모듈러스로 ...

충돌이 감지되면 speedX =-(speedY / 20) * (패들 X-ballX); speedY = -speedY;
출구; 경우 종료

...

x = x + stepX; y = y + stepY;

끝나다

기억하십시오, 항상 물건을 단순하게 유지하십시오. 도움이 되길 바랍니다!


4

설명하는 스타일을 따르는 브레이크 아웃의 패들은 일반적으로 곡면으로 모델링됩니다. 입사각은 패들의 위치에 따라 달라집니다. 데드 센터에서는 커브의 접선이 완전히 수평이며 공이 예상대로 반영됩니다. 중심에서 멀어지면 커브에 접하는 각도가 점점 더 커지고 결과적으로 공이 다르게 반사됩니다.

요점은 볼 속도가 아닌 반사 각도가 변화한다는 것입니다. 볼 속도는 일반적으로 시간이 지남에 따라 천천히 증가합니다.


1
당신은 곡면이라고 말하고 내 머리에는 논리적으로 들리지만 일단 코드면에서 생각하면 퍼지가 빨라집니다. 코드에서 변수 또는 무언가로 어떻게 선언 할 수 있습니까?

같은 것은 angle = 1 - 2 * (ball.x - paddle.left) / paddle.width1에서 -1 사이의 숫자를 줄 것입니다. 이것은 (게임 메카닉을 위해 약간의 가치가 조정 된 시간) 공이 충돌 한 지점에서 접선의 기울기입니다. 엄격하게 수평이 아닌 선을 반영하십시오.

4

놀란 부쉬 넬 은 지난 주말 SIEGE에서 기조 연설을했고 오리지널 탁구와 비슷한 문제에 대해 이야기했습니다. 복잡한 계산을 많이 할 필요는 없습니다. 패널의 왼쪽 부분을 향해 쳤다면 공을 왼쪽으로 보내십시오. 오른쪽도 똑같이하십시오.

먼저 왼쪽과 오른쪽의 각도를 45 도로 만들 수 있습니다. 게임을 마치면 돌아가서 더 복잡하게 만들 수 있지만 처음부터 최대한 간단하게 만들 수 있습니다.


1
나는 기조 연설을 보았는데, 이것이 그의 결정은 수학적인 결정이 아니라 디자인 결정이라는 것이었다. 또한 원래 Pong and Breakout에서 속도는 볼 / 패들 충돌 횟수의 함수였습니다 (따라서 시간이 지남에 따라 속도가 빨라짐). 또한 특정 횟수의 타격 후에도 패들 크기를 줄였습니다. 나는 공이 똑바로 올라가는 것을 피하고 패들을 무기한으로 떠날 수 있습니다.
Ian Schreiber

4

브레이크 아웃은 물리 기반 게임 프로그래밍의 세계로 뛰어 들기 시작한 고전 초보자입니다. 기본적으로 공은 벽에 닿으면 바운스 동작을합니다. 위의 누군가가 제안한 것처럼 입사각은 반사각과 같습니다. 그러나 공이 패들을 때리는 것을 고려할 때. 논리는 3 개의 섹션으로 나뉩니다. 1.) 공이 패들의 중앙 부분에 닿습니다. 2.) 공이 패들의 왼쪽 부분을 쳤다. 3.) 공이 패들의 오른쪽 위치에 닿았습니다.

중앙 부분을 고려할 때 : 볼을 칠 때 적용되는 바운스 효과와 다르지 않아도됩니다. 공은 정상적으로 편향되지만, 어느 한 방향에 도달하면 케이스가 다릅니다.

공이 왼쪽에 닿을 때, 예를 들어 공이 화면의 왼쪽에서 오는 것을 고려하면 오른쪽에서 패들이옵니다. 그런 다음 왼쪽 부분으로 공을 쳤을 때 공은 원래 방향, 즉 같은 각도로 왼쪽 방향으로 반사되어야합니다. 같은 경우도 마찬가지입니다. 오른쪽 부분에서도 같은 것이 적용됩니다.

볼이 맞았을 때 왼쪽 또는 오른쪽 방향으로 볼을 움직이면 더욱 믿을 수 있습니다.

최소한 논리적 인 아이디어를 얻길 바랍니다. 감사합니다.


1

패들 중심과 볼 Y가 치는 지점 사이의 거리를 계산하고이를 호출한다고 상상해보십시오 d. d공이 패들 중심을 쳤을 때 양의 값을 가졌다 고 가정 해 봅시다 . 이제 d * -0.1공의 Y 속도에 추가 할 수 있으며 방향이 바뀌기 시작합니다. 다음은 C #으로 쉽게 번역 될 수있는 자바 스크립트의 예입니다!

var canvas = document.querySelector('canvas');
var resize = function () {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
};
resize();
var ctx = canvas.getContext('2d');
var ball = {
  size: 3,
  x: 1,
  y: canvas.height/2,
  vx: 2,
  vy: 0
};
var paddle = {
  height: 40,
  width: 3,
  x: canvas.width/2,
  y: canvas.height/2
};
addEventListener('mousemove', function (e) {
  paddle.y = e.clientY - (paddle.height/2);
});
var loop = function () {
  resize();
  ball.x += ball.vx;
  ball.y += ball.vy;
  if (ball.x > canvas.width || ball.x < 0) ball.vx *= -1; // horiz wall hit
  if (ball.y > canvas.height || ball.y < 0) ball.vy *= -1; // vert wall hit
  if (ball.x >= paddle.x && ball.x <= paddle.x + paddle.width && ball.y >= paddle.y && ball.y <= paddle.y + paddle.height) {
    // paddle hit
    var paddleCenter = paddle.y + (paddle.height/2);
    var d = paddleCenter - ball.y;
    ball.vy += d * -0.1; // here's the trick
    ball.vx *= -1;
  }
  ctx.fillRect(ball.x,ball.y,ball.size,ball.size);
  ctx.fillRect(paddle.x,paddle.y,paddle.width,paddle.height);
  requestAnimationFrame(loop);
};
loop();
body {overflow: hidden; margin: 0}
canvas {width: 100vw; height: 100vh}
<canvas></canvas>



0

안녕하세요, 저는 최근에 볼 게임을 만들려고 노력했고 이것에 대한 해결책을 찾았습니다. 그래서 내가 한 일 : 우리가 게임을하는 동안 패들이 움직이고 있습니다. 내 좌표계는 그대로 남아 있으며 캔버스의 왼쪽 상단 점은 0,0입니다. 이 좌표계에서 패들이 움직이고 있습니다. x 축은 0에서 캔버스 너비를 가리키고 y 축은 0에서 캔버스 높이를 가리 킵니다. 고정 크기 100 너비와 20 높이의 패들을 만들었습니다. 그리고 그 주위에 상상의 원을 그립니다. 공이 패들을 때리면 패들의 중심점을 계산합니다

double paddleCenter=Squash.paddle.getXpos()+Squash.paddle.getPaddleWidth()/2;

그런 다음 좌표계가 패들의 중심에 위치하는 방식으로 볼의 현재 위치에서 중심을 뺍니다. ballCenter는 공이 패들을 치는 지점입니다 (-(패들 폭 + r) .. 0 .. )) 이것은 외륜의 타격 지점을 재조정하는 것입니다.

double x0 = ballCenterX-paddleCenter;

공의 타격 지점 (x0)의 도움으로 원의 교점을 계산하십시오. 이것은 재 계산이며, 우리는 이미 알려진 x0 좌표를 가진 원에 y 좌표를 요구하고 y 축에 대한 플립이 필요했습니다

double y0 = -Math.sqrt(paddleRadius*paddleRadius-x0*x0);

paddleRadius f (x, y) = x ^ 2 + y ^ 2-r ^ 2로 패들 주위에 정의 된 원의 정규 방정식의 미분을 계산합니다.

double normalX=2*x0;
double normalY=2*y0;

표면 법선에 대한 단위 벡터를 얻기 위해 N 벡터를 정규화

double normalizer=Math.sqrt(normalX*normalX + normalY*normalY);
normalX=normalX/normalizer;
normalY=normalY/normalizer;

이제 패들에 대한 정규화 된 (단위) 표면 법선이 있습니다. 이 표면 법선을 사용하여 새로운 방향을 계산하면 반사 벡터 공식을 사용하여 계산됩니다. 공이 패들에 부딪 치는 지점에서 지점으로 변경

double eta=2; //this is the constant which gives now perfect reflection but with different normal vectors, for now this set to 2, to give perfect reflection
double dotprod=vX*normalX+vY*normalY;
vX=vX-eta*dotprod*normalX;//compute the reflection and get the new direction on the x direction
vY=-vY;//y direction is remain the same (but inverted), as we just want to have a change in the x direction

이 문제에 대한 해결책을 발표했습니다. 자세한 내용과 전체 게임을 보려면 내 github 저장소를 볼 수 있습니다.

https://github.com/zoli333/BricksGame

이클립스와 자바로 작성되었습니다. Ball.java에 주석 처리 된 또 다른 해결책이 있습니다. 크기 조정이 발생하지 않는 경우 좌표 좌표계를 패들의 중심점으로 이동하지 않고 대신 위 왼쪽의 0,0 좌표계에서 위의 모든 것을 계산합니다 패들의 중심점. 이것은 또한 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.