Bewegungslehre
-> Bouncing Ball

Das mit der Physik ist so ne Sache: es reizt mich ungemein damit rumzuspielen, weils so schöne Formeln gibt, die man so schön verwenden könnte. Aber bei der praktischen Anwendung stolper ich immer wieder über dumme Probleme.
So hab ich mal was zusammengebastelt, was irgendwie zu funktionieren scheint, aber ich weiß, daß es nicht wirklich korrekt ist. Wer die folgenden Scripte korrigieren möchte, ist herzlich eingeladen. :-)
Bouncing Ball
Phase 1 - Der Ball fällt, und fällt, und fällt, und wird erstaunlicherweise immer schneller...

Ohne Physik ists so und so keine Zauberei. "pSpeed" sei eine Property, die die aktuelle Geschwindigkeit enthält. Sie wird ständig durch einen fixen Wert erhöht.

 
on exitFrame
  pSpeed = pSpeed + pAcceleration
  sprite(me.spriteNum).locV = sprite(me.spriteNum).locV + pSpeed
end
Mit Physik schauts zunächst auch nicht viel schwieriger aus. Im Physikbuch steht ne Formel, und die tippt man ab. Ach, und eine neue Property namens "pGravity" erfindet man noch (und setzt den Wert gleich 9.81 oder so ähnlich).
 
on exitFrame me
  -- Physikbuch: s = s0 + (v0 * t) + (b * t^2 / 2)
  t = 1 -- vereinfachen wir mal den Zeitfaktor
  pLocV = pLocV + (pSpeed * t) + (pGravity * t * t / 2)
  pSpeed = pSpeed + (pGravity * t)
  sprite(me.spriteNum).locV = pLocV
end

Phase 2 - Achtung Boden!
 
"Abprallen ist ja nicht schwer" denkt man sich, fragt ab, ob die Position des Balls noch über dem Fußboden liegt, und tippt andernfalls eine kleine if-then-Ausnahme. Und bei dieser Gelegenheit fällt einem noch ein, daß man sich ja auch mit der Elastizität des Balls beschäftigen sollte, woraufhin man klarerweise "pElasticity" kreiert, und mal nen Wert von -0.8 probiert.
 
on exitFrame me
  -- Physikbuch: s = s0 + (v0 * t) + (b * t^2 / 2)
  t = 1 -- vereinfachen wir mal den Zeitfaktor
  pLocV = pLocV + (pSpeed * t) + (pGravity * t * t / 2)
  pSpeed = pSpeed + (pGravity * t)

  if pLocV >= pGround then
    pLocV = pGround
    pSpeed = pSpeed * pElastic
  end if

  sprite(me.spriteNum).locV = pLocV
end

Example: bball1.dir
Sieht ja hübsch aus, allerdings hört der blöde Ball nimma auf zu hüpfen, obwohl man ihn doch deutlich bei jedem Aufprall seiner Energie beraubt...

Ich habe einige Scripts vom BouncingBall im Internet gefunden, die witzigerweise bei genauerer Betrachtung alle diesen Fehler aufweisen. Manchmal erst, wenn man ein bischen mit den Werten rumspielt.
Der Grund für den Fehler ist aber glaube ich logisch, wie die nebenstehnde Skizze verdeutlichen soll:
 

Zum Zeitpunkt, da die Position des Balls zum Fußboden gezwungen wird, darf nicht einfach jene Geschwindigkeit in Rechnung gestellt werden, die der Ball hätte, wenn er ohne Hindernis weitergeflogen wäre.

Man muß sie neu berechnen! Juchu. ;-)



Die Korrektur.

Wenn man die Formel aus dem Physikbuch ein bischen umdreht, sieht sie folgendermaßen aus: 

(b * t^2 / 2) + (v0 * t) + s0 - s = 0
Sämtliche Faktoren sind zu dem Zeitpunkt, da der Ball den Boden berührt, bekannt: Die letze Position (s0), die neue Position (s), die letzte Geschwindigkeit (v0), die Beschleunigung oder Gravitation (b) - nur das "t", die verstrichene Zeit, ist das große Fragezeichen.

Die umgedrehte Formel entspricht folgendem mathematischen System ("quadratische Gleichung"):

(a * x^2) + (b * x) + c = 0
und dafür gibt es erneut eine Formel ("Große Lösungsformel"), um das x ausrechnen zu können:
x = (-b +- sqrt(b^2  - 4ac)) / 2a

Setzt man die richtigen Werte ein (und macht dabei hoffentlich keine Fehler ;-) dann sieht das fertige Script ca. so aus:

 
on exitFrame me
  t = 1 -- the time - lets make it easy

  -- s = s0 + (v0 * t) + (b * t^2 / 2)
  newLocV = pLocV + (pSpeed * t) + (pGravity * t * t / 2)
  newSpeed = pSpeed + (pGravity * t)

  if newLocV >= pGround then -- touching the ground
    if newLocV > pGround then
      --  (a * x^2) + (b * x) + c = 0
      -->  x = (-b +- sqrt(b^2  - 4ac)) / 2a
      --      a ... pGravity/2
      --      b ... pSpeed
      --      c ... pLocV - pGround
      partOfTheFormula = sqrt((pSpeed * pSpeed) - (4 * (pGravity/2) * (pLocV - pGround)))
      newT = (-pSpeed + partOfTheFormula) / pGravity
      if newT < 0 or newT > t then newT = (-pSpeed - partOfTheFormula) / pGravity

      if newT > 0 then
        newSpeed = pSpeed + (pGravity * newT)
      end if
    end if

    newLocV = pGround
    newSpeed = pSpeed * pElastic
  end if

  pLocV = newLocV
  pSpeed = newSpeed
  sprite(me.spriteNum).locV = pLocV
end

Example: bball2.dir


Phase 3 - und seitlich werfen, bitte!

Das Hinzufügen der horizontalen Bewegung ist eigentlich nicht schwierig, weil sie ziemlich unabhängig vom Rest berechnet werden kann. Genaugenommen müßte man zwar Korrekturen vornehmen, wenn der Ball am Boden aufprallt, aber dafür war ich glücklicherweise zu faul. ;-)
Ein halbwegs funktionierendes Script ist dem nächsten Beispiel zu entnehmen. Sieht ja eigentlich eh ganz cool aus, nicht?


 
Example: bball3.dir
PS: Ball nehmen, Maus bewegen, und während der Bewegung den Ball loslassen :-)


Wars das?
Nein.

Praktisch betrachtet habe ich mich mit dem Rollen des Balles noch nicht auseinandergesetzt, und sehe auch deutlich einen hässlichen, ruckartigen Übergang vom Hüpfen ins Rollen. Diesbezügliche Tips werden gerne entgegengenommen.

Und theoretisch betrachtet könnte ich noch stundenlang davon erzählen, wie ungenau und mangelhaft dieses Script doch eigentlich ist. Insbesondere, was den Aufprall des Balles betrifft. Aber ich denke, das interessiert wohl niemanden mehr... :-)


Last: Don't touch me