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

Peter Bucher

Frischling

  • »Peter Bucher« ist der Autor dieses Themas

Beiträge: 28

Wohnort: Schweiz

Beruf: IT

  • Private Nachricht senden

1

30.01.2007, 21:52

Kollision performant und zuverlässig abfragen (2D)

Hallo zusammen

Ich arbeite an einem Echtzeitstrategiespiel, mit C#, .NET 2.0 und Managed DirectX 9.0c in der aktuellsten Version.
Es funktioniert schon ziemlich viel, nun stehe ich aber an einem Punkt an.

Ich habe eine Map die aus Tiles besteht, diese Tiles sind Objekte, zusätzlich habe ich eine Liste von Unit Objekten.

Jetzt habe ich es so gehandhabt, bei jedem Renderdurchgang alle Einheiten mit allen Einheiten auf Kollision zu überprüfen, sich selber natürlich ausgeschlossen.

Natürlich geht das bei mehreren Hundert Einheiten schief und arbeitet nicht korrekt bzw. ist überlastet~

Wie kann ich das besser lösen?
--
Microsoft MVP - Visual Developer ASP / ASP.NET
http://www.aspnetzone.de/blogs/peterbucher/ - Auf den Spuren von .NET

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

30.01.2007, 21:59

verwende bounding volumes. ist sicher einmal der erste schritt (falls du das noch nicht machst)

der nächste wäre es, eine hierarchie, hier z.b. einen quad oder bsp/kd-tree, zu verwenden und dann nurmehr die einheiten, die sich im selben leaf des trees befinden zu testen...

Peter Bucher

Frischling

  • »Peter Bucher« ist der Autor dieses Themas

Beiträge: 28

Wohnort: Schweiz

Beruf: IT

  • Private Nachricht senden

3

30.01.2007, 22:08

Hallo dot

Zitat von »"dot"«

verwende bounding volumes. ist sicher einmal der erste schritt (falls du das noch nicht machst)

Meinst du damit "Bounding Box"?
Ich prüfe im Moment sowieso nicht genau, sondern nur auf eine Rechteckkollision.

Zitat von »"dot"«


der nächste wäre es, eine hierarchie, hier z.b. einen quad oder bsp/kd-tree, zu verwenden und dann nurmehr die einheiten, die sich im selben leaf des trees befinden zu testen...

Da werde ich mich einmal umsehen, danke.

Ist es dann üblich, bei jedem Renderdurchgang auf Kollision zu prüfen oder lässt man hier z.B. jeden zweiten weg?
--
Microsoft MVP - Visual Developer ASP / ASP.NET
http://www.aspnetzone.de/blogs/peterbucher/ - Auf den Spuren von .NET

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

30.01.2007, 22:18

Zitat

Meinst du damit "Bounding Box"?

ich meinte damit alles mögliche, wie z.b. bounding box, zylinder, kugeln, ellipsoide, etc.
die 2d varianten, die hier natürlich sinnvoll sind, eingeschlossen.

ich könnte mir dann so einige weiterentwicklungen eines systems mit rechtecken vorstellen. z.b. dass man erst nur die mittelpunkte der boxen überprüft (wenn abstand im verhältnis zur ausdehnung groß dann weg damit) und so schneller potentielle kollisionspartner aussortiert (also im prinzip erstmal mit kreisen testet).
in strategiespielen werden einheiten ja oft gruppiert, wobei die gruppe ja meist zusammenbleibt. man könnte also auch eine bounding box für eine ganze gruppe nehmen und erst die testen usw...

du könntest z.b. aber auch jedem tile eine liste mit den objekten die sich drauf befinden geben, wobei die objekte sich beim bewegen automatisch ein- und austragen...

am einfachsten ist es natürlich in jedem frame zu testen. wenn die framerate hoch und die relativen geschwindigkeiten klein genug sind, dann spricht natürlich auch nichts dagegen, in jedem n-ten frame testen.
weiters könnte man z.b. bei linearen bewegungen der einheiten die bewegung über die nächsten x frames abschätzen und dann potentielle kollisionspartner über die schnittpunkte der bewegungslinien finden...

Peter Bucher

Frischling

  • »Peter Bucher« ist der Autor dieses Themas

Beiträge: 28

Wohnort: Schweiz

Beruf: IT

  • Private Nachricht senden

5

30.01.2007, 22:34

Zitat von »"dot"«

ich könnte mir dann so einige weiterentwicklungen eines systems mit rechtecken vorstellen. z.b. dass man erst nur die mittelpunkte der boxen überprüft (wenn abstand im verhältnis zur ausdehnung groß dann weg damit) und so schneller potentielle kollisionspartner aussortiert (also im prinzip erstmal mit kreisen testet).
in strategiespielen werden einheiten ja oft gruppiert, wobei die gruppe ja meist zusammenbleibt. man könnte also auch eine bounding box für eine ganze gruppe nehmen und erst die testen usw...

