[#_lesson_24_physics] == Lektion 24: Physik image::learn/24-physics.png[Die fertige Lektion, 480] Bis jetzt hat sich alles, was sich in deinen Spielen bewegt hat, bewegt, weil du gerechnet hast. Eine Zahl ist in jedem Frame um zwei gestiegen; wenn sie über einen Rand hinaus war, hast du sie umgedreht. Das ist der ehrliche Weg, ein Spiel in Bewegung zu bringen, und die meisten Spiele bestehen daraus. Diese Lektion gibt die Rechnerei an jemand anderen ab. Eine *Physik-Engine* simuliert die Welt -- Schwerkraft, Fallen, Rutschen, Stapeln, Abprallen, Rollen und Dinge, die zusammenstoßen -- und bewegt deine Knoten für dich. Am Ende hast du eine Wanne voller Kisten und eine Kanone, die sie umwirft. Im letzten Abschnitt geht es darum, wann du nichts davon tun solltest, und das ist der Teil, den du zweimal lesen solltest. [#_what_it_does_and_what_it_does_not] === Was sie tut und was nicht Gib einem Knoten einen *Körper*, und die Simulation übernimmt ihn. Von da an fällt er durch die Schwerkraft, landet auf dem, was unter ihm ist, kippt, wenn er außerhalb der Mitte getroffen wird, liegt ruhig in einem Stapel, rutscht aus, bis er steht, und der Knoten folgt, wohin die Simulation ihn auch setzt. Du setzt seine Position nicht mehr selbst. Das ist der Handel: Du gibst Position und Drehung dieses Knotens ab und bekommst dafür Verhalten, das du nicht schreiben musstest. Was sie nicht tut, ist irgendetwas, das dein Spiel betrifft. Sie weiß nicht, was eine Punktzahl ist, oder dass die rote Kiste die ist, die Punkte bringt, oder dass das Herunterfallen von der Kante ein Leben kostet. Sie animiert keine gehende Figur, sie weiß nicht, wie sich ein Sprung anfühlen soll, und sie wird ein Objekt nicht so bewegen, dass du es genau vorhersagen kannst. Physik ist eine Maschine, die glaubwürdiges Chaos erzeugt. Die Einheiten sind Meter, Kilogramm und Sekunden. Die Schwerkraft ist `0, -9.81, 0`, wenn du nichts anderes sagst, und das ist die der Erde, nach unten, in Metern pro Sekunde zum Quadrat. Eine Kiste, die eine Einheit breit ist, ist einen Meter breit, und sie fällt auch so. Wenn deine Szene in einem anderen Maßstab gebaut ist, sieht alles aus, als fiele es durch Sirup oder durch einen Zeichentrickfilm, und die Lösung ist, in Metern zu bauen. [#_a_floor_and_one_crate] === Ein Boden und eine Kiste Leg einen Ordner an, leg `crates.singe` an und tipp das ein. Es ist eine Szene, eine Platte, eine Kiste über der Platte und drei Zeilen Physik. [source,lua] ---- sceneEnable(true) sceneSetBackground(16, 18, 26) sceneSetAmbient(40, 42, 50) local stone = materialNew() materialSetColor(stone, 110, 110, 120) materialSetRoughness(stone, 0.9) local wood = materialNew() materialSetColor(wood, 190, 140, 80) materialSetRoughness(wood, 0.8) local floor = nodeNew() nodeSetMesh(floor, meshBox(16, 0.4, 16), stone) nodeSetPosition(floor, 0, -0.2, 0) bodyNew(floor, BODY_STATIC, SHAPE_BOX, 16, 0.4, 16) local crate = nodeNew() nodeSetMesh(crate, meshBox(0.8, 0.8, 0.8), wood) nodeSetPosition(crate, 0.3, 4, 0) nodeSetRotation(crate, 20, 0, 15) bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.8, 0.8, 0.8) bodySetMass(crate, 4) local sun = lightNew(LIGHT_DIRECTIONAL) nodeSetPosition(sun, -5, 9, 6) nodeLookAt(sun, 0, 0, 0) lightSetIntensity(sun, 1.7) lightSetShadow(sun, true) local camera = nodeNew() nodeSetPosition(camera, 0, 4, 11) nodeLookAt(camera, 0, 1, 0) cameraSet(camera) function onOverlayUpdate() return OVERLAY_UPDATED end ---- Starte es mit `Singe -R crates`. Die Kiste fällt aus vier Metern Höhe, landet auf einer Ecke, weil du sie gekippt hast, schaukelt auf eine Seite und bleibt liegen. Drück *F5*, um es noch einmal zu starten und es dir noch einmal anzusehen. Nichts in `onOverlayUpdate` bewegt irgendetwas. Die beiden Aufrufe von `bodyNew` haben alles erledigt. [#_what_just_happened_20] === Was gerade passiert ist [source,lua] ---- bodyNew(floor, BODY_STATIC, SHAPE_BOX, 16, 0.4, 16) ---- `bodyNew` gibt einem Knoten einen Körper. Es nimmt den Knoten, welche Art von Körper es ist, welche Form die Simulation dafür benutzen soll, und die Größen dieser Form. Es gibt drei Arten, und die richtige auszuwählen ist der größte Teil der Kunst. * `BODY_STATIC` bewegt sich nie. Er wird dort hingesetzt, wo der Knoten ist, wenn du den Körper erzeugst, und dort bleibt er für immer. Böden, Wände, der Erdboden, der Automat, das Level. Den Knoten danach zu bewegen bewegt den Körper nicht, und das ist eine Falle, die man sich merken sollte: statisch heißt statisch. * `BODY_DYNAMIC` wird von der Simulation bewegt. Er fällt, prallt ab, rutscht, wird geschubst und zieht seinen Knoten mit. Das ist eine Kiste, ein Fass, ein Ball, ein Trümmerstück. * `BODY_KINEMATIC` geht dorthin, wo *du* seinen Knoten hinsetzt, und schiebt dynamische Körper aus dem Weg, ohne je selbst geschubst zu werden. Eine bewegliche Plattform, ein Schläger, eine Tür auf einer Schiene, die schwingende Faust einer Figur. Er ist die Brücke zwischen deiner Rechnerei und der Simulation: Du steuerst ihn, er steuert sie. Die Form ist nicht das Mesh. Sie ist ein grober Ersatz, den die Simulation benutzt, weil sie Fragen über einen Quader viel schneller beantworten kann als über neunhundert Dreiecke. `SHAPE_BOX` nimmt eine Breite, eine Höhe und eine Tiefe; `SHAPE_SPHERE` nimmt einen Radius; `SHAPE_CAPSULE` und `SHAPE_CYLINDER` nehmen einen Radius und eine Höhe. Es gibt `SHAPE_HULL`, das das Mesh unter dem Knoten wie mit Schrumpffolie umhüllt -- die übliche Antwort für ein Modell -- und `SHAPE_MESH`, die exakten Dreiecke, die statische Kulissen benutzen dürfen und ein fallendes Objekt nicht. Die Größen richtig hinzubekommen ist deine Sache. Nichts prüft, ob der Körper, den du beschrieben hast, zu dem Mesh passt, das du gezeichnet hast, und eine Kiste, die mit `0.8` gezeichnet und mit einem Körper von `2` erzeugt wurde, schwebt über dem Boden und wirft Dinge aus der Entfernung um. Wenn sich etwas verhält, als hätte es eine andere Größe, dann hat es die auch. [source,lua] ---- bodySetMass(crate, 4) ---- Die Masse eines dynamischen Körpers ergibt sich aus seiner Form bei der Dichte von Wasser, und das macht Dinge viel schwerer, als du erwartest: Ein Quader von der Größe einer Tür wiegt ungefähr hundertvierzig Kilogramm, und nichts, was du darauf werfen kannst, bewegt ihn. Setz die Masse von allem, was sich leicht anfühlen soll. Vier Kilogramm sind eine Kiste, die du hochheben könntest. Die Masse ist wichtig, weil Kräfte durch sie geteilt werden. Derselbe Stoß bewegt ein leichtes Ding weiter, und es braucht einen Impuls von Masse mal Geschwindigkeit, um etwas mit dieser Geschwindigkeit in Bewegung zu setzen. Stimm zuerst die Masse ab und danach die Kräfte, sonst drehst du dich im Kreis. [#_making_things_move] === Dinge in Bewegung bringen Drei Aufrufe schubsen einen dynamischen Körper herum, und sie sind nicht austauschbar. `bodySetVelocity(node, x, y, z)` sagt „du bewegst dich jetzt mit dieser Geschwindigkeit“ und ersetzt, was er vorher getan hat. Das ist ein Aufschlag, ein Abschuss, ein Erscheinen mit schon etwas Tempo. `bodyApplyImpulse(node, ix, iy, iz)` ist ein Treffer: ein Tritt, eine Explosion, ein Schläger, der einen Ball trifft. Er kommt zu dem dazu, was der Körper schon getan hat. Gib ihm als drei weitere Zahlen einen Punkt in der Welt, und er trifft *dort* statt in der Mitte, sodass eine Kiste, die oben getroffen wird, umkippt, statt zu rutschen. `bodyApplyForce(node, fx, fy, fz)` ist ein Schub, der anhält: eine Rakete, ein Ventilator, ein Förderband. Eine Kraft, die einen Frame lang wirkt, tut fast nichts, und das überrascht Leute, die einen Impuls erwartet haben. Die Schwerkraft selbst ist `physicsSetGravity(x, y, z)`, und sie gilt für die ganze Welt. Der Mond ist `physicsSetGravity(0, -1.62, 0)`, der Weltraum ist alles Nullen, und ein Spiel mit seitlicher Schwerkraft ist eine Zeile entfernt. Zwei weitere Einstellungen runden das Gefühl eines Objekts ab. `bodySetBounce(node, amount)` reicht von `0`, das sofort liegen bleibt, bis `1`, das mit allem zurückkommt, womit es angekommen ist; der Standard ist ein kaum spürbares `0.1`. `bodySetFriction(node, amount)` ist `0` für Eis und standardmäßig `0.5`. Beide werden zwischen den zwei Dingen kombiniert, die sich berühren, also braucht ein vereister Boden die kleine Zahl auf dem Boden, nicht auf allem, was darüber läuft. [#_knowing_when_things_touch] === Wissen, wann sich Dinge berühren [source,lua] ---- function onCollision(nodeA, nodeB, x, y, z, speed) if speed > 4 then knocks = knocks + 1 end end ---- `knocks` ist dort eine Variable von dir, die oben in der Datei mit `local knocks = 0` wartet, und das Spielzeug weiter unten gibt sie aus. `onCollision` ist ein Callback wie `onOverlayUpdate`: Du schreibst ihn, die Engine ruft ihn auf. Er kommt einmal für jeden neuen Kontakt -- zwei Körper, die sich weiter berühren, melden sich nicht noch einmal -- mit beiden Knoten, dem Punkt in der Welt, an dem sie sich getroffen haben, und wie schnell sie sich getroffen haben. Durch die Geschwindigkeit können ein Anstupsen und ein Krachen verschieden klingen, und sie ist der Grund, warum der Test oben das sanfte Setzen eines Stapels ignoriert. Die andere Hälfte des Paars ist `onTrigger`. Mach einen Körper mit `bodySetTrigger(node, true)` zu einem Trigger, und er hört auf, Dinge zu schieben: Er wird zu einem Bereich, der über `onTrigger(trigger, other, entered)` meldet, was hineinkommt und was ihn verlässt. Eine Ziellinie, ein Türdurchgang, eine Grube, ein Gegenstand zum Aufsammeln. Er ist der 3D-Nachfahre der Kollisionsprüfungen aus Lektion elf, und für alles, was nach „hat der Spieler diese Stelle erreicht“ aussieht, ist er das, was du willst, statt eines festen Körpers. [#_the_toy] === Das Spielzeug Jetzt das Ganze. Fang mit dem Skript von oben an und bau es hierzu um: eine Wanne mit vier Wänden, sechs Kisten in einer Pyramide und eine Stahlkugel, die du ausrichten und abschießen kannst. Die Teile kennst du alle schon. Alles, was sich der neue Code merkt, kommt ganz oben in die Datei, über die Szene: [source,lua] ---- local crates = {} local balls = {} local aimX = 0 local aimLeft = false local aimRight = false local knocks = 0 local showShapes = false ---- Zu den Materialien kommen zwei weitere gemeinsam genutzte Handles: ein poliertes Metall für die Kugel und die Zielmarkierung und ein Kisten-Mesh für alle sechs Kisten. [source,lua] ---- local steel = materialNew() materialSetColor(steel, 200, 205, 215) materialSetMetallic(steel, 1) materialSetRoughness(steel, 0.25) local crateMesh = meshBox(0.8, 0.8, 0.8) ---- Die Wände der Wanne sind vier statische Quader, gemacht in einer Schleife aus einer Tabelle von Platzierungen, und das ist Lektion sechs, die ehrliche Arbeit leistet: [source,lua] ---- local wallMesh = meshBox(16, 1, 0.4) for _, side in ipairs({ { 0, -8, 0 }, { 0, 8, 0 }, { -8, 0, 90 }, { 8, 0, 90 } }) do local wall = nodeNew() nodeSetMesh(wall, wallMesh, stone) nodeSetPosition(wall, side[1], 0.5, side[2]) nodeSetRotation(wall, 0, side[3], 0) bodyNew(wall, BODY_STATIC, SHAPE_BOX, 16, 1, 0.4) end ---- Jeder Eintrag ist ein X, ein Z und eine Drehung um Y. Zwei Wände liegen quer hinten und vorn; zwei sind um neunzig Grad gedreht und bilden die Seiten. Ein Mesh reicht für alle vier, und der Körper wird erst erzeugt, nachdem der Knoten platziert und gedreht ist, weil ein statischer Körper die Position und Drehung des Knotens aus dem Moment übernimmt, in dem er erzeugt wird. Die Kisten kommen aus einer Funktion, damit sechs davon sechs Zeilen sind: [source,lua] ---- local function crateAt(x, y) local crate = nodeNew() nodeSetMesh(crate, crateMesh, wood) nodeSetPosition(crate, x, y, -2) bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.8, 0.8, 0.8) bodySetMass(crate, 4) crates[#crates + 1] = crate end ---- Sie bewahrt jede Kiste, die sie macht, in der Tabelle `crates` auf, und so kann der Zeichen-Callback später allen eine Frage stellen. Sechs Aufrufe bauen die Pyramide, drei auf dem Boden, zwei darauf und eine obendrauf: [source,lua] ---- crateAt(-0.9, 0.4) crateAt(0, 0.4) crateAt(0.9, 0.4) crateAt(-0.45, 1.25) crateAt(0.45, 1.25) crateAt(0, 2.1) ---- Die oberen Reihen sitzen eine Fingerbreite über denen darunter und fallen in dem Moment an ihren Platz, in dem das Spiel startet. Eine kleine Lücke zu lassen ist einfacher, als genau zu stapeln, und es ist freundlicher zur Simulation, als ihr einen Haufen zu geben, der schon zusammengepresst ist. Jeder Schuss macht eine neue Kugel: [source,lua] ---- local function fire() local ball = nodeNew() nodeSetMesh(ball, meshSphere(0.35, 24), steel) nodeSetPosition(ball, aimX, 1.2, 6) bodyNew(ball, BODY_DYNAMIC, SHAPE_SPHERE, 0.35) bodySetMass(ball, 3) bodySetBounce(ball, 0.3) bodySetVelocity(ball, 0, 2, -14) balls[#balls + 1] = ball if #balls > 5 then nodeDelete(balls[1]) table.remove(balls, 1) end end ---- Sieh dir die Reihenfolge an. Der Knoten wird erzeugt, bekommt eine Form zum Zeichnen, wird *platziert* und bekommt erst dann einen Körper. Ein dynamischer Körper fängt dort an, wo sein Knoten ist, und danach läuft der Verkehr andersherum: Der Körper bewegt den Knoten. Wenn du die Position eines dynamischen Knotens danach selbst setzt, kämpfst du gegen die Simulation, und die Simulation gewinnt. Wenn etwas bewegt werden muss, gib ihm eine Geschwindigkeit, triff es mit einem Impuls, oder mach es wie hier -- erzeug ein neues und lass das alte gehen. Das alte gehen zu lassen ist `nodeDelete`, das den Knoten freigibt, alles darunter und den Körper, der darauf mitfuhr. Ohne diese drei Zeilen bliebe jeder Schuss für immer in der Welt, und ein paar hundert Kugeln später würde die Simulation eine Menge Arbeit für Dinge erledigen, die niemand sehen kann. Aufräumen gehört dazu, wenn man Physik benutzt. Die Steuerung und die Anzeige sind der Rest: [source,lua] ---- function onInputPressed(what) if what == SWITCH_BUTTON1 then fire() elseif what == SWITCH_BUTTON3 then showShapes = not showShapes if showShapes then physicsSetDebug(DEBUG_SHAPES + DEBUG_CONTACTS) else physicsSetDebug(DEBUG_NONE) end elseif what == SWITCH_LEFT then aimLeft = true elseif what == SWITCH_RIGHT then aimRight = true end end ---- Die Leertaste feuert, Umschalt schaltet die Physikansicht ein und aus, und mit gedrückten Pfeiltasten verschiebst du den Abschusspunkt. Eine flache Metallplatte ohne Körper, die in jedem Frame auf `aimX` gesetzt wird, zeigt, woher die nächste Kugel kommt. Das vollständige Skript, mit dem Callback fürs Loslassen und dem Rest der Einrichtung, ist `24-physics.singe` im Ordner `learn`. `physicsSetDebug` ist der nützlichste Aufruf in dieser Lektion. Er zeichnet, was die Simulation tatsächlich enthält -- die echten Formen als Drahtgitter und ein rotes Kreuz überall dort, wo sich in diesem Schritt zwei Körper berührt haben -- über deine Szene. Wenn zum ersten Mal eine Kiste eine Handbreit über dem Boden schwebt, durch ihn hindurchfällt oder etwas aus zwei Metern Entfernung umwirft, schalte das ein, und die Antwort steht auf dem Bildschirm. Füg `DEBUG_STATIC` hinzu, um auch den Boden und die Wände zu sehen. Solange es aus ist, kostet es nichts. Zum Schluss das Zählen: [source,lua] ---- function onOverlayUpdate() local over = 0 local settled = true if aimLeft then aimX = math.max(aimX - 0.08, -5) end if aimRight then aimX = math.min(aimX + 0.08, 5) end nodeSetPosition(marker, aimX, 0.05, 6) for _, crate in ipairs(crates) do local pitch, _, roll = nodeGetRotation(crate) if math.abs(pitch) > 30 or math.abs(roll) > 30 then over = over + 1 end if not bodyIsResting(crate) then settled = false end end overlayClear() overlayPrint(2, 2, "Arrows aim. Space fires. Shift shows the shapes.") overlayPrint(2, 4, "knocked over " .. over .. " of " .. #crates .. " hard hits " .. knocks) if settled then overlayPrint(2, 6, "everything has settled") end return OVERLAY_UPDATED end ---- Eine Kiste zählt als umgeworfen, wenn sie mehr als dreißig Grad geneigt ist, und das ist eine Spielregel, geschrieben in einer Zeile Rechnerei, die auf dem aufsetzt, was die Simulation getan hat. `nodeGetRotation` liefert drei Winkel, und hier werden der erste und der dritte gebraucht, also wird der mittlere in einer Variablen namens `_` aufgefangen. Dieser Unterstrich ist ein gewöhnlicher Name, an dem nichts Besonderes ist, und wird nach Konvention benutzt, um zu sagen: „Den hier ignoriere ich.“ `bodyIsResting` sagt, ob die Simulation einen Körper schlafen gelegt hat, weil er sich nicht mehr bewegt. Daran erkennst du, dass ein Schuss vorbei ist: kein Timer, kein Raten, sondern jede Kiste schläft. Ein Bowlingspiel wertet seinen Frame genau an dieser Stelle. [#_when_not_to_use_it] === Wann du sie nicht benutzen solltest Physik ist das verlockendste Werkzeug in jeder Engine und das, das die meisten Wochenenden verschwendet. Drei Dinge sind über sie wahr, und keins davon ist die Schuld der Engine. *Sie ist teuer.* Jeder dynamische Körper ist Arbeit in jedem Schritt, sechzigmal pro Sekunde, ob ihn jemand ansieht oder nicht. Vierzig Kisten sind in Ordnung. Viertausend nicht, und auf einem kleinen Rechner auch vierhundert nicht. *Sie ist unberechenbar.* Starte dein Spielzeug zweimal, und die Kisten landen anders. Das lässt es lebendig aussehen, und es ist auch der Grund, warum sich ein Spiel bei nichts darauf verlassen kann, was jedes Mal gleich ausgehen muss. Eine Tür, die aufgehen muss, eine Plattform, die ankommen muss, ein Sprung, den der Spieler schaffen können muss: Das ist Rechnerei, oder ein Tween, oder ein kinematischer Körper auf einem Pfad, den du geschrieben hast. Überlass nie etwas, das der Spieler tun muss, einer Simulation, die es hinter eine Kiste fallen lassen kann. *Sie ist schwer abzustimmen.* Die Masse, die Reibung, das Abprallen, die Form und die Größe ziehen alle aneinander, und „es fühlt sich falsch an“ hat selten nur eine Ursache. Jede Stunde, die du damit verbringst, dass sich eine Physik-Kiste gut anfühlt, ist eine Stunde, die du nicht mit dem Spiel verbracht hast. Greif also zu ihr, wenn das Chaos der eigentliche Zweck *ist*: Dinge, die umkippen, fliegende Trümmer, ein Haufen, der sich setzt, ein Ball, der durch einen Raum scheppert, ein Fahrzeug, das über unebenes Gelände rollt, eine Ragdoll, die eine Treppe hinunterpurzelt. Das kann niemand von Hand schreiben, und niemand kann dir genau sagen, wie es aussehen soll. Greif nicht zu ihr, wenn du weißt, was passieren soll. Ein Geschoss, das geradeaus fliegt, sind zwei Zeilen aus Lektion zwei. Eine Plattform, die hoch- und runterfährt, ist `math.sin`. Eine Spielfigur, die so geht und springt, wie Spieler es erwarten, will einen Character-Controller (`playerNew`, in der Referenz), weil ein Mensch kein Quader ist und sich nie wie einer verhält. Ein Gegner, der patrouilliert, will einen Pfad. Ein Menü, das hereingleitet, will einen Tween. Die Regel, die am besten taugt: Benutz Physik für die Dinge, denen der Spieler zusieht, und Rechnerei für die Dinge, die der Spieler tut. [#_try_it_24] === Probier es aus . *Ändere die Welt.* Füg `physicsSetGravity(0, -1.62, 0)` weit oben ein und feuere noch einmal. Dann probier `0, -30, 0`. Dieselben Kisten, ein anderer Planet, eine Zeile. . *Ändere die Kugel.* Mach die Kugel mit `bodySetMass(ball, 30)` zehnmal schwerer, dann mit `0.3` zehnmal leichter, und lass die Geschwindigkeit gleich. Dann setz die Masse zurück und ändere stattdessen die `-14`. Zwei Wege, härter zuzuschlagen, und sie fühlen sich nicht gleich an. . *Mach die Kisten zu Eis.* Füg in `crateAt` `bodySetFriction(crate, 0.02)` ein und füg eine Zeile hinzu, die auch dem Boden eine niedrige Reibung gibt. Sieh zu, wie sich der Haufen von vornherein weigert, stehen zu bleiben. . *Mach die Form absichtlich falsch.* Ändere in `crateAt` die Größen des Körpers auf `2, 2, 2`, während das Mesh bei `0.8` bleibt. Starte es, sieh zu, wie die Kisten schweben und sich aus dem Nichts schubsen, dann drück Umschalt und sieh zu, wie die Debug-Ansicht es in einem Bild erklärt. . *Werte es richtig.* Benutz `bodyIsResting` auf jeder Kiste, um zu bemerken, wann der Schuss vorbei ist, und zähl erst dann, wie viele umgefallen sind, und addier das zu einer Punktzahl. Das ist die Wertungshälfte eines Bowlingspiels. [#_break_it_on_purpose_23] === Mach es absichtlich kaputt Füg unter dem `bodyNew` des Bodens eine Zeile ein, die den Boden schwer macht, und das ist etwas, von dem man vernünftigerweise denken könnte, dass man es will: [source,lua] ---- bodySetMass(floor, 200) ---- Singe hält das Spiel mit etwas wie dem hier an: ---- 32:bodySetMass: Node 1 is not a dynamic body, or the mass is not positive. ---- Die erste Zahl ist die Zeile in deiner eigenen Datei, also wird deine anders sein. Die Zahl nach `Node` ist das Handle des Knotens, die Zahl, die `nodeNew` zurückgegeben hat. Die Beschwerde ist genau: Masse gehört zu Dingen, die die Simulation bewegt, und ein statischer Körper wird von nichts bewegt, also hat er keine Masse, die man setzen könnte. Dass du danach fragst, ist ein Zeichen, dass der Körper die falsche Art für das ist, was du im Sinn hattest -- und wenn du wirklich einen schweren Boden willst, der sich verschieben lässt, dann willst du einen dynamischen Körper mit großer Masse oder einen kinematischen, den du selbst bewegst. Dieselbe Meldung erscheint bei einer Masse von `0`, die Leute schreiben, wenn sie „schwerelos“ meinen. Schwerelos ist keine Masse. Es ist eine Welt ohne Schwerkraft oder `playerSetGravityScale` auf einem Character-Controller. [#_what_you_learned_24] === Was du gelernt hast * Ein Körper hängt an einem Knoten, und von da an sind Knoten und Körper ein Ding. * Statisch bewegt sich nie, dynamisch wird von der Simulation bewegt, und kinematisch wird von dir bewegt und schiebt alles andere beiseite. * Die Kollisionsform ist ein Ersatz für das Mesh, und du bist dafür verantwortlich, dass ihre Größen zu dem passen, was du gezeichnet hast. * Die Masse ist standardmäßig das Volumen der Form bei der Dichte von Wasser, also setz sie bei allem, was sich leicht anfühlen soll. * `bodySetVelocity` ersetzt die Bewegung eines Körpers, `bodyApplyImpulse` ist ein Treffer, und `bodyApplyForce` ist ein Schub, der anhält. * Setz nicht die Position eines dynamischen Knotens; lösch ihn und mach einen neuen, wenn du ihn neu anfangen lassen musst. * `onCollision` meldet jeden neuen Kontakt mit seinem Punkt und seiner Geschwindigkeit, und ein Trigger-Körper meldet Ankünfte und Abgänge über `onTrigger`. * `bodyIsResting` sagt dir, wann alles stillsteht, und dann ist meistens ein Zug vorbei. * `physicsSetDebug` zeichnet die eigene Sicht der Simulation, und es beantwortet die meisten Physikfragen schneller, als Code zu lesen. * Physik ist teuer, nicht wiederholbar und fummelig. Benutz sie für das Chaos, nicht für die Dinge, von denen das Spiel abhängt. [#_next_time_24] === Beim nächsten Mal Fallende Kisten sind eine Art von Spektakel. Die andere Art besteht aus tausend winzigen Dingen, die eine halbe Sekunde leben: Funken, Rauch, Feuer, Regen und Staub. In der nächsten Lektion geht es um Emitter, und sie endet mit einer Explosion. Das ist Lektion fünfundzwanzig.