# 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.