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

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

1

02.03.2011, 19:24

Kollisionsprüfung stimmt nicht richtig

Hi

Ich bin daran eine Engine zu programmieren. Momentan bin ich an der Programmierung der Kollision dran. Ich habe eine Klasse namens veBox programmiert, welche für das Speichern der aktuellen Position eines Objektes zuständig ist. Dann habe ich eine weitere Klasse namens veBoundingBox. Diese hat zwei Klassen, eine welche die Kollision auf einfaches schneiden von Vierecken prüft und eine weitere, welche genau auf die Pixelüberschneidung achtet. Eine weitere Klasse namens veSprite ist zuständig für das Managing von Sprites. Wenn man ein Sprite lädt, dann wird automatisch eine Membervariable der Klasse veBox gefüllt.

Doch nun stimmt die Kollisionsprüfung nicht ganz überein, obwohl die Box-Daten des Sprites korrekt sind, dass habe ich durch den Debugger überprüfen können.

Hier ist ein Codeauschnitt:

C-/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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
bool veBoundingBox::CheckCollision(const veBox& Box1, const veBox& Box2)
{
// Prüfen, ob eine Kollision statt fand
if((Box1.PosX + Box1.SizeX) <= Box2.PosX)
return false;
if((Box1.PosY + Box1.SizeY) <= Box2.PosY)
return false;
if((Box2.PosX + Box2.SizeX) <= Box1.PosX)
return false;
if((Box2.PosY + Box2.SizeY) <= Box1.PosY)
return false;

return true;
}

veSprite::veSprite(string TextureFilename)
{
// -------------------------------------------------------------------------
// Konstruktor der Sprite Klasse
// -------------------------------------------------------------------------

m_pTexture = NULL;
m_pBuffer = NULL;
m_Width = 0;
m_Height = 0;
m_Frames = 0;
m_Position = veVector3(0.0f, 0.0f, 0.0f);
m_XAnimation = 0.0f;
m_X2Animation = 0.0f;
m_IsWithAnimation = false;
m_IsAnimationPlaying = false;
m_AnimationInterval = 0;
m_IntervalCounter = 0;

HRESULT hResult;

D3DXIMAGE_INFO ImgInfo;
D3DXGetImageInfoFromFile(TextureFilename.c_str(), &ImgInfo);

// Das Sprite ist ohne Animation, also auf false setzen
m_IsWithAnimation = false;

if(FAILED(hResult = D3DXCreateTextureFromFileEx(veSceneManager::SceneManager().m_pD3DDevice,
TextureFilename.c_str(),
ImgInfo.Width,
ImgInfo.Height,
D3DX_DEFAULT,
NULL,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
D3DX_DEFAULT,
D3DX_DEFAULT,
D3DCOLOR_XRGB(255, 0, 255),
NULL,
NULL,
&m_pTexture)))
{
MessageBox(NULL, "Fehler beim Erzeugen der Spieler Sprite Textur", "Fehler aufgetreten",
MB_OK | MB_ICONEXCLAMATION);
}

m_Width = ImgInfo.Width;
m_Height = ImgInfo.Height;

........

// Die Bewegungsmatrix durchführen
m_mTranslation = veMatrixTranslation(m_Position);

// Die Bewegungsmatrix setzen
veSceneManager::SceneManager()->SetTransform(D3DTS_WORLD, (D3DXMATRIX*)&m_mTranslation);

// Die Box mit Werten füllen
m_Box.PosX = static_cast<int>(m_Position.x);
m_Box.PosY = static_cast<int>(m_Position.y);
m_Box.SizeX = static_cast<int>(m_Position.x + m_Width);
m_Box.SizeY = static_cast<int>(m_Position.y + m_Height);

........
}

veResult veSprite::SetPosition(veVector3 Position)
{
    // -------------------------------------------------------------------------
    // Das Sprite bewegen
    // -------------------------------------------------------------------------

    // Die Position übergeben
    m_Position = Position;

    // Die Box mit den aktuellen Werten füllen
    m_Box.PosX = static_cast<int>(m_Position.x);
    m_Box.PosY = static_cast<int>(m_Position.y);
    m_Box.SizeX = static_cast<int>(m_Position.x + m_Width);
    m_Box.SizeY = static_cast<int>(m_Position.y + m_Height);

    return VE_OK;
}

