Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Wie lässt sich das abstoßen von 2 Kugeln berechnen?


Bergmann89 - Mo 19.02.07 11:37
Titel: Wie lässt sich das abstoßen von 2 Kugeln berechnen?
HI,

es gab vor ein paar wochen schonma so'n Thema. das hat mich auf die Idee gebracht n Billjardspiel zu programmieren. Nun ist das eintige woran es hängt die berechnung wie sich die beiden Kugeln voneinander abstoßen. Ich hab schon edliche Skizzen gemacht und irgedwie versucht mit Winkelfkt. die Flugbahn zu berechnen aber ich komm einfach nicht dahinter. Kann mir da jemand helfen?! Die rein physikalische Formel würde reichen umsetzung in Delphi müsste ich selber hin bekommen.

MfG & Thx Bergmann.


rockminstrel - Mo 19.02.07 11:44

Also eigentlich müsste es so sein: (Billardbeispiel) die rote Kugel bewegt sich geradlinig in die Richtung, die Berührungspunkt mir der weißen ugel und Zentrum bilden. Deswegen testen die Spieler oft, wo man die rote Kugel treffen muss um sie direkt ins Loch zu stoßen.

Was ich dir nicht sagen kann ist wohin die weiße Kugel geht nachdem Sie die rote berührt hat.


Dragonclaw - Mo 19.02.07 11:48

Hallo,

das ganze nennt sich dezentraler elastischer Stoß.

Es gab mal einen Beitrag hier im Forum wo jemand genau dieses Problem gelöst hatte. Ich würde hier gerne delfiphan quoten, aber der Beitrag scheint down zu sein. Hier mal der Link: http://www.delphi-forum.de/viewtopic.php?t=47305&highlight=tkugel+class+tshape

Allerdings habe ich mir damals den Code geladen und hab den auch noch wieder gefunden. Hier ist der Code:

Zitat:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
unit Unit1;

interface

uses
  SysUtils, Forms, StdCtrls, ExtCtrls, Classes, Controls;

const
 dt = 20// in ms
 SubIterations = 20;
 KugelCount = 10;

