Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Wirago

Alter Hase

  • »Wirago« ist der Autor dieses Themas

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

1

13.09.2017, 10:05

2D Kollision > verhindern von glitches

Hallo zusammen,

Ich habe ein recht einfaches rechteck-basiertes System zur Kollisionserkennung das es ermöglicht bei diagonaler Bewegung die Kante "entlang zu rutschen".
Funktioniert soweit ganz gut, allerdings ist es Möglich, dass zB. der Spieler in das Kollisionsobjekt rein glitcht. Es scheint als ob es nur dann passieren kann, wenn man sich um die Ecken des Kollsionsobjektes bewegt.
Ich habe die Vermutung, dass das dann passiert, wenn die Bewegung die Ecke des Kollisionsobjektes abschneidet und so irgendwie dazu führt, dass die Rechtecke des Spielers und des Objektes ineinander rutschen.

Die relevanten Codestellen dazu sehen so aus:

Der UpdateCall erhält playerMovement aus der Tastatureingabe

C#-Quelltext

1
2
3
4
5
6
7
8
public void UpdatePlayer(GameTime gameTime, Vector2 playerMovement)
{
    //...
    playerMovement = CalculatePlayerMovement(Vector2.Normalize(playerMovement) * movementspeed);
    if (playerMovement.Length() != 0)
        MoveBy(playerMovement);
    //...
}


Die CalculatePlayerMovement-Method checkt den Kartenrand, Kollisionsobjekte usw. Abhängig von der Richtung der Kollsion wird dann die x- or y-Bewegung auf 0 gesetzt.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private Vector2 CalculatePlayerMovement(Vector2 movement)
{
    Rectangle destRect = new Rectangle(BoundingBox.X + (int)movement.X, BoundingBox.Y + (int)movement.Y, Width, Height);
    Vector2 returnValue = movement;
    //...
    foreach (Rectangle r in TileMap.collisionObjects)
    {
         if (destRect.Intersects(r))
         {
             string collsionSide = GetCollisionSide(destRect, r);
             if (collsionSide == "top" || collsionSide == "bottom")
                 returnValue.Y = 0;
             if (collsionSide == "left" || collsionSide == "right")
                 returnValue.X = 0;
         }
     }
    //...
}


Die Erkennung der Kollsionsseite habe ich von HIER.

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private string GetCollisionSide(Rectangle destRect, Rectangle r)
{
    float w = (destRect.Width + r.Width) / 2;
    float h = (destRect.Height + r.Height) / 2;
    float dx = destRect.Center.X - r.Center.X;
    float dy = destRect.Center.Y - r.Center.Y;

     if (Math.Abs(dx) <= w && Math.Abs(dy) <= h)
     {
         float wy = w * dy;
         float hx = h * dx;
         if (wy > hx)
         {
            if (wy > -hx)
                return "top";
            else
                return "right";
         }
         else if (wy < hx)
         {
             if (wy > -hx)
                 return "left";
             else
                 return "bottom";
         }
    }
    return "";
}


Ich weis nicht so recht an welcher Stelle das Problem liegt. Wahrscheinlich übersehe ich nur eine Kleinigkeit, komme allerdings nicht drauf.
Die Erkennung der Kollsionsseite funktioniert soweit eigentlich tadellos, zumindest laut debug-Anzeige

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

2

13.09.2017, 18:23

Was passert wenn wy = hx? Oder kann das per Definition nicht passieren?
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Wirago

Alter Hase

  • »Wirago« ist der Autor dieses Themas

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

3

13.09.2017, 19:00

Was passert wenn wy = hx? Oder kann das per Definition nicht passieren?


War mir jetzt selber nicht 100% sicher, aber ein breakpoint bei einem

C#-Quelltext

1
2
if (wy == hx)
    return "test";


wurde trotz glitch nie erreicht.

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

4

13.09.2017, 20:14

schmeiß doch mal testweise das mit den Seiten raus und mach bei einer Kollision einfach immer einen kompletten Stop. Oder lass es dir ausgeben, um zu prüfen, ob das intersect auch immer dann anspringt, wenn du es erwartest.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Wirago

Alter Hase

  • »Wirago« ist der Autor dieses Themas

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

5

13.09.2017, 20:39

schmeiß doch mal testweise das mit den Seiten raus und mach bei einer Kollision einfach immer einen kompletten Stop. Oder lass es dir ausgeben, um zu prüfen, ob das intersect auch immer dann anspringt, wenn du es erwartest.


Das war ja der Ursprungszustand. Ganz furchtbar im gameplay.
War dann ein einfaches

C#-Quelltext

1
2
3
4
5
foreach (Rectangle r in TileMap.collisionObjects)
{
     if (destRect.Intersects(r))
         returnValue = Vector2.Zero;
}


Hat auch einwandfrei funktioniert. Nur eben spieltechnisch ganz schlimm. Die degbug-Augabe gibt auch immer die korrekte Seite der Kollision aus.

PuppetMaster

Frischling

Beiträge: 20

Beruf: Embedded-System Entwickler (C++)

  • Private Nachricht senden

6

13.09.2017, 20:52

Was passert wenn wy = hx? Oder kann das per Definition nicht passieren?