veBox& veSprite::GetBoundingBox()
{
    // Die Box mit den aktuellen Werten füllen
    m_Box.PosX = static_cast<int>(m_Position.x);
    m_Box.PosY = static_cast<int>(m_Position.y);
    m_Box.SizeX = static_cast<int>(m_Position.x + m_Width);
    m_Box.SizeY = static_cast<int>(m_Position.y + m_Height);

    return m_Box;
}


Der Prüfungsbereich ist viel zu gross, die CheckCollision Funktion gibt viel zu früh true zurück.

Kann mir jemand dabei helfen?

LG Partrick

2

02.03.2011, 19:32

Hi,

warum willst du der (Member-)Funktion CheckCollision() denn zwei Boxes übergeben? Das macht in meinen Augen vielleicht noch Sinn, wenn die Funktion statisch wäre, aber da sie das nicht ist gehe ich mal davon aus das du, wenn du von Objekt A diese Funktion aufrufst, dann auch eben dieses Objekt A auf Kollision mit einem anderen Objekt B überprüfen willst. Also musst du nur Objekt B übergeben.

Gruß
SaRu_

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

3

02.03.2011, 19:37

Die CheckCollision Funktion ist aber nur eine Memberfunktion der Klasse veBoundingBox, nicht aber von veSprite. Ich rufe die CheckCollision Funktion in dem SceneManager auf, also ich habe dort eine Funktion namens Check2DCollision(const veBox& Box1, const veBox& Box2). Diese Funktion greift dann einfach, über einen Zeiger auf die Klasse veBoundingBox, auf die Funktion CheckCollision zu.

Aber warum ist der Kollisionsbereich nicht richtig?

4

02.03.2011, 19:43

Diese Funktion greift dann einfach, über einen Zeiger auf die Klasse veBoundingBox, auf die Funktion CheckCollision zu.

Eben da ist der Unterschied: Du greifst nicht auf die Klasse zu, sondern auf ein Objekt der Klasse. Es muss also ein Objekt der Klasse verfügbar sein, sonst hättest du wohl kaum einen Zeiger auf dieses Objekt. Statt dessen könntest du die Funktion auch als statisch deklarieren und brauchst somit kein Objekt bzw. keinen Zeiger mehr.

Nur so eine Anregung.

Ich kann mir auch nicht so recht vorstellen, was du mit "Kollisionsbereich nicht richtig" meinst?! Deine if-Bedingungen in der Funktion sind korrekt. Woran stellst du denn fest, dass der Bereich nicht richtig ist?

Ich verstehe außerdem deinen Ansatz mit den zwei Klassen auch nicht so ganz. Wenn veBox doch schon Position und Größe eines Sprites beinhaltet, wofür brauchst du dann veBoundingBox ? Die Funktionen für die Kollisionserkennung kannst du ja auch der veBox Klasse verpassen. Oder wie handhabst du das?

Gruß
SaRu_

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

5

02.03.2011, 19:54

Aha, also du meinst die Funktion ohne Klasse schreiben. Kann ich auch machen.

Ich habe zwei Sprites in die Szene geladen. Und habe darauf eine Kollision beider Objekte geprüft. Die Kollision entsteht schon ziemlich früher als die Sprite sich schneiden. Warum?

Ich habe eine Skizze auf dem WordPad gemacht. Das erste Viereck soll das obere Sprite darstellen, die Koordinaten sind angegeben. Und das zweite Viereck das zweite Sprite.


(Link)

ernest7

Frischling

Beiträge: 20

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

6

02.03.2011, 20:06

C-/C++-Quelltext

1
 m_Box.SizeX = static_cast<int>(m_Position.x + m_Width);


das sieht mir so aus, als würde SizeX einen viel zu großen Wert bekommen...
*Werbung* Der Welt bestes Android-Metronom: Metronomerous *Werbung*