type
  TKugel = class;

  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Kugeln: array[0..KugelCount-1of TKugel;
  end;

  Borders = (boLeft, boTop, boBottom, boRight);
  BordersSet = set of Borders;
  TKugel = class(TShape)
  private
    FX, FY: Extended;
    FR: Extended;
    FM: Extended;
    FVx, FVy: Extended;
    procedure setR(const Value: Extended);
  public
    property X: Extended read FX write FX; // Position [Pixel]
    property Y: Extended read FY write FY;
    property R: Extended read FR write setR; // Radius [Pixel]
    property M: Extended read FM write FM; // Masse
    property Vx: Extended read FVx write FVx; // Geschwindigkeit [Pixel/s]
    property Vy: Extended read FVy write FVy;
    procedure Move; // Nächster Zeitschritt
    procedure Apply;
    function CollidesWith(const Kugel2: TKugel): Boolean;
    function WallCollisions: BordersSet;
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

 { TKugel }

function TKugel.CollidesWith(const Kugel2: TKugel): Boolean;
begin
  Result := sqr(FX-Kugel2.FX)+sqr(FY-Kugel2.FY) < sqr(FR+Kugel2.FR);
end;

constructor TKugel.Create(AOwner: TComponent);
begin
  inherited;
  Shape := stCircle;
end;

procedure TKugel.Move;
begin
  X := X + (dt/1000)/SubIterations*FVx; // ms => s
  Y := Y + (dt/1000)/SubIterations*FVy;
end;

function TKugel.WallCollisions: BordersSet;
const
  Border = 10;
begin
  Result := [];
  if X-Border < R then // links über Rand
   Result := Result + [boLeft];
  if Y-Border < R then // oben über Rand
   Result := Result + [boTop];
  if X+Border >= Parent.ClientWidth-R then // rechts über Rand
   Result := Result + [boRight];
  if Y+Border >= Parent.ClientHeight-R then // unten über Rand
   Result := Result + [boBottom];
end;

procedure TKugel.setR(const Value: Extended);
begin
  FR := Value;
  Width := Round(FR*2); // Grösse des Shapes mitändern
  Height := Round(FR*2);
end;

procedure TKugel.Apply;
begin
  Left := round(X-FR); // Position des Shapes anpassen
  Top := round(Y-FR);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
const
 Border = 10;
var
 I: Integer;
begin
  // Timer initialisieren
  with Timer1 do
  begin
   Interval := dt;
   OnTimer := Timer1Timer;
  end;

  // Kugeln zufällig in den Raum platzieren
  for I := 0 to Length(Kugeln)-1 do
  begin
    Kugeln[I] := TKugel.Create(Form1);
    with Kugeln[I] do
    begin
     Parent := self;
     R := 20+Random(8);
     X := Random(self.ClientWidth-2*Round(R+Border))+Round(R+Border);
     Y := Random(self.ClientHeight-2*Round(R+Border))+Round(R+Border);
     Vx := Random(400);
     Vy := Random(400);
     M := 4/3*R*R*R*pi;
     Brush.Color := Random(256*256*256); // farbige Bälle
    end;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
 DX, DY, M11, M21, M12, M22, L, Vp1, Vp2, Vs1, Vs2, MTot, Vp1_, Vp2_: Extended;
 I, J, K: Integer;
 B: BordersSet;
begin
  for K := 0 to SubIterations-1 do
  begin
   // Position nachführen (gemäss gegebenen Geschwindigkeiten)
   for I := 0 to Length(Kugeln)-1 do
    Kugeln[I].Move; 

   // Kollisionserkennung zwischen Kugeln
   for I := 0 to Length(Kugeln)-1 do
    for J := I+1 to Length(Kugeln)-1 do // über alle geordneten Paare
     // Falls Kollision zwischen Kugel I und Kugel J:
     if Kugeln[I].CollidesWith(Kugeln[J]) then
     begin
      DX := Kugeln[j].X-Kugeln[i].X; // Delta x
      DY := Kugeln[j].Y-Kugeln[i].Y; // Delta y
      L := sqrt(sqr(DX)+sqr(DY)); // Abstand
      // Matrix mit neuen Koordinatenachsen (parallel/senkrecht zum Stoss)
      M11 := DX/L; M12 := -DY/L;
      M21 := DY/L; M22 := DX/L;

      // Koordinatentransformation
      Vp1 := Kugeln[i].Vx*M11+Kugeln[i].Vy*-M12;
      Vs1 := Kugeln[i].Vx*-M21+Kugeln[i].Vy*M22;
      Vp2 := Kugeln[j].Vx*M11+Kugeln[j].Vy*-M12;
      Vs2 := Kugeln[j].Vx*-M21+Kugeln[j].Vy*M22;

      if Vp1-Vp2<0 then
       Continue; // Bälle gehen bereits auseinander
      // Zentraler Stoss
      MTot := Kugeln[i].M+Kugeln[j].M;
      Vp1_ := (Kugeln[i].M-Kugeln[j].M)/MTot*Vp1+2*Kugeln[j].M/MTot*Vp2;
      Vp2_ := (Kugeln[j].M-Kugeln[i].M)/MTot*Vp2+2*Kugeln[i].M/MTot*Vp1;

      // Rücktransformation
      Kugeln[i].Vx := Vp1_*M11+Vs1*M12;
      Kugeln[i].Vy := Vp1_*M21+Vs1*M22;
      Kugeln[j].Vx := Vp2_*M11+Vs2*M12;
      Kugeln[j].Vy := Vp2_*M21+Vs2*M22;
     end;

   // Wandkollisionen
   for I := 0 to Length(Kugeln)-1 do
    with Kugeln[I] do
    begin
     B := WallCollisions;
     if ((boLeft in B) and (Vx < 0)) or ((boRight in B) and (Vx > 0)) then 
      Vx := -Vx;
     if ((boTop in B) and (Vy < 0)) or ((boBottom in B) and (Vy > 0)) then
      Vy := -Vy;
    end;
   end// for K
  for I := 0 to Length(Kugeln)-1 do
   Kugeln[I].Apply; // Angezeigte Position updaten
end;

end.



Corpsman - Mo 19.02.07 11:49

Das wurde echt schon zig mal gefragt.

Schau dir einfach mal das
Sample [http://tyrann.deadbyte.de/corpsman/klickcounter.php?url=download/pingpong.zip]
an.


Bergmann89 - Di 20.02.07 08:19

OK, das hab ich gesucht...
Werd mich da jetzt erstma Durcharbeiten, DANKE.

Gibts da auch noch ne genaue Erklärung dazu, denn die Variablen sind
nicht grad aussagefähig, mit manchen Befehlen weiß ich nicht viel
anzufangen und erläuterungen sind auch nicht sehr viele drin.
Ich will ja nicht meckern, aber wenn ich das bloß abscreib hab ich auch
nix davon...


reptile - Mo 02.04.07 18:33

ich habe mir das beispiel mal angesehen und muss bergmann89 recht geben: es wäre schön wenn das ganze besser auskommentiert wäre, nicht jeder kommt so hinter die variablenbezeichnungen.
ich verstehe zwar das vorgehen grob und es funktioniert auch wenn ich es einbaue, aber kann mir jemand die variablen:
M11, M21, M12, M22, Vp1, Vp2, Vs1, Vs2, Vp1_, Vp2_
erklären ?
ich vermute die M-s sind die neuen koordinatenachsen ? aber was ist Vp und Vs?

tut mir leid da kugel-kollision bestimmt schon tausendmal durchgekaut wurde, aber es ist für anfänger kein leichtes thema ^^


delfiphan - Di 03.04.07 00:12

Der FAQ-Beitrag berechnet den elastischen Stoss für Kugeln verschiedener Grössen und Massen. Bei Billardkugeln gibt es einfachere Formeln, da die Massen und Kugelradien für alle Kugeln gleich sind. Die Kugeln gehen dann z.B. immer im rechten Winkel zueinander auseinander.