FireMonkey で物理的な動きを視覚化する

By: Chikako Yonezawa

Abstract: この記事では、ビリヤードの物理学の一見単純なケースについて解説します。Delphi XE2 および FireMonkey を使用して (球の)衝突と、回転を視覚化します。

    前提条件!

プール(球突きの一種 またはビリヤード) のいくつかのゲームで遊ぶことは悪くはない。;)

    ビリヤードを数学的に

ビリヤードの球同士の衝突に注目します。また、球の回転のシミュレーションのほか、球の回転とテーブルの間の摩擦のシミュレーションを行います。

ひねり(横回転), 滑り(ゼロ摩擦), 回転の伝達などと同様なすべてを完全に無視します。ジャンプショット (重力)も無しです。

ここのビリヤードの物理 ページ上に、非常によく解説されたものがあります。

いくつかのベクトル代数の埃を払う準備はできていますか?3次元内の 2 つのオブジェクト間の衝突.. 運動エネルギーの維持と、運動量を維持することは、衝突後に次の速度が得られます。

         v1 = v1i - ((m2*c)/(m1 + m2))*(1 + Cr)*n

         v2 = v2i + ((m1*c)/(m1 + m2))*(1 + Cr)*n

式内の項目:

  • v1, v2 = 衝突後の球1、および、球 2 のベクトルの速度
  • v1i, v2i = 衝突直前の球 1 と球 2 の初期速度のベクトル
  • m1, m2 = 球1 と球 2 の質量
  • Cr = 反発係数 (Cr = 1 弾性衝突)
  • n = 球 1 から球 2 に対する垂直単位ベクトル
  • c =ドット積 (n, v1i - v2i)

これを Delphi で記述すると以下になります。

normal := Vector3DSubtract(Ball1.Position.Vector, Ball2.Position.Vector);
normal := Vector3DNormalize(normal);

c := Vector3DDotProduct(normal,Vector3DSubtract(Ball1.Velocity,Ball2.Velocity));

Ball1.Velocity := Vector3DSubtract(Ball1.Velocity, Vector3DScale(normal,Ball2.Mass*c/(Ball1.Mass+Ball2.Mass)*(1+Cr)));

Ball2.Velocity := Vector3DAdd(Ball2.Velocity, Vector3DScale(normal,Ball1.Mass*c/(Ball1.Mass+Ball2.Mass)*(1+Cr)));

OK, 球の同士の間に発生する衝突の面倒を見ることができます。レール(外枠) との衝突は、単に問題のレールに垂直な速度ベクトルの反転させることで、(大幅に)簡略化することができます。

    回転, 回転, 回転...

(テーブル上での) 回転を無視するのであれば、ビリヤードの球の一群は、回転することはなく、そのシミュレーションは奇妙に見えるでしょう。正確な回転を行う方法を見つけることはできませんでしたが、思いついた最高の方法は次のような関係でした。

         TDummyZ

          TDummyX

          TDummyY

          TBall

これで、それぞれの軸の周りを独立して回転することが可能で、複雑なベクトルの回転を回避することができます (もし、方法をご存じであれば教えてください)。

実行すべきことは、進行方向に球を回転させることです。進行方向は次の式によって求めます(単位は度)。

         180/Pi*ArcTan2(Velocity.Y,Velocity.X);

Delphi で記述すると:

NewZ := 180/Pi*ArcTan2(Ball[n].Velocity.Y,Ball[n].Velocity.X);
Ball[n].DummyZ.RotationAngle.Z := NewZ;
Ball[n].DummyX.RotationAngle.Z := -NewZ;
rX := Ball[n].RotationAngle.X;
rY := Ball[n].DummyY.RotationAngle.Y;
Ball[n].DummyY.RotationAngle.Y := rY - Ball[n].Velocity.X*2/Pi;
Ball[n].RotationAngle.X := rX + Ball[n].Velocity.Y*2/Pi;

    球のテクスチャを生成する

衝突の処理のほかの最もクールな部分として、ビリヤードの球を本物のように見せることです。以下の Delphi のコード部分は、ビリヤードの球に使用するテクスチャを生成します。

function MakeBallTexture(n : Integer) : TBitmap;
var
  BMP   : TBitmap;
  C     : TCanvas;
  Color : TAlphaColor;
begin
  BMP := TBitmap.Create(200,100);
  C := BMP.Canvas;

  C.BeginScene(nil);

  case n mod 8 of
    0 : Color := TAlphaColorRec.Yellow;
    1 : Color := TAlphaColorRec.Blue;
    2 : Color := TAlphaColorRec.Red;
    3 : Color := TAlphaColorRec.Violet;
    4 : Color := TAlphaColorRec.Orange;
    5 : Color := TAlphaColorRec.Green;
    6 : Color := TAlphaColorRec.Brown;
    7 : Color := TAlphaColorRec.Black;
  end;

  // Center fill
  C.Fill.Color := Color;
  C.Stroke.Color := Color;
  C.FillRect(RectF(0,30,200,70),0,0,[],1);

  // Top and bottom fill
  if n div 8 > 0 then begin
    C.Fill.Color := TAlphaColorRec.White;
    C.Stroke.Color := TAlphaColorRec.White;
  end;
  C.FillRect(RectF(0,0,200,30),0,0,[],1);
  C.FillRect(RectF(0,70,200,100),0,0,[],1);

  // Center circles and numbers
  C.Fill.Color := TAlphaColorRec.White;
  C.Stroke.Color := TAlphaColorRec.Black;
  C.FillEllipse(RectF(35,35,65,65),1);
  C.FillEllipse(RectF(135,35,165,65),1);
  C.Fill.Color := TAlphaColorRec.Black;
  C.Stroke.Color := TAlphaColorRec.Black;
  C.Font.Size := 24;
  C.FillText(RectF(135,35,165,65),IntToStr(n+1),False,1,[],TTextAlign.taCenter);
  C.FillText(RectF(35,35,65,65),IntToStr(n+1),False,1,[],TTextAlign.taCenter);

  C.EndScene;

  Result := BMP;
end;

参考までに、これは 8 番の球のテクスチャを生成します...

Hide image
Ball8

そして以下が 15番の球のテクスチャ...

Hide image
Ball15

    スクリーンショット

以下のスクリーンショットはデモアプリケーションのものです。手球を打つ前、完全なシナリオのシミュレーションの最終状態、その最終状態の角度を変えたものです。

Hide image
Click to see full-sized imageHide image
Click to see full-sized imageHide image
Click to see full-sized image

    デモアプリケーション

CodeCentral 上に、このデモアプリケーション のプロジェクト一式があります。このデモでいろいろプレイすることが可能です。気軽にテーブル上に手球を置き、3 次元(Z軸の速度だけではなく)での球の衝突をご覧ください。

    お問い合わせ先

embarcadero.com で aohlson へフィードバック等、メールでお気軽にご相談ください。

Server Response from: ETNASC04