7

02.03.2011, 20:09

Aha, also du meinst die Funktion ohne Klasse schreiben. Kann ich auch machen.

Nicht ganz. Das wäre natürlich auf möglich, dabei würde die Funktion aber irgendwie untergehen, weil sie keiner Klasse mehr angehört und außerdem könntest du keine andere Funktion - sei's in deiner Engine oder auch in einem Spiel das damit programmiert wird - mit gleichem Namen haben. Ich meinte, dass du die Funktion als Member der Klasse behalten kannst, sie aber lediglich statisch machst (mit dem kleinen Keyword "static" davor). Dann kannst du über die Klassenbezeichnung die Funktion aufrufen, ohne das du ein Objekt der Klasse brauchst: veBoundingBox::CheckCollision(...) statt Objekt->CheckCollision(...)

Ich würde allerdings trotzdem eine einzige Klasse für solche BoundingBoxes entwerfen und der die Funktion CheckCollision() geben. So machen das die meisten 2D Engines bzw. Frameworks und das macht auch irgendwo Sinn. ;)

Ein Beispiel:

C-/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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class BoundingBox
{
public:
    bool CheckCollision(const BoundingBox& box);

    void SetPosition(const float newx, const float newy);
    void SetSize(const float newwidth, const float newheight);

    // usw...

private:
    float x;
    float y;
    float width;
    float height;

    // usw...
}

bool BoundingBox::CheckCollision(const BoundingBox& box)
{
    // Hier dein Quellcode  (die if-Bedingung etc.) von oben
}

// In der Anwendung:
BoundingBox box1();
box1.SetPosition(123.f, 456.f);
box1.SetWidth(100.f, 100.f);

BoundingBox box2();
 box2.SetPosition(132.f, 456.f);
box2.SetSize(100.f, 100.f);

if(box1.CheckCollision(box2))
{
    // Kollision!
}

if(box2.CheckCollision(box1))   // Entspricht der oberen if-Bedingung, es ist also egal, von welchem Objekt die Funktion aufgerufen wird.
 {
    // Kollision!
 }



EDIT: ernest7 hat Recht. Außer du wolltest statt der Breite der Box direkt die x-Koordinate des rechten Box-Endes angeben. Darauf stößt man auch hin und wieder. Aber dann ist zum einen die Variablenbezeichnung nicht günstig gewählt und zum anderen ist dann die Kollisionsüberprüfung falsch. Dort musst du dann m_Position.x weglassen.

Gruß
SaRu_

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »SaRu« (03.03.2011, 14:09)


Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

8

02.03.2011, 20:11

Ich habe die Orthogonale Weltmatrix gewählt. Deshalb ist das nicht zu gross denke ich mal. Es ist ja die Position des Sprites + die Breite oder die Höhe des Sprites. Sollte eingentlich stimmen oder nicht?

ernest7

Frischling

Beiträge: 20

Wohnort: Dresden

Beruf: Student

  • Private Nachricht senden

9

02.03.2011, 20:19

Deine Kollisionsfunktion ist so angelegt, dass in m_Box.SizeX nur die Breite des Sprites stehen darf.
und die Breite des Sprites ist nur m_Width
und nicht m_Position.x + m_Width

mit der Höhe ist es natürlich das gleiche Problem
*Werbung* Der Welt bestes Android-Metronom: Metronomerous *Werbung*

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

10

02.03.2011, 20:21

Eine gute Idee, danke. Doch dann kann ich meinen SceneManager für die Funktion nicht mehr verwenden. Und der Quellcode wird etwas unübersichtlicher. Ich könnte die Funktion CheckCollision der Sprite Klasse hinzufügen. Dann kann ich das Sprite mit dem übergebenen Paramter überprüfen.

ernest7: Aber wenn die Position nicht mehr 0 ist, dann stimmt SizeX nicht mehr. Deine Methode würde nur dann Sinn machen, wenn die Position immer bei 0 wäre. Dann wäre PosX = 0 und SizeX = m_Width.

Werbeanzeige