Ich steige nicht ganz, wie ich das lösen soll.
Irgendwie muss ich ja bei einer Einheit wissen, mit welchen anderen (benachbarten) Einheiten, zu prüfen ist.
Ich finde so wenig Informationen zu meinem Problem.
--
Microsoft MVP - Visual Developer ASP / ASP.NET
http://www.aspnetzone.de/blogs/peterbucher/ - Auf den Spuren von .NET

6

30.01.2007, 22:36

Ich muss sagen, dass du dir zu dem Thema auch mal EMpire Earth 1 anschauen kannst. Ich weiss nicht ob das in EE2 noch so krass war- aber bei EE1 war es so, dass die ganz offensichtlich sowas wie bounding Boxen bei ihren Einheiten hatten- was gerade bei den Schiffen dazu gefuehrt hat, dass das totale Chaos ausgebrochen ist!! Die Schiffe sind an einer Stelle nicht zwischen 2 "Landmassen" durchgekommen weil da was falsch berechnet wurde. Rein vom Augenmass her haette das Schiff da locker durchfahren koennen- das Wasser zwischen den beiden Inseln war breit genug. Was ich damit sagen will ist, dass es mit einer Verwendung von Bounding Boxen bei den Einheiten ziemlich daneben gehen kann. Ich wuerde einfach versuchen, dass die Einheiten einfach nicht sehr durch andere Charactere durchlaufen. Sogar so Spiele wie Warcraft 3 haben das so geloest. Du kannst dann halt immer noch bounding Boxen um die Gebauede haben- bei den Einheiten kann das aber, denke ich mal, Chaos bedeuten!!

Peter Bucher

Frischling

  • »Peter Bucher« ist der Autor dieses Themas

Beiträge: 28

Wohnort: Schweiz

Beruf: IT

  • Private Nachricht senden

7

30.01.2007, 22:40

Hmm, sollte doch eigentlich nicht, wenn man nach dem Box Kontrolltest auch noch einen Pixelgenauen Test macht~

Mein Problem ist aber nicht die Kollisionsabfrage an sich, sondern wie ich diese Übeprüfung eingeschränkt auf die Einheiten anwenden kann.
Also das ausschliessen von Einheiten.

<edit>
Was evt. noch wichtig wäre.
Wenn ich 4 Einheiten auf der Map habe, funktioniert meine Methode.
Bei > 200 gibt es aber Probleme, das die Kollision nicht erkannt wird bzw. übersprungen wird.
</edit>
--
Microsoft MVP - Visual Developer ASP / ASP.NET
http://www.aspnetzone.de/blogs/peterbucher/ - Auf den Spuren von .NET

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

8

31.01.2007, 09:39

Zitat

Hmm, sollte doch eigentlich nicht, wenn man nach dem Box Kontrolltest auch noch einen Pixelgenauen Test macht~

richtig; auf jeden fall vorher mit bounding boxen testen...

Zitat

Was evt. noch wichtig wäre.
Wenn ich 4 Einheiten auf der Map habe, funktioniert meine Methode.
Bei > 200 gibt es aber Probleme, das die Kollision nicht erkannt wird bzw. übersprungen wird.


was macht denn deine methode genau?
wenn du einfach jede einheit gegen jede andere testest, dann sollte eigentlich nur die geschwindigkeit, aber nicht das ergebnis des tests von der anzahl der einheiten abhängen...bug im kollisionstest?

wenn ich ganz ehrlich bin wundert es mich, dass 200 rechtecktests ein performance problem darstellen...

EDIT: habs mal getestet

ich brauch für 2000000 tests in der debug version ca 110 ms und im release build 18 ns (das kommt mir aber etwas zu wenig vor...) (AMD Athlon 64 X2 3800+)

hier mein testprogramm:

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
#include <iostream>
#include <list>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

const int rectCount = 2000000;

float rnd() { return 15.0f * static_cast<float>(rand()) / static_cast<float>(RAND_MAX); };

class rect
{

    float left;
    float top;
    float right;
    float bottom;

public:

    bool overlaps(const rect& r) const
    {
        if( r.left > right ) return false;
        if( r.top > bottom ) return false;
        if( r.bottom < top ) return false;
        if( r.right < left ) return false;
        return true;
    };

    rect() : left(rnd()), top(rnd()), right(left+rnd()), bottom(top+rnd()) {};

};

