This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Wenn du kinematische Kollisionsobjekte (kinematic collision objects) verwendest, musst du Kollisionen selbst auflösen und die Objekte als Reaktion darauf bewegen. Eine naive Implementierung zum Trennen zweier kollidierender Objekte sieht so aus:
function on_message(self, message_id, message, sender)
-- Handle collision
if message_id == hash("contact_point_response") then
local newpos = go.get_position() + message.normal * message.distance
go.set_position(newpos)
end
end
Dieser Code trennt dein kinematisches Kollisionsobjekt von anderen Physikobjekten, in die es eingedrungen ist. Die Verschiebung geht jedoch oft zu weit, sodass du in vielen Fällen ein Zittern siehst. Um das Problem besser zu verstehen, betrachte den folgenden Fall, in dem eine Spielfigur mit zwei Objekten, A und B, kollidiert ist:

Die Physik-Engine sendet in dem Frame, in dem die Kollision auftritt, mehrere "contact_point_response"-Nachrichten: eine für Objekt A und eine für Objekt B. Wenn du die Figur als Reaktion auf jedes Eindringen bewegst, wie im naiven Code oben, ergibt sich folgende Trennung:
Die Reihenfolge ist beliebig, aber das Ergebnis ist in beiden Fällen gleich: eine Gesamtverschiebung, die der Summe der einzelnen Eindringvektoren entspricht:

Um die Figur korrekt von den Objekten A und B zu trennen, musst du die Eindringtiefe jedes Kontaktpunkts verarbeiten und prüfen, ob vorherige Verschiebungen die Trennung bereits vollständig oder teilweise bewirkt haben.
Angenommen, die erste Kontaktpunktnachricht kommt von Objekt A und du bewegst die Figur um den Eindringvektor von A heraus:

Dann ist die Figur bereits teilweise von B getrennt. Der schwarze Pfeil oben zeigt den noch nötigen Ausgleich für eine vollständige Trennung von Objekt B. Die Länge des Ausgleichsvektors lässt sich berechnen, indem du den Eindringvektor von A auf den Eindringvektor von B projizierst:

l = vmath.project(A, B) * vmath.length(B)
Den Ausgleichsvektor erhältst du, indem du die Länge von B um l verringerst. Um dies für eine beliebige Anzahl von Eindringungen zu berechnen, kannst du die nötige Korrektur in einem Vektor aufsummieren. Beginne mit einem Korrekturvektor der Länge null und führe für jeden Kontaktpunkt folgende Schritte aus:
Eine vollständige Implementierung sieht so aus:
function init(self)
-- correction vector
self.correction = vmath.vector3()
end
function update(self, dt)
-- reset correction
self.correction = vmath.vector3()
end
function on_message(self, message_id, message, sender)
-- Handle collision
if message_id == hash("contact_point_response") then
-- Get the info needed to move out of collision. We might
-- get several contact points back and have to calculate
-- how to move out of all of them by accumulating a
-- correction vector for this frame:
if message.distance > 0 then
-- First, project the accumulated correction onto
-- the penetration vector
local proj = vmath.project(self.correction, message.normal * message.distance)
if proj < 1 then
-- Only care for projections that does not overshoot.
local comp = (message.distance - message.distance * proj) * message.normal
-- Apply compensation
go.set_position(go.get_position() + comp)
-- Accumulate correction done
self.correction = self.correction + comp
end
end
end
end