Autor Beitrag
cuejo
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 142

Win XP
Delphi 7 Personal und 2005 PE
BeitragVerfasst: So 02.04.06 20:44 
Hallo Leute,

ich bin grad dabei ein Programm zu schreiben, welches die Gravitation simulieren soll. Bin auch schon recht weit gekommen, aber ich hab noch so meine Probleme:
1. Schaltet man in meinem Programm die Reibung aus, so springt die Kugel immer höher obwohl es eigentlich immer auf die gleiche "Maximalhöhe" kommen soll.
2. Prallt die Kugel and der rechten Seite ab, scheint er schneller zu werden.
3. Die horizontale Reibung wirkt bei niedrigen Geschwindikeiten zu abruppt und die Kugel hat dann plötzlich keine Horizontalbewegung mehr.
Na ja das sind so die gröbsten Bugs die ich gefunden habe, aber ich finde die Fehler nicht. :autsch:

Hier mal die Unit in der ich die Kugel und die Gravitation simuliere:
ausblenden volle Höhe 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:
[...]

public
  posx,posy, // Position
  r, //Radius
  bottom,top,left,right:integer;  // Spielfeldbegrenzungen
  vert,hori, // Geschwindigkeiten
  g,t:real;  // Gravitationsbeschleunigung, Zeit
  fri:boolean;  // Reibung an/aus
end;

implementation

procedure ball.update;   // neue Position neue Geschwindigkeiten
var v,h:real;  // Hilfsvariablen für zurückgelegten Weg
begin
vert:=vert + g*t;   // überlagerte Vertikalgeschwindigkeit
v:=vert*t*1000;     // Weg=Geschwindigkeit*Zeit (1000 px = 1 m)
posy:=trunc(posy+v);
vert:=air(vert);    // Luftwiderstand

// analog die Horizontalgeschwindigkeit
h:=hori*t*1000;
posx:=trunc(posx+h);
hori:=air(hori);
end;

procedure ball.collide;   // Kollisionsabfrage
begin
if (posy+r)>=(bottom-1then
  begin
    vert:=-1*vert;     // Invertierung der Richtung
    posy:=bottom-r-1;  // damit Kugel immer innerhalb des Shapes bleibt
    vert:=wall(vert);  // Reibung bei Kollision an Bande
  end;
if (posy-r)<(top+1then
  begin
    vert:=-1*vert;
    posy:=top+r+1;
    vert:=wall(vert);
  end;
if (posx+r)>(right-1then
  begin
    hori:=-1*hori;
    posx:=right-r-1;
    hori:=wall(hori);
  end;
if (posx-r)<(left+1then
  begin
    hori:=-1*hori;
    posx:=left+r+1;[/list]
    hori:=wall(hori);
  end;
end;

function ball.frame:trect; // TRect für die Zeichnung der Kugel mit Canvas
begin
result:=bounds(posx-r,posy-r,2*r,2*r);
end;

function ball.wall(value:real):real; // Reibung an Bande
begin
if fri then result:=value*0.8 else result:=value;
end;

function ball.air(value:real):real;  // Luftwiderstand
begin
if fri then result:=value*0.992 else result:=value;
end;

procedure ball.follow(x,y:integer);  // Kugel folgt Maus
begin
posx:=x;
posy:=y;
end;

Das ganze Programm gibt's hier: Gravity
Ich hoffe ihr könnt mir ein bisschen Helfen. :mrgreen:

Moderiert von user profile iconGausi: Code- durch Delphi-Tags ersetzt
Simon Joker
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 236
Erhaltene Danke: 1



BeitragVerfasst: Di 04.04.06 11:22 
Hi cuejo

Dein Problem sind grundsätzlich Rundungsfehler!

1. Stelle
Du rundest die aktuelle Position des Balls auf ganz Zahlen mittels trunc auf den nächstkleineren Pixelwert. dadurch ist der Ball IMMER etwas höher als die Berechnung ergeben hat. Das summiert sich halt und der Ball springt immer höher.
Lösung -> Mach die Position des Balls als Float-Wert. Runde diese nur bei der Darstellung.

2. Stelle
Wenn der Ball mit der Wand kollidiert, setzt du die Ball-Position einfach auf die Position, die der Ball zum Zeitpunkt der Kollision hätte. Dabei berechnest du aber NICHT die Geschwindigkeit, die der Ball dan hätte. So geht jedesmal etwas Speed verloren. Wenn du das erste Prob fixt, wird der Ball trotz deaktivierter Reibung immer etwas Höhe verlieren.
Lösung -> Berechne den Kollisionszeitung dediziert und passe die Position exakt an.

MfG Simon
cuejo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 142

Win XP
Delphi 7 Personal und 2005 PE
BeitragVerfasst: Di 04.04.06 22:02 
Vielen dank Simon Joker :mrgreen: ! Du bist der Beste :flehan: !
Ohne ein zweites paar Augen hätte ich den Fehler nie gefunden. Es lag tatsächlich an den Rundungsfehlern :oops: , obwohl ich immer der festen Überzeugung war darauf geachtet zu haben. Hab den Wald vor lauter Bäumen wohl nicht mehr gesehen. Aber jetzt ist es vollbracht :dance2: . Dank dir!
Mein Programm hat jetzt keine Fehler mehr... bis auf vielleicht ein paar kosmetischer Art. Das Problem mit der falschen Sprunghöhe ohne Reibung hab ich aber nicht durch eine neuberechnug der Vertikalgeschwindigkeit gelößt, da es mir zu umständlich erschien. Ich hab die Kugel ganz einfach nicht direkt am Rand replaced sondern um den Abstand, die sie vom Rand entfernt wäre, wenn sie durch die Bande hinduchfliegen würde. Das bewirkt allerdings ein unschönes zittern, wenn sie auf dem Boden liegt. Mal sehen ob ich da noch was machen kann... Auf jeden fall wäre ich ohne deine Hilfe nie auf die Idee gekommen! Danke noch einmal.
Die neue Version werde ich natürlich auch hochladen.
cuejo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 142

Win XP
Delphi 7 Personal und 2005 PE
BeitragVerfasst: Di 04.04.06 22:08 
Ach und hier noch mal die überarbeitete Version. Vielleicht hat ja jemand eine Idee wie ich das zittern wegbekomme.

ausblenden volle Höhe 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:
[...]

type
  ball=class
    procedure update;
    procedure collide;
    procedure follow(x,y:integer);
    function frame:trect;
    function wall(value:real):real;
    function air(value:real):real;
  public
    r, //Radius
    bottom,top,left,right:integer;  // Spielfeldbegrenzungen
    vert,hori, // Geschwindigkeiten
    posx,posy, // Position
    g,t:real;  // Gravitationsbeschleunigung, Zeit
    airfri,wallfri:boolean;  // Reibung an/aus
end;

implementation

procedure ball.update;   // neue Position neue Geschwindigkeiten
var v,h:real;  // Hilfsvariablen für zurückgelegten Weg
begin
vert:=vert + g*t;   // überlagerte Vertikalgeschwindigkeit
v:=vert*t*1000;     // Weg=Geschwindigkeit*Zeit (1000 px = 1 m)
posy:=posy+v;
vert:=air(vert);    // Luftwiderstand

// analog die Horizontalgeschwindigkeit
h:=hori*t*1000;
posx:=posx+h;
hori:=air(hori);
end;

procedure ball.collide;   // Kollisionsabfrage
begin
if (posy+r)>=(bottom-1then
  begin
    vert:=-1*vert;     // Invertierung der Richtung
    posy:=posy-2*((posy+r)-bottom)-2;//damit Kugel immer innerhalb des Shapes bleibt
    vert:=wall(vert);  // Reibung bei Kollision an Bande
  end;
if (posy-r)<=(top+1then
  begin
    vert:=-1*vert;
    posy:=posy+2*(top-(posy-r))+2;
    vert:=wall(vert);
  end;
if (posx+r)>=(right-1then
  begin
    hori:=-1*hori;
    posx:=posx-2*((posx+r)-right)-2;
    hori:=wall(hori);
  end;
if (posx-r)<=(left+1then
  begin
    hori:=-1*hori;
    posx:=posx+2*(left-(posx-r))+2;
    hori:=wall(hori);
  end;
end;

function ball.frame:trect; // TRect für die Zeichnung der Kugel mit Canvas
begin
result:=bounds(trunc(posx)-r,trunc(posy)-r,2*r,2*r);
end;

function ball.wall(value:real):real; // Reibung an Bande
begin
if wallfri then result:=value*0.8 else result:=value;
end;

function ball.air(value:real):real;  // Luftwiderstand
begin
if airfri then result:=value*0.992 else result:=value;
end;

procedure ball.follow(x,y:integer);  // Kugel folgt Maus
begin
posx:=x;
posy:=y;
end;
cuejo Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 142

Win XP
Delphi 7 Personal und 2005 PE
BeitragVerfasst: Do 06.04.06 21:00 
Hi,
das zittern ist jetzt geschichte:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
if (posy+r)>=(bottom-1then
  begin
    vert:=-1*vert;     // Invertierung der Richtung
    posy:=trunc(posy)-2*trunc((posy+r)-(bottom-1));//damit Kugel immer innerhalb des Shapes bleibt
    vert:=wall(vert);  // Reibung bei Kollision an Bande
  end;

Mit der überarbeiten Kollisionsabfrage kommt es jetzt nur noch sehr selten zu dem unschönen zittern und wenn, dann ist es längst nicht so extrem wie vorher...