int main()
{

    __int64 freq, tick1, tick2;

    srand(GetTickCount());

    rect* rects = new rect[rectCount];

    QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>(&freq) );
    QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>(&tick1) );

    for( int i = 0; i < 10 ; ++i )
    {
        for( int i = 1; i < rectCount; ++i )
            rects[i].overlaps(rects[0]);
    }

    QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>(&tick2) );

    double timedif = static_cast<double>(tick2 - tick1) / static_cast<double>(freq) * 100.0f;

    std::cout << "average: " << timedif << "ms";

    delete[] rects;

    while( !_kbhit() );

    return 0;
}

Peter Bucher

Frischling

  • »Peter Bucher« ist der Autor dieses Themas

Beiträge: 28

Wohnort: Schweiz

Beruf: IT

  • Private Nachricht senden

9

31.01.2007, 20:09

Hallo dot

Zitat von »"dot"«


was macht denn deine methode genau?
wenn du einfach jede einheit gegen jede andere testest, dann sollte eigentlich nur die geschwindigkeit, aber nicht das ergebnis des tests von der anzahl der einheiten abhängen...bug im kollisionstest?

In meinem Render Loop rufe ich bei jeder Frame meine Methode DrawGameObjects auf.
In dieser rufe ich für jede Einheit, die ich im Game habe, eine Methode "IsCollides" auf, diese kontrolliert ob eine Kollision mit irgend einer anderen Einheit vorhanden ist.
Ich glaube es könnte zuviel sein, in jeder Frame, jede Einheit gegen jede Einheit zu testen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
        public bool IsCollides(GameObject obj) {
            foreach (GameObject item in this._gameObjects) {
                // Verhindern dass sich das Objekt mit sich selber vergleicht

                // Ansonsten ist die Kollision immer true

                if(!obj.Equals(item)) {
                    return Tools.Intersect(
                        new Rectangle((int)obj.Position.X, (int)obj.Position.Y, obj.Size.Width, obj.Size.Height),
                        new Rectangle((int)item.Position.X, (int)item.Position.Y, item.Size.Width, item.Size.Height)
                        );
                }
            }
            return false;
        }


Tools.Intersect sollte sicher fehlerfrei sein:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        /// <summary>

        /// Prüft die Kollision zweier rechteckigen Objekte anhand der Koordinaten

        /// </summary>

        /// <param name="rect1, der linke obere Punkt eines Rechteckes muss übergeben werden"></param>

        /// <param name="rect2 DITO"></param>

        /// <returns>Kollision true/false</returns>

        public static bool Intersect(RectangleF rect1, RectangleF rect2) {
            if (((rect1.X < (rect2.X + rect2.Width))
              && (rect2.X < (rect1.X + rect1.Width)))
              && (rect1.Y < (rect2.Y + rect2.Height))) {
                return (rect2.Y < (rect1.Y + rect1.Height));
            }
            return false;
        }


Zitat von »"dot"«


ich brauch für 2000000 tests in der debug version ca 110 ms und im release build 18 ns (das kommt mir aber etwas zu wenig vor...) (AMD Athlon 64 X2 3800+)

Interessant... dann wundere ich mich noch mehr, an was es liegt.
--
Microsoft MVP - Visual Developer ASP / ASP.NET
http://www.aspnetzone.de/blogs/peterbucher/ - Auf den Spuren von .NET

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

10

31.01.2007, 20:26

sieht eigentlich ganz ok aus. wo man sicher sparen kann, ist das if in der schleife (das wird aber nicht soo schlimm sein), und die temporären objekte als parameter für die funktion (new Rectange()...). wies in C# bezüglich by Value vs. by Reference und const correctness (falls es da sowas gibt) aussieht weis ich net. ich vermute mal, dass diesbezüglich alles in ordnung ist (objekte also als referenzen übergeben werden).

bist du dir sicher, dass es am kollisionscode liegt (immerhin sind es nur 200 objekte)?
evtl. ist etwas andres schuld!? ein profiler könnte dir genaues sagen.

EDIT: ich hatte in meinem programm nicht berücksichtigt, dass right und bottom immer neu berechnet werden müssen. hab ich jetzt eingebaut.
außerdem hab ich nur die zeit für die tests gemessen.
ich habs jetzt so umgeschrieben, dass alle rechtecke gegeneinander getestet werden.

neue zeit: 250 ms (debug) und 12 ms (release) für 2000 rechtecke (entspricht also 3998000 tests).

aber immer noch schnell genug. für 200 objekte braucht er nur 140 ns.
selbst wenn C# 10 mal langsamer als C++ wäre (was sicher nicht der fall ist), wäre das vermutlich immer noch kein wirkliches problem...

Werbeanzeige