Pongでは、パドルから跳ね返ったボールの方向をどのように計算しますか?


28

ゲーム開発におけるこの非常にHello World-yの問題を回避しようとしています。XNAでTicTacToeゲームを作成したので、次のステップはBreakoutクローンになると思います。

ゲームプログラミングについて知識も、どこにどの数学を適用すべきかも知らないことを覚えておいてください。それが私がこの質問をしている理由です。


質問:画面の下部にあるパドルに当たったときにボールが跳ね返る場所を判断するにはどうすればよいですか?

私はそれが次のようなものになると想像します:

  1. 入ってくるボールから速度と角度をキャプチャします。
  2. バーに触れた場所(左端、右端、中央)を検出し、それに従って外側の領域に触れた場合により高速になります。
  3. これは私が立ち往生しているところです。へへ。

洞察はありますか?これは単純な回答タイプの質問ではありませんが、ある時点で誰もが直面するものであると確信しています。

このウェブサイトで推奨されている「線形代数」という本を読んでいますが、ここで適用すべきかどうかはまだわかりません。


ブレイクアウトの前にピンポンを書くと、ボール、ウォール、パドルクラスをエクスポートし、さまざまな種類のレンガやパワーアップで動作するように拡張できます。さらに、ピンポンはブレイクアウトよりも簡単だと考えています。
シークレット

回答:


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;時間はミリ秒単位の最後のフレームからの時間です(したがって、速度はミリ秒あたりのピクセル単位です)。


ボールを曲げる機能の実装について:

まず、ボールがヒットしたときのパドルの速度を知る必要があります。つまり、パドルの履歴を追跡する必要があります。これにより、パドルの過去の位置を1つ以上把握し、現在の位置と比較して移動したかどうかを確認できます。(位置の変化/時間の変化=速度;したがって、2つ以上の位置とそれらの位置の時間が必要です)

また、ボールの角速度を追跡する必要があります。これは、実際にボールが移動する曲線を表しますが、ボールの実際のスピンに相当します。パドルとの衝突時のボールの相対位置からバウンス角を補間する方法と同様に、衝突時のパドルの速度からこの角速度(またはスピン)を補間する必要があります。バウンス角度で行うように単純にスピンを設定するのではなく、ゲームでうまく機能する傾向があるため、ボールの既存のスピンに加算または減算することができます(プレーヤーはボールが回転していることに気付き、スピンさせるさらに乱暴に、またはスピンを直進させるためにスピンに対抗します)。

ただし、これが最も一般的な方法であり、おそらく最も簡単な実装方法ですが、実際のバウンスの物理は、衝突するオブジェクトの速度だけに依存するわけではないことに注意してください。角速度のない(スピンのない)物体が斜めに表面にぶつかると、スピンが与えられます。これはより良いゲームメカニックにつながる可能性がありますので、あなたはこれを調べたいかもしれませんが、私はその背後にある物理学が確かではないので、それを説明しようとはしません。


その効果は私が目指しているものです。バーの端に当たる速度が速くなります。これを書くのに時間を割いてくれてありがとう。しかし、いくつかのことを理解するのに苦労しています。たとえば、最初のスニッパーで「intersectY」とは何ですか?また、「paddle1Y」はバーの高さが正しいですか?

intersectionYは、パドルと交差するボールの位置です。私は正直に今でも理解できないほど複雑な計算を行いますが、本質的には衝突するときのボールのY値です。paddle1Yは、画面上部からのパドルのY値です。PADDLEHEIGHTは、パドル(「バー」)の高さです。
リケット

「曲線」ボールを許可するために何を追加する必要がありますか?たとえば、ボールがパドルに当たろうとするとき、ボールをカーブさせるためにパドルを動かします。次のようなもの:en.wikipedia.org/wiki/Curve_ball
ゾロモン

編集を参照して、考えを教えてください(何かについてもっと情報が必要な場合、何かを得ない、など)
リケット

