Tak się trochę zastanawiałem, czy dać tu, czy do inne, no ale w gruncie rzeczy kod jest w c#, więc niech na razie jest tu. Jak się komuś nie spodoba, to przeniesie ;)
Ale idę do sedna. Otóż, w ramach zakończenia sesji i zyskania odrobiny wolnego czasu, postanowiłem sobie coś tam skodzić, padło na odbijanie się kulek :P znaczy oczywista projekt potem się rozrośnie, a może i się nie rozrośnie, ale na razie jest w fazie początkowej. Otóż latają sobie dwie kulki i chcę, żeby po zderzeniu się odbiły, ale żeby robiły to w miarę realistycznie, zgodnie z zasadami fizyki. Jako że przyjmuję kulki za idealne, padło na zderzenie niesprężyste (inelastic collision). Metoda jest prosta: obracamy wektor prędkości tak, by zderzenie następowało na lini jednego wektora, na przykład x. W tym momencie wektor y nie bierze udziału w obliczaniu wektora po kolizji. Wzór na wartości składowych po obrocie wektora jest dosyć prosty:
x' = x * cos(alfa) - y * sin(alfa)
y' = x * sin(alfa) + y * cos(alfa)
Po przekształceniu wektorów prędkości następuje obliczenie nowych prędkości x, zgodnie ze wzorem znalezionym na wikipedii, a który potwierdza się też na paru innych stronach:
http://pl.wikipedia.org/wiki/Zderzenie_sprężyste#Analiza_zderzenia_spr.C4.99.C5.BCystego
Potem oczywiście powrót do wektorów podstawowych, czyli składowe obracam o kąt 360 - alfa, zgodnie z powyższym wzorem.
Wydaje mi się, ze wszystko robię dobrze, cóż, kiedy jednak nie. Błąd na pewno występuje, co można rozpoznać po nieoczekiwanym zachowaniu się piłek ;P Gdy założymy, że piłki lecą wzdłuż osi x lub y, odbicie jest prawidłowe. Jednak gdy kulki lecą na prykład po skosie, odbicie, zamiast zmienić ich kierunki na przeciwne (zderzenie centralne), sprawia, że obie zaczynają lecieć w zupełnie nieoczekiwanym kierunku. JAko że obraz wart tysiąca słów:
przed odbiciem
po odbiciu
Są tam zaznaczone boundingboxy, a także wektory prędkości (z rozbiciem na wektory skłądowe).
Jak więc widać, zupełnie nie tak, jak logika i doświadczenie nakazuje.
Wierzę, że błąd jakiś jest, jednak mimo analizy kodu kilka razy, nie znalazłem. Wydaje się też być wszystko zgodne z równaniami z wiki, obrót wektorów też raczej ok. Gdzie więc jest błąd? Coś nie tak robię? Czy może ogólnie zastosowanie tych wzorów jest złe?
Kod wykrywania kolizji i zmiany prędkości ciał kolidujących:
static public void CheckCollision(List<ISimulationObject> simulationObjects)
{
ISimulationObject collisionObject, simulationObject;
for (int i = 0; i < simulationObjects.Count; ++i)
{
simulationObject = simulationObjects[i];
for (int j = i + 1; j < simulationObjects.Count; ++j)
{
collisionObject = simulationObjects[j];
double left1 = simulationObject.boundingBox.X;
double left2 = collisionObject.boundingBox.X;
double right1 = simulationObject.boundingBox.X + simulationObject.boundingBox.Width;
double right2 = collisionObject.boundingBox.X + collisionObject.boundingBox.Width;
double top1 = simulationObject.boundingBox.Y;
double top2 = collisionObject.boundingBox.Y;
double bottom1 = simulationObject.boundingBox.Y + simulationObject.boundingBox.Height;
double bottom2 = collisionObject.boundingBox.Y + collisionObject.boundingBox.Height;
//checking boundbox collision
if (bottom1 < top2) continue;
if (top1 > bottom2) continue;
if (right1 < left2) continue;
if (left1 > right2) continue;
//if there is probability of collision, check carefully
Region simulationRegion = simulationObject.region;
Region collisionRegion = collisionObject.region;
simulationRegion.Intersect(collisionRegion);
RectangleF[] intersection = simulationRegion.GetRegionScans(new Matrix());
simulationRegion.Dispose();
collisionRegion.Dispose();
if (intersection.Length == 0)
continue;
//take action after collision detection
double angle, dx, dy;
dx = collisionObject.position.x - simulationObject.position.x;
dy = collisionObject.position.y - simulationObject.position.y;
//angle of collision
angle = Math.Atan(dy / dx);
//calculation of velocity after frame correction
Vector2D v1, v2;
v1 = new Vector2D();
v2 = new Vector2D();
v1.x = simulationObject.velocity.x * Math.Cos(angle) - simulationObject.velocity.y * Math.Sin(angle);
v1.y = simulationObject.velocity.x * Math.Sin(angle) + simulationObject.velocity.y * Math.Cos(angle);
v2.x = collisionObject.velocity.x * Math.Cos(angle) - collisionObject.velocity.y * Math.Sin(angle);
v2.y = collisionObject.velocity.x * Math.Sin(angle) + collisionObject.velocity.y * Math.Cos(angle);
//calculating velocity after collision
Vector2D newV1, newV2;
double massSum = simulationObject.mass + collisionObject.mass;
newV1 = new Vector2D();
newV2 = new Vector2D();
newV1.x = v1.x * (simulationObject.mass - collisionObject.mass) + 2 * collisionObject.mass * v2.x;
newV1.x /= massSum;
newV1.y = v1.y;
newV2.x = v2.x * (collisionObject.mass - simulationObject.mass) + 2 * simulationObject.mass * v1.x;
newV2.x /= massSum;
newV2.y = v2.y;
//correcting frame to previous state
simulationObject.velocity.x = newV1.x * Math.Cos(2 * Math.PI - angle) + newV1.y * Math.Sin(360 - angle);
simulationObject.velocity.y = newV1.x * Math.Sin(2 * Math.PI - angle) + newV1.y * Math.Cos(360 - angle);
collisionObject.velocity.x = newV2.x * Math.Cos(2 * Math.PI - angle) + newV2.y * Math.Sin(360 - angle);
collisionObject.velocity.y = newV2.x * Math.Sin(2 * Math.PI - angle) + newV2.y * Math.Cos(360 - angle);
}
Fakt, nakłądania się kul po odbiciu myślę, że jest póki co do pominięcia, to trzeba jeszcze oczywiście poprawić w wykrywaniu kolizji, ale na razie chciałbym uzyskac zadowalający efekt odbicia ;)
Wiem, rozwlekły post i kodu też trochę, ale może ktoś chcętny jednak poczyta :) za co z góry dzięki :)
Pozdr
Pako