War mir jetzt selber nicht 100% sicher, aber ein breakpoint bei einem

C#-Quelltext

1
2
if (wy == hx)
    return "test";


wurde trotz glitch nie erreicht.

Hi,
wy und hx sind vom Typ IEEE 754 floating point number, ein Vergleich mittels == ist deshalb nicht Zielführend.
Ich kann dir nur wärmstens folgende Lektüre empfehlen: http://www.itu.dk/~sestoft/bachelor/IEEE754_article.pdf

Um die Sache abzukürzen, normalerweise wird eine fuzzy comparision mit bestimmten epsilon Wert benutzt um Fließkommazahlen zu vergleichen.
Abgewandelt aus https://stackoverflow.com/a/485777:

C#-Quelltext

1
2
3
4
public static bool fuzzyCompare(this float float1, float float2, float epsilon)
{
    return (Math.Abs(float1 - float2) <= epsilon);
}


Vielleicht löst das ja schon dein Problem.

Zitat von »"Billy Talent - Fallen Leaves"«

Run away before you drown, or the streets will beat you down.
Fallen leaves, fallen leaves, fallen leaves on the ground.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »PuppetMaster« (13.09.2017, 20:57)


Wirago

Alter Hase

  • »Wirago« ist der Autor dieses Themas

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

7

13.09.2017, 21:46


wy und hx sind vom Typ IEEE 754 floating point number, ein Vergleich mittels == ist deshalb nicht Zielführend.

Da hast du natürlich absolut Recht. Danke für den Input.

Eingebaut:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (wy > hx)
{
    if (wy > -hx)
    return "top";
    else
    return "right";
}

if (wy < hx)
{
    if (wy > -hx)
    return "left";
    else
    return "bottom";
}

if (fuzzyCompare(wy, hx, 0.1f))
    return "test";


Zeile 17 im o.g. Code in meinen Tests nie erreicht. Ergo ist wy und hx nicht gleich. Kann aber natürlich sein, dass in irgendeinem Frame die Differenz nur sehr klein ist, glaube ich aber nicht wirklich.

Nox

Supermoderator

Beiträge: 5 272

Beruf: Student

  • Private Nachricht senden

8

14.09.2017, 02:15

Hm solange der Algorithmus auf das Ergebnis kommt, dass die Kollision oben oder unten statt findet, kann sich der Spieler weiterhin noch nach links und rechts bewegen und vice versa. Ggf ist dies das Problem. Kann man schnell nachprüfen indem man sich die Geschwindigkeit und das Ergebnis von "GetCollisionSide" sich ausgeben lässt.
PRO Lernkurs "Wie benutze ich eine Doku richtig"!
CONTRA lasst mal die anderen machen!
networklibbenc - Netzwerklibs im Vergleich | syncsys - Netzwerk lib (MMO-ready) | Schleichfahrt Remake | Firegalaxy | Sammelsurium rund um FPGA&Co.

Wirago

Alter Hase

  • »Wirago« ist der Autor dieses Themas

Beiträge: 1 193

Wohnort: Stockerau

Beruf: CRM Application Manager

  • Private Nachricht senden

9

14.09.2017, 16:48

Jop, genau das ist das Problem.
Anhand des Anhangs erkennt man den Fehler ganz gut. Eigentlich leicht erklärt.

Die diagonale Bewegung (grün) würde eine Kollision verursachen. GetCollisionSide() erkennt, dass Top Seite kollidiert. Die Y-Richtung wird auf 0 gesetzt, die X-Richtung allerdings nicht. Und schon wandert das Objekt zwar nicht mehr nach oben, sonder seitlich in das zweite Objekt rein.

Als Lösung dachte ich mir, prüfe ich die Seiten nochmals, nachdem die Y bzw X Bewegung bereits bereinigt wurde:


C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
foreach (Rectangle r in TileMap.collisionObjects)
{
    if (destRect.Intersects(r))
    {
        string collsionSide = GetCollisionSide(destRect, r);
        if (collsionSide == "top" || collsionSide == "bottom")
            returnValue.Y = 0;
        if (collsionSide == "left" || collsionSide == "right")
            returnValue.X = 0;
    }

    Rectangle newDestRect = new Rectangle(BoundingBox.X + (int)returnValue.X, BoundingBox.Y + (int)returnValue.Y, Width, Height);
    if (newDestRect.Intersects(r))
    {
        string collsionSide = GetCollisionSide(newDestRect, r);
        if (collsionSide == "top" || collsionSide == "bottom")
            returnValue.Y = 0;
        if (collsionSide == "left" || collsionSide == "right")
            returnValue.X = 0;
    }
}


Das hat insofern geholfen, dass der glitch nicht mehr auftaucht, allerdings tritt jetzt statt dem glitch der Fall auf, dass die Bewegung 0,0 ist. Immer noch besser als rein glitchen, allerdings unschön, wenn man aus - aus Spielersicht - unerfindlichem Grund an der Ecke hängen / stehen bleibt. :S

Ich müsste also in dem spezifischen Fall den Spieler irgendwie "um die Ecke schieben"
»Wirago« hat folgendes Bild angehängt:
  • aabb.png

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Wirago« (14.09.2017, 17:01)


Werbeanzeige