ありがとう!素晴らしい答え、私はいつもその効果を達成する方法を疑問に思っていました!
ゾロモン

8

これをやってからしばらく経ちましたが、これは正しいと思います。

完全な衝突が与えられると、反射角は入射角に等しくなります。

パドルの法線を知っている(平らな表面を想定):Nボールの元の位置を知っている(タイムステップの開始時):Pボールの新しい位置を知っている(タイムステップの終了時): P '衝突点を知っています:CセグメントP-> P'がパドルを通過すると計算したと仮定すると、新しい反射位置(P '')は次のようになります。

P '+ 2 *(N *(P'ドット-N))

N *(P 'dot -N)部分式は、ボールが移動した衝突の法線に沿った深さを計算します。マイナス記号は、法線の方向と反対の深さをチェックしているという事実を修正するためのものです。

P '+ 2 *部分式の一部は、衝突の深さの2倍でボールを衝突面の上に戻します。

完全ではない衝突が必要な場合は、係数2を(1 +(1-k))に変更します。ここで、kは摩擦係数です。完全な衝突のk値は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でも、これに関する詳細な情報がWeb上にあります。

「簡単な」方法は、壁にぶつかったときにXまたはYの動きを無効にすることです。サイドウォールに当たった場合、Xを無効にします。トップに当たった場合、Yを無効にします。ボールをスピードアップしたい場合は、何でも増やします。XとYの両方の速度を乗算することで現在の方向に速度を上げることができます。または、1つの軸でのみ速度を上げることができます。


上記の「簡単な」方法と「正しい」方法は本質的に同じではありませんか?
トム14年

壁が主軸に沿っている場合、それらはまったく同じです。壁がすべてX、Y、およびZ軸に沿っていない場合、いいえ、2つは完全に異なります。
ダッシュトムバン14年

5

私も自分でアルカノイドのようなゲームを作っていますが、パドルを打ったときのボールの振る舞いの解決策は、sin / cosアプローチに入るよりもはるかに簡単で速いと思います...それは、このようなゲーム。ここに私がやることがあります:

  • もちろん、ボールの速度は時間とともに増加するため、正確な衝突検出を維持するために前後のx、yステップを補間し、各速度成分を形成されたベクトルの係数で除算して計算されるすべての「stepX」および「stepY」をループします現在および将来のボール位置によって。

  • パドルとの衝突が発生した場合、Y速度を20で除算します。この「20」は、ボールがパドルの側面に当たったときに得られる最大角度を得るために見つけた最も便利な値ですが、任意に変更できますあなたのニーズは、ただいくつかの価値で遊んで、あなたにとってより良いものを選ぶことです。この数値(20)で私の最初のゲーム速度である5の速度を除算すると、0.25の「リバウンド係数」が得られます。この計算により、速度が最大速度値(たとえば、15/20 = 0.75)に達するまで速度が増加しても、角度はほぼ比例します。パドルのx、y座標が中間処理されていることを考慮して(xとyはパドルの中心を表します)、この結果にボール位置とパドル位置の差を掛けます。差が大きいほど、その結果、角度が大きくなります。また、中央のパドルを使用すると、中心の計算を気にせずに、ボールが当たる側に応じてx増分の正しい符号を取得できます。擬似コード内:

n = 0の場合、モジュラス...

collision_detectedの場合、speedX =-(speedY / 20)*(paddleX-ballX); speedY = -speedY;
出口; 終了する場合

...

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

で終わる

覚えておいて、常に物事をシンプルに保つようにしてください。私はそれが役立つことを願っています!


4

ブレイクアウトのパドルは、説明しているスタイルに従っている場合、通常は曲面としてモデル化されます。入射角は、パドルのどこに当たるかに基づいて変化します。死点では、曲線の接線は完全に水平であり、ボールは予想どおりに反射します。中心から離れると、曲線の接線の角度が大きくなり、結果としてボールの反射が異なります。

重要な点は、ボールの速度ではなく反射角が変化することです。一般的に、ボールの速度は時間とともにゆっくりと増加します。


1
あなたは曲面として言って、それは私の頭の中で論理的に聞こえますが、一度コードの観点から考えようとすると、物事は速くぼやけます。それをコードで変数または何かとして宣言することさえできますか?

次のようなものangle = 1 - 2 * (ball.x - paddle.left) / paddle.widthが1から-1の間の数を与えます。これ(ゲームメカニクスのために調整された値)は、ボールが衝突したポイントでの接線の勾配です。厳密に水平な線ではなく、その線に反射します。

4

ノーランブッシュネルは先週末、SIEGEで基調講演を行い、元のピンポンと同様の問題について話しました。複雑な計算をたくさんする必要はありません。パネルの左側に向かって打った場合、ボールを左に送ります。右側にも同じことを行います。

まず、左側と右側の角度を45度にすることができます。ゲームを終了したら、戻ってこれをより複雑にしたい場合でも、最初からできる限り簡単にすることができます。


1
私はその基調講演も見ました。彼のポイントは、これは数学的な決定ではなく設計上の決定であるということでした。さらに、元のポンとブレイクアウトでは、速度はボールとパドルの衝突回数の関数でした(したがって、時間の経過とともに速度が上がります)。また、一定数のヒットの後、パドルのサイズも小さくなりました。しかし、ボールを真っ直ぐ上に移動させないようにすると、パドルを無期限にそのままにしておくことができます。
イアンシュライバー

4

Breakoutは、物理学ベースのゲームプログラミングの世界に飛び込み始めるための古典的な初心者向けの作品です。基本的に、ボールは壁に当たると跳ね返ります。上記の誰かが示唆したように、入射角は反射角に等しい。しかし、パドルを打つボールを考えるとき。ロジックは3つのセクションに分かれています。1.)パドルの中央部分を打つボール。2.)パドルの左部分を打つボール。3.)パドルの右方向の位置にボールが当たります。

中央部分を考慮する場合:ボールを打つときに適用されるバウンス効果と異なる必要はありません。ボールは通常どおりに偏向されますが、どちらの方向に打たれても、ケースは異なります。

ボールが左側に当たったとき、つまり、画面の左側からボールが来ており、右側からパドルで来ていると考えてください。次に、左向きの部分でボールを打つと、ボールは.ie.leftwardsからの方向と同じ角度で反射します。同じ場合も同様です。右側の部分にも同じことが当てはまります。

打たれたときにボールが左方向または右方向に向かって移動することにより、ボールがより信頼できるものになります。

少なくとも論理的にはあなたがアイデアを手に入れたことを願っています。ありがとう


1

パドルの中心と、ボールYがヒットしたポイントとの間の距離を計算して、それを呼び出すと想像してくださいddボールがパドルの中心より上に当たったときに正の値を持つと仮定しましょう。d * -0.1これで、ボールのY速度に追加でき、方向が変わり始めます。これは簡単にc#に翻訳できるjavascriptの例です!

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は、ボールがパドルに当たる点です(-(paddlewidth + r).. 0 ..(paddlewidth + r ))これは、パドルのヒッティングポイントを再スケーリングする以外の何物でもありません

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;

これで、パドルの正規化された(ユニット)サーフェス法線ができました。これらの表面法線を使用して新しい方向を計算します。これは、反射ベクトル公式new_direction = old_direction-2 * dot(N、old_direction)* Nを使用して計算されますが、代わりに常に法線が上向きの場合、法線はボールがパドルを打つポイントからポイントに変化している

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

EclipseでJavaで書かれています。Ball.javaにコメントアウトされた別のソリューションがあります。再スケーリングは行われず、座標座標系をパドルの中心点に移動せず、代わりに上記のすべてを左上の0,0座標系から計算しますパドルの中心点。これも機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.