Files
pygame-physics-opus5.5/docs/PHYSIK_EINSTIEG.md
T
2026-09-24 15:45:31 +02:00

467 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Physik für Einsteiger – wie die Sandbox funktioniert
Dieses Dokument erklärt die Physik der Sandbox Schritt für Schritt und ohne Vorwissen.
Jeder Abschnitt sagt, **was** physikalisch passiert, **warum** das Programm es so macht und
**wo** es im Code steht. Die kompakte Formelsammlung für Fortgeschrittene ist
[PHYSICS.md](PHYSICS.md).
## Inhalt
1. [Die Grundidee: Die Welt als Daumenkino](#1-die-grundidee-die-welt-als-daumenkino)
2. [Vektoren: Pfeile mit Richtung und Länge](#2-vektoren-pfeile-mit-richtung-und-länge)
3. [Position, Geschwindigkeit, Beschleunigung](#3-position-geschwindigkeit-beschleunigung)
4. [Masse und Trägheit](#4-masse-und-trägheit)
5. [Der Simulationsschritt: Euler-Verfahren](#5-der-simulationsschritt-euler-verfahren)
6. [Warum ein fester Zeitschritt?](#6-warum-ein-fester-zeitschritt)
7. [Kollisionen erkennen](#7-kollisionen-erkennen)
8. [Kollisionen auflösen: Impulse](#8-kollisionen-auflösen-impulse)
9. [Elastizität: Wie stark etwas abprallt](#9-elastizität-wie-stark-etwas-abprallt)
10. [Reibung](#10-reibung)
11. [Drehung und Rollen](#11-drehung-und-rollen)
12. [Viele Kontakte gleichzeitig](#12-viele-kontakte-gleichzeitig)
13. [Wenn Körper ineinanderstecken: Positionskorrektur](#13-wenn-körper-ineinanderstecken-positionskorrektur)
14. [Ruhe: Warum nichts zittert](#14-ruhe-warum-nichts-zittert)
15. [Tunneln: Warum nichts durch Wände fliegt](#15-tunneln-warum-nichts-durch-wände-fliegt)
16. [Ziehen und Werfen mit der Maus](#16-ziehen-und-werfen-mit-der-maus)
17. [Alles zusammen: ein kompletter Schritt](#17-alles-zusammen-ein-kompletter-schritt)
18. [Zum Ausprobieren](#18-zum-ausprobieren)
---
## 1. Die Grundidee: Die Welt als Daumenkino
Ein Computer kann Bewegung nicht "fließend" berechnen. Er macht es wie ein Daumenkino: Er
berechnet viele Einzelbilder dicht hintereinander. Die Sandbox berechnet die Welt
**120-mal pro Sekunde**. Zwischen zwei Bildern liegt also die Zeit
```text
dt = 1/120 s ≈ 0,0083 s ("dt" = Delta t = kleines Zeitstück)
```
In jedem dieser Schritte fragt das Programm für jeden Körper:
1. Welche Kräfte wirken? (Schwerkraft)
2. Wie ändert sich dadurch die Geschwindigkeit?
3. Wie ändert sich dadurch die Position?
4. Stößt der Körper irgendwo an? Wenn ja: Wie reagieren beide Körper?
Mehr ist Physik-Simulation im Kern nicht. Der Rest dieses Dokuments erklärt die einzelnen
Punkte genauer.
## 2. Vektoren: Pfeile mit Richtung und Länge
Viele Größen in der Physik haben nicht nur einen Betrag, sondern auch eine **Richtung**.
"Der Ball fliegt 5 m/s schnell" reicht nicht – man muss wissen, *wohin*. Deshalb rechnet man
mit **Vektoren**: Pfeilen mit x- und y-Anteil.
```text
v = (3, 4) bedeutet: 3 nach rechts, 4 nach unten (y zeigt wie am Bildschirm nach unten)
```
Die wichtigsten Operationen (alle in [`physics/vector.py`](../src/sandbox/physics/vector.py)):
| Operation | Rechnung | Bedeutung |
|---|---|---|
| Addition `a + b` | (a.x+b.x, a.y+b.y) | Pfeile aneinanderhängen: "erst 3 nach rechts, dann 2 nach oben" |
| Skalierung `a * 2` | (2·a.x, 2·a.y) | Pfeil doppelt so lang, gleiche Richtung |
| Länge `a.length()` | √(x² + y²) | Satz des Pythagoras: wie lang ist der Pfeil? (3,4) hat Länge 5 |
| Normalisieren `a.normalized()` | a / Länge | Pfeil auf Länge 1 bringen – übrig bleibt nur die *Richtung* |
| Skalarprodukt `a.dot(b)` | a.x·b.x + a.y·b.y | "Wie viel von a zeigt in Richtung b?" |
| Kreuzprodukt `a.cross(b)` | a.x·b.y − a.y·b.x | "Wie stark dreht a um b herum?" (für Drehungen) |
**Das Skalarprodukt ist der wichtigste Trick der ganzen Engine.** Ist `n` ein Pfeil der
Länge 1 (z. B. "senkrecht vom Boden weg"), dann gibt `v.dot(n)` an, wie schnell sich etwas
*in Richtung n* bewegt. Ein Ball mit `v = (5, 8)` (schräg nach unten) und der
Boden-Richtung `n = (0, 1)` (nach unten) liefert `v·n = 8`: Er fällt mit 8 Einheiten pro
Sekunde auf den Boden zu. Die 5 nach rechts spielen für den Aufprall keine Rolle.
## 3. Position, Geschwindigkeit, Beschleunigung
Drei Größen hängen wie eine Kette zusammen:
| Größe | Einheit hier | Was sie ändert |
|---|---|---|
| Position x | px | – |
| Geschwindigkeit v | px/s | die Position: "so viele Pixel pro Sekunde" |
| Beschleunigung a | px/s² | die Geschwindigkeit: "pro Sekunde wird man so viel schneller" |
**Schwerkraft** ist eine Beschleunigung. Auf der Erde sind das 9,81 m/s². Die Sandbox rechnet
in Pixeln und nimmt an, dass 100 px einem Meter entsprechen, also:
```text
g = (0, 980) px/s² → pro Sekunde wird ein fallender Körper 980 px/s schneller
```
Beispiel: Ein Ball, der aus der Ruhe fällt, ist nach 0,5 s 490 px/s schnell und hat
½·g·t² = ½ · 980 · 0,25 ≈ 122 px zurückgelegt. Genau das prüft der Test
`test_free_fall_matches_half_g_t_squared`.
## 4. Masse und Trägheit
**Masse** sagt, wie schwer es ist, einen Körper zu beschleunigen. Ein Stoß, der einen
Tischtennisball wegschießt, bewegt eine Bowlingkugel kaum. In der Sandbox gilt:
```text
Masse = Fläche × Dichte
Kreis: Fläche = π · r²
Rechteck: Fläche = Breite · Höhe
```
Ein doppelt so großer Kreis (doppelter Radius) hat die vierfache Fläche und damit die
vierfache Masse.
**Trägheitsmoment** ist dasselbe Prinzip für Drehungen: Wie schwer ist es, einen Körper in
Drehung zu versetzen? Für eine Scheibe gilt `I = ½ · m · r²`. Masse, die weit außen sitzt,
dreht sich schwerer an. Deshalb steht dort das r².
Rechtecke drehen sich in dieser Sandbox **nie** (die Aufgabe verlangt achsenparallele
Rechtecke). Programmtechnisch bekommen sie ein unendliches Trägheitsmoment. Dann kann kein
noch so großer Stoß sie drehen.
**Trick mit dem Kehrwert:** Der Code speichert zusätzlich `inv_mass = 1/Masse`. Wände
bekommen `inv_mass = 0`, das heißt "unendlich schwer". So braucht man für Wände keine
Sonderfälle: Die Rechnung "Geschwindigkeitsänderung = Stoß · inv_mass" ergibt bei einer Wand
automatisch 0 – sie bewegt sich nicht.
## 5. Der Simulationsschritt: Euler-Verfahren
Wie kommt man von "Beschleunigung" zu "neuer Position"? Mit dem einfachsten Verfahren der
Welt, benannt nach Leonhard Euler:
```text
neue Geschwindigkeit = alte Geschwindigkeit + Beschleunigung · dt
neue Position = alte Position + neue Geschwindigkeit · dt
```
Beispiel mit dt = 1/120 s für einen fallenden Ball:
| Schritt | Geschwindigkeit (px/s) | Position y (px) |
|---|---|---|
| 0 | 0 | 100,00 |
| 1 | 0 + 980/120 = 8,17 | 100 + 8,17/120 = 100,07 |
| 2 | 16,33 | 100,20 |
| 3 | 24,50 | 100,41 |
**Wichtiges Detail:** In der zweiten Zeile steht die *neue* Geschwindigkeit. Das heißt
"semi-impliziter Euler". Die naheliegende Variante mit der *alten* Geschwindigkeit hat einen
fiesen Fehler: Sie erzeugt bei Schwingungen und Pendelbewegungen langsam immer mehr
Energie, bis die Simulation "explodiert". Die hier verwendete Variante kostet genauso wenig,
bleibt aber stabil. Im Code: `World._integrate_velocities` und `World._integrate_positions`
in [`physics/world.py`](../src/sandbox/physics/world.py).
## 6. Warum ein fester Zeitschritt?
Das Bild wird z. B. 60-mal pro Sekunde gezeichnet, aber nicht exakt regelmäßig. Mal dauert
ein Bild 15 ms, mal 18 ms, und wenn Windows gerade etwas anderes tut, auch einmal 200 ms.
Würde die Physik diese schwankende Zeit als dt benutzen, gäbe es zwei Probleme:
- Die Simulation liefe auf jedem Rechner etwas anders.
- Bei einem Ruckler von 200 ms würde ein Ball in *einem* Schritt 200 px weit springen und
dabei vielleicht durch einen anderen Körper hindurch.
Deshalb gibt es einen **Akkumulator** ("Zeitsparschwein") in
[`physics/timestep.py`](../src/sandbox/physics/timestep.py): Die echte Zeit wird
gesammelt und in festen Portionen von 1/120 s an die Physik ausgezahlt. Bei 60 Bildern pro
Sekunde sind das meist genau 2 Physikschritte pro Bild. Bleibt ein Rest, wartet er auf das
nächste Bild.
Beim Zeichnen wird zusätzlich zwischen dem vorletzten und dem letzten Physikzustand
**interpoliert** (Zwischenwert gebildet). Dadurch wirkt die Bewegung flüssig, auch wenn
Physik und Bildschirm nicht im selben Takt laufen.
## 7. Kollisionen erkennen
Nach jedem Schritt muss das Programm herausfinden, welche Körper sich überlappen. Für jede
Berührung erzeugt es einen **Kontakt** mit drei Angaben:
- **Normale n**: ein Pfeil der Länge 1, der von Körper A zu Körper B zeigt. In diese
Richtung werden die Körper auseinandergedrückt.
- **Eindringtiefe**: wie weit sich die Körper überlappen.
- **Kontaktpunkt**: wo sie sich berühren.
### Kreis gegen Kreis – der einfachste Fall
Zwei Kreise berühren sich, wenn der Abstand ihrer Mittelpunkte kleiner ist als die Summe
der Radien:
```text
A ●───────● B Abstand d = 30, Radien 20 + 20 = 40
╲___╱ ╲___╱ → Überlappung 40 − 30 = 10 px
Normale n = (B − A) / d → Pfeil von A nach B, Länge 1
```
### Kreis gegen Rechteck
Man sucht den Punkt des Rechtecks, der dem Kreismittelpunkt am nächsten liegt. Dafür
"klemmt" man die Kreismitte in das Rechteck: Liegt sie links vom Rechteck, nimmt man den
linken Rand, liegt sie darüber, den oberen Rand usw. Ist dieser nächste Punkt näher als der
Radius, berühren sich die beiden.
### Rechteck gegen Rechteck
Zwei achsenparallele Rechtecke überlappen genau dann, wenn sie sich **sowohl** in
x-Richtung **als auch** in y-Richtung überlappen. Getrennt werden sie entlang der Richtung
mit der kleineren Überlappung – das ist der kürzeste Weg nach draußen.
### Wände
Boden, Decke und Seiten sind "Halbebenen": Alles hinter der Wandlinie gilt als Wand. Der
Vorteil: Auch ein Körper, der in einem Schritt komplett hinter die Wand gesprungen ist, wird
noch erkannt und zurückgeschoben.
### Schneller suchen: Broadphase
Bei 150 Körpern gibt es über 11 000 mögliche Paare – zu viele, um jedes genau zu prüfen.
Deshalb sortiert [`physics/broadphase.py`](../src/sandbox/physics/broadphase.py) alle Körper
von links nach rechts. Zwei Körper, die in x-Richtung weit auseinanderliegen, können sich
nicht berühren und werden gar nicht erst genau geprüft ("Sweep-and-Prune").
## 8. Kollisionen auflösen: Impulse
Jetzt der wichtigste Teil. Zwei Körper berühren sich und bewegen sich aufeinander zu. Was
passiert?
### Impuls – "Wucht"
Der **Impuls** eines Körpers ist Masse mal Geschwindigkeit: `p = m · v`. Ein schwerer,
langsamer Lkw und ein leichter, schneller Ball können gleich viel "Wucht" haben.
Ein **Stoß** (ebenfalls Impuls genannt, Formelzeichen P) ist eine sehr kurze, starke Kraft,
etwa beim Aufprall. Er ändert die Geschwindigkeit sofort:
```text
Geschwindigkeitsänderung = Stoß / Masse
```
### Actio = Reactio
Newtons drittes Gesetz: Wenn A gegen B drückt, drückt B genauso stark gegen A zurück. Der
Stoß P wirkt also auf B in Richtung der Normalen und auf A genau entgegengesetzt:
```text
v_A neu = v_A − P · n / m_A
v_B neu = v_B + P · n / m_B
```
Dadurch bleibt der Gesamtimpuls erhalten. Was A an Wucht verliert, gewinnt B.
### Wie groß muss der Stoß sein?
Man schaut, wie schnell sich die beiden Körper aufeinander zu bewegen – entlang der
Normalen, also mit dem Skalarprodukt aus Abschnitt 2:
```text
v_n = (v_B − v_A) · n (negativ = sie nähern sich an)
```
Dann sucht man den Stoß P, der diese Annäherung genau stoppt (bzw. umkehrt, siehe nächster
Abschnitt). Weil der Stoß beide Körper beeinflusst, hängt die nötige Stärke von beiden
Massen ab:
```text
P = −(1 + e) · v_n / (1/m_A + 1/m_B)
```
Den Nenner `1/(1/m_A + 1/m_B)` nennt man **effektive Masse**. Bei einer Wand
(`1/m = 0`) ist sie einfach die Masse des Körpers. Bei zwei gleich schweren Körpern ist sie
halb so groß, weil der andere Körper mit ausweicht.
**Rechenbeispiel:** Eine Kugel (m = 1) fliegt mit 10 px/s senkrecht auf den Boden, der
völlig unelastisch ist (e = 0). Dann ist v_n = −10, die effektive Masse ist 1 (Wand), und
der Stoß ist P = −(1 + 0) · (−10) / 1 = 10. Die neue Geschwindigkeit ist 10 − 10 = 0: Die
Kugel bleibt liegen.
Im Code: [`physics/solver.py`](../src/sandbox/physics/solver.py), die ausführliche
Herleitung steht am Dateianfang.
## 9. Elastizität: Wie stark etwas abprallt
Die **Stoßzahl e** (in der Sidebar "Elastizität") sagt, wie viel der Geschwindigkeit nach
dem Aufprall übrig bleibt:
| e | Verhalten | Beispiel |
|---|---|---|
| 0 | bleibt kleben, prallt nicht ab | Knetmasse |
| 0,5 | prallt mit halber Geschwindigkeit ab | Holzklotz |
| 0,8 | springt kräftig zurück | Gummiball |
| 1 | prallt ohne Verlust ab | ideale Superkugel |
Daraus folgt eine schöne Regel: Ein Ball springt nach dem Aufprall auf **e²** seiner
Fallhöhe. Mit e = 0,8 sind das 0,64, also 64 % der Höhe. (Die Energie hängt vom Quadrat der
Geschwindigkeit ab, deshalb das Quadrat.)
Zwei praktische Sonderregeln:
- **Unter 50 px/s kein Abprallen.** Ein Körper, der auf dem Boden liegt, "fällt" wegen der
Schwerkraft in jedem Schritt minimal in den Boden. Würde er davon jedes Mal ein winziges
bisschen abprallen, würde er ständig zittern.
- **Gehaltene Objekte prallen nicht ab.** Die "Hand" fängt den Stoß ab – sonst würde ein
gegen eine Wand gedrücktes Objekt dauernd zurückfedern.
## 10. Reibung
Reibung bremst, wenn zwei Flächen aneinander entlangrutschen. Sie wirkt **quer zur
Normalen**, also entlang der Berührfläche (dieser Richtung sagt man "Tangente").
Das Coulomb-Gesetz: Die Reibung kann höchstens so stark sein wie der Andruck mal dem
**Reibungskoeffizienten μ**:
```text
Reibung ≤ μ · Andruck
```
- Ist wenig Schwung da, reicht die Reibung, um die Bewegung ganz zu stoppen: **Haftreibung**.
Eine Kiste bleibt auf dem Boden stehen.
- Ist mehr Schwung da, rutscht die Kiste trotz Reibung weiter und wird gleichmäßig langsamer:
**Gleitreibung**. Sie bremst mit `μ · g`. Eine Kiste mit 300 px/s und μ ≈ 0,5 rutscht
etwa 90 px weit.
Der Test `test_sliding_box_decelerates_with_coulomb_friction` prüft genau diesen Bremsweg.
## 11. Drehung und Rollen
Kreise können sich drehen. Die **Winkelgeschwindigkeit ω** (Omega) sagt, wie schnell, in
Radiant pro Sekunde (eine volle Umdrehung sind 2π ≈ 6,28 rad).
Warum dreht sich ein Kreis überhaupt? Die Reibung greift nicht in der Mitte an, sondern
unten am Kontaktpunkt. Ein Stoß, der nicht durch die Mitte geht, erzeugt ein
**Drehmoment**, ähnlich wie ein Schraubenschlüssel. Genau dafür braucht man das
Kreuzprodukt aus Abschnitt 2: `Hebelarm × Kraft` ergibt, wie stark gedreht wird.
**Rollen** heißt: Der Kontaktpunkt steht still, der Ball dreht sich genau passend zu seiner
Geschwindigkeit:
```text
v = ω · r
```
Ein Experiment, das die Sandbox exakt nachrechnet: Stößt man eine Scheibe an, sodass sie
ohne Drehung über den Boden rutscht, bremst die Reibung sie und bringt sie gleichzeitig zum
Drehen. Irgendwann rollt sie – und dann hat sie **genau ⅔ ihrer Startgeschwindigkeit**,
unabhängig davon, wie stark die Reibung ist. Den kleinen Strich in jedem Kreis zeichnet das
Programm, damit man die Drehung sehen kann.
**Rollwiderstand:** Ein perfekt rollender Ball würde nie anhalten, denn reines Rollen
verbraucht keine Reibungsenergie. In der Realität verformen sich Ball und Boden ein wenig,
das bremst. Die Sandbox bildet das mit einem kleinen Gegen-Drehmoment nach. Ohne diese
Bremse würde ein Rechteck, das auf einem Kreis liegt, darauf wie auf einer Rolle
davonfahren.
## 12. Viele Kontakte gleichzeitig
Liegt eine Kiste in einem Stapel, hat sie Kontakte nach oben und nach unten. Jeder Stoß an
einem Kontakt verändert die Situation an den anderen Kontakten. Man kann sie nicht einzeln
nacheinander "fertig" rechnen.
Die Lösung heißt **Sequential Impulses** und funktioniert wie beim Aufräumen eines Zimmers
in mehreren Runden: Man geht alle Kontakte durch und korrigiert jeden ein bisschen. Dann
geht man sie noch einmal durch, weil die Korrekturen sich gegenseitig beeinflusst haben.
Nach **8 Runden** pro Schritt ist das Ergebnis sehr genau.
Zwei Tricks machen das Verfahren stabil:
- **Kontakte können nur drücken, nicht ziehen.** Die Summe aller Stöße an einem Kontakt darf
nie negativ werden, sonst würden Körper aneinanderkleben.
- **Warm-Starting:** Ein ruhender Stapel sieht im nächsten Schritt fast genauso aus wie
jetzt. Deshalb startet man mit den Stößen des letzten Schritts statt bei null und ist
sofort fast fertig.
## 13. Wenn Körper ineinanderstecken: Positionskorrektur
Weil nur in festen Schritten gerechnet wird, landet ein fallender Ball meist schon ein
kleines Stück *im* Boden, bevor der Aufprall erkannt wird. Die Stöße korrigieren nur die
Geschwindigkeit, nicht die Überlappung. Ohne Gegenmaßnahme würden Stapel langsam
ineinandersinken.
Deshalb schiebt der Solver die Körper nach jedem Schritt ein Stück auseinander – direkt über
ihre Position, **ohne** ihnen Geschwindigkeit zu geben. Würde man sie stattdessen
"wegschubsen", würden stark überlappende Körper (zum Beispiel, wenn man ein Objekt mitten
in einen Haufen setzt) davonfliegen.
Eine winzige Überlappung von 0,5 px (der "Slop") lässt man absichtlich stehen. So bleibt der
Kontakt im nächsten Schritt sicher bestehen, statt ständig zwischen "berührt" und "berührt
nicht" zu wechseln – auch das wäre sichtbares Zittern.
## 14. Ruhe: Warum nichts zittert
Auch mit allen Tricks bleiben winzige Restbewegungen übrig. Die Lösung ist der
**Schlafmodus** ([`physics/sleep.py`](../src/sandbox/physics/sleep.py)):
1. Jeder Körper zählt, wie lange er schon fast stillsteht (langsamer als 4 px/s).
2. Körper, die sich berühren, bilden eine Gruppe (eine "Insel").
3. Hat **die ganze Insel** eine halbe Sekunde lang fast stillgestanden, wird sie
eingefroren: Sie bewegt sich nicht mehr und kostet keine Rechenzeit.
4. Stößt etwas gegen einen schlafenden Körper, wacht **die ganze Insel** wieder auf.
Warum die ganze Insel? Weckt man nur die unterste Kiste eines Stapels und zieht sie weg,
würde die Kiste darüber sonst in der Luft weiterschlafen. Genau diesen Fehler hat das
unabhängige Review gefunden, und genau so wurde er behoben.
## 15. Tunneln: Warum nichts durch Wände fliegt
Weil nur in Schritten gerechnet wird, könnte ein sehr schneller, kleiner Körper in einem
Schritt komplett über einen anderen hinwegspringen, ohne dass je eine Überlappung auftritt.
Das nennt man **Tunneln**.
Zwei Gegenmaßnahmen:
- **Wände sind unendlich dick** (Halbebenen). Ein Körper kann nicht "hinter" sie gelangen,
ohne erkannt zu werden.
- **Geschwindigkeitsgrenze:** Die kleinsten Körper haben einen Radius von 12 px. Solange
sich zwei davon pro Schritt zusammen weniger als ihre Radien-Summe bewegen, können ihre
Mittelpunkte nicht aneinander vorbeispringen. Daraus ergibt sich eine Höchstgeschwindigkeit
von etwa 1300 px/s, auf die jeder Körper begrenzt wird.
## 16. Ziehen und Werfen mit der Maus
Würde man ein gezogenes Objekt einfach an die Mausposition "teleportieren", könnte man es
durch Wände und andere Objekte hindurchschieben. Stattdessen bekommt es in jedem
Physikschritt die **Geschwindigkeit**, die es zur Maus bringt:
```text
v = (Mausziel − Position) · 60
```
Danach behandelt die Physik es wie jeden anderen Körper. Es schiebt leichte Objekte weg und
wird von Wänden gestoppt.
**Werfen:** Beim Loslassen bekommt das Objekt die Geschwindigkeit der Maus. Die Maus
zittert aber und liefert unregelmäßige Werte. Deshalb wird geglättet
([`interaction/velocity_tracker.py`](../src/sandbox/interaction/velocity_tracker.py)):
erst ein Mittelwert über die letzten 0,1 Sekunden, dann noch eine weiche Glättung. Hält man
die Maus vor dem Loslassen still, sinkt die Geschwindigkeit auf fast null – das Objekt fällt
dann einfach herunter.
## 17. Alles zusammen: ein kompletter Schritt
So sieht ein einzelner Physikschritt (1/120 s) in `World.step` aus:
1. Zustand merken (für das flüssige Zeichnen und das Abprallen).
2. Schwerkraft anwenden: `v = v + g · dt`.
3. Mögliche Kollisionspaare grob vorsortieren (Broadphase).
4. Kontakte genau bestimmen; schlafende Inseln wecken, die berührt werden.
5. Kontakte mit den Wänden bestimmen.
6. Stöße berechnen – 8 Runden lang, mit Abprallen, Reibung und Rollwiderstand.
7. Geschwindigkeit begrenzen (gegen Tunneln).
8. Bewegen: `x = x + v · dt`.
9. Restliche Überlappungen durch Verschieben abbauen.
10. Notbremse: Niemand steckt tiefer als 12 px in einer Wand.
11. Schlafmodus aktualisieren.
## 18. Zum Ausprobieren
Am besten versteht man Physik, indem man damit spielt:
- Setze die **Elastizität auf 1** und lass einen Ball fallen: Er springt immer wieder genau
auf seine Starthöhe. Bei 0,5 wird jeder Sprung deutlich niedriger.
- Wirf einen kleinen Kreis mit **Dichte 0,2** gegen einen großen mit **Dichte 5**: Der
leichte prallt ab, der schwere bewegt sich kaum (Impulserhaltung).
- Baue einen Turm aus Rechtecken und ziehe die unterste Kiste weg: Der Rest fällt.
- Schalte mit **G** die Schwerkraft aus und wirf Objekte: Sie fliegen geradeaus, bis sie
etwas treffen (Newtons erstes Gesetz: Trägheit).
- Drücke **F1**: Gelbe Punkte zeigen die Kontaktpunkte, rote Striche die Normalen, graue
Umrandungen schlafende Körper.
- Ändere Werte in [`config.py`](../src/sandbox/config.py), zum Beispiel `gravity` oder
`rolling_resistance`, und beobachte die Wirkung.