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

1

14.06.2012, 18:37

Box2D/OpenGL völlig simpel und trotzdem Fehler...

Hi,
Ich habe eingesehen dass ich mit meiner Physikengine nicht die Features bekommen kann, die ich in meinem Spiel sehen will. Also habe ich mich für eine freie (auch für kommerzielle Zwecke, falls es je dazu kommen sollte) Bibliothek entschieden: Box2D
Mit dem HelloWorld Beispiel für Box2D habe ich angefangen und das Ganze gleich grafisch anzeigen lassen, leider bekomme ich jetzt schon Fehler und weiß nicht wo diese liegen..
Ich lasse zuerst ein statisches Objekt als Boden erzeugen und möchte auf Tastendruck eine Box fallen lassen. Um einfach viele (mehrere) Objekte zu verwalten habe ich eine kleine Klasse dazu geschrieben und bewahre die Objekte in einem std::vector auf.
Das Problem liegt darin dass die Objekte zwar stoppen aber 10 Pixel über der eigentlich gewünschten Position. Dies lasse ich mir mit einer Ausgabe der Y-Position aller Objekte bestätigen. Natürlich kam mir direkt die Unterschiede zwischen dem OpenGL- und dem "normalen" (in den meisten anderen Bibliotheken) -Koordinatensystem, sodass ich die Objekte auch horizontal gespiegelt mir anzeigen gelassen habe (in TestBody:: Draw den 4.Parameter des DrawBox Aufrufs auf -10.0f stellen), was leider ebenfalls nicht ganz richtig ist, denn die erste Box fällt schön genau dort hin wo sie fallen soll, die nächsten Boxes stoppen allerdings wieder 10 Pixel obendrüber...
Quellcode:

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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "GFX.h"
#include "Timer.hpp"
#include <Box2D\Box2D.h>

#define M_PI 3.14159

void DrawBox (float x,float y,float w,float h);
void DrawCircle (float x,float y,float r);

#pragma warning (disable:4482)

class TestBody
{
public:
    TestBody (bool ci,b2World* world)
    {
        circle=ci;
        bodyDef.type = b2BodyType::b2_dynamicBody;
        bodyDef.position.Set(250.0f, 200.0f);
        body = world->CreateBody(&bodyDef);
        b2Shape* shape;
        if (circle) {
            shape=new b2CircleShape ();
            ((b2CircleShape*)(shape))->m_radius=10.0f;
        }
        else {
            shape=new b2PolygonShape ();
            ((b2PolygonShape*)(shape))->SetAsBox(10.0f,10.0f);
        }
        fixtureDef;
        fixtureDef.shape = shape;
        fixtureDef.density = 1.0f;
        fixtureDef.friction = 0.3f;
        body->CreateFixture(&fixtureDef);
    }

    void Draw ()
    {
        glColor3f(1.0f,0.0f,0.0f);
        float r=body->GetAngle()*(180/M_PI);
        float tx=body->GetPosition().x;
        float ty=body->GetPosition().y;
        glTranslatef(tx,ty,0.0f);
        glRotatef(r,0.0f,0.0f,1.0f);
        if (circle)
            DrawCircle(0.0f,0.0f,10.0f);
        else
            DrawBox(0.0f,0.0f,10.0f,10.0f);
        glRotatef(r,0.0f,0.0f,-1.0f);
        glTranslatef(-tx,-ty,0.0f);
        glColor3f(1.0f,1.0f,1.0f);
    }

    b2BodyDef bodyDef;
    b2Body* body;
    b2FixtureDef fixtureDef;
    bool circle;
};

int main (int argc,char* argv[])
{
    GFX* gfx=new GFX ();
    if (!gfx->Init ())
        return -1;
    bool isRunning=true;
    SDL_Event e;
    sdlTimer->Update ();

    b2Vec2 gr (0.0f,-100.0f);
    b2World* world=new b2World(gr);
    b2BodyDef groundBodyDef;
    groundBodyDef.type=b2BodyType::b2_staticBody;
    groundBodyDef.position.Set(0.0f, 0.0f);
    b2Body* groundBody = world->CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 50.0f);
    groundBody->CreateFixture(&groundBox, 0.0f);

    glColor3f(1.0f,1.0f,1.0f);
    vector<TestBody*> bodies;
    vector<TestBody*>::iterator i;
    while (isRunning)
    {
        gfx->BeginDraw();
        DrawBox (groundBody->GetPosition().x,groundBody->GetPosition().y,500.0f,50.0f);
        for (i=bodies.begin();i!=bodies.end();++i)
            (*i)->Draw();
        gfx->EndDraw ();
        if (SDL_PollEvent(&e)) {
            switch (e.type)
            {
            case(SDL_QUIT):{isRunning=false;}break;
            case(SDL_KEYDOWN):{
                switch(e.key.keysym.sym)
                {
                case(SDLK_ESCAPE):{isRunning=false;}break;
                case(SDLK_SPACE):{
                    bodies.push_back(new TestBody(false,world));
                                 }break;
                case(SDLK_x):{
                    for (i=bodies.begin();i!=bodies.end();++i)
                        cout << (*i)->body->GetPosition().y << endl;
                             }break;
                }
                              }break;
            }
        }
        sdlTimer->Update ();
        world->Step(sdlTimer->GetElapsed(),6,2);
    }
    for (i=bodies.begin();i!=bodies.end();++i)
        delete (*i);
    delete world;
    gfx->Quit ();
    return 0;
}

void DrawBox (float x,float y,float w,float h)
{
    glBegin(GL_QUADS);
    glVertex2f(x,y);
    glVertex2f(x+w,y);
    glVertex2f(x+w,y+h);
    glVertex2f(x,y+h);
    glEnd ();
}

void DrawCircle (float x,float y,float r)
{
}

Tobiking

1x Rätselkönig

  • Private Nachricht senden

2

14.06.2012, 19:04

Laut Box2D Manual wird SetAsBox nur die halbe Breite/Höhe übergeben. Du erstellst also 20x20 große Boxen und nicht 10x10.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

14.06.2012, 19:30

Ich hätte noch zwei unabhängige Anmerkungen zu deinem Code:
1) Ich würde pi nicht als Makro, sondern als richtige Konstante definieren, Macros are evil ;)
2) Du solltest warning C4482 nicht disablen, sondern besser das Problem beheben. Diese Warnung weißt dich darauf hin, dass du unnötigerweise von nicht portablen Spracherweiterungen gebrauch machst.


Ich definier mir mathematische Konstanten so:

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
namespace math
{
  template <typename T>
  struct constants;

  template <>
  struct constants<float>
  {
    static float  one() { return 1.0f; }
    static float zero() { return 0.0f; }
    static float   pi() { return 3.1415926535897932384626434f; }
    static float    e() { return 2.7182818284590452353602875f; }
  };

  template <>
  struct constants<double>
  {
    static double  one() { return 1.0; }
    static double zero() { return 0.0; }
    static double   pi() { return 3.1415926535897932384626434; }
    static double    e() { return 2.7182818284590452353602875; }
  };

  template <>
  struct constants<long double>
  {
    static long double  one() { return 1.0; }
    static long double zero() { return 0.0; }
    static long double   pi() { return 3.1415926535897932384626434; }
    static long double    e() { return 2.7182818284590452353602875; }
  };
}


Ein Problem mit einer einfachen Konstante oder gar einem #define für pi ist nämlich, dass pi dann z.B. immer vom Typ double wäre. Das ist nicht nur nicht schön, sondern auch tatsächlich potentiell problematisch. Denn wenn du nun dein M_PI, welches vom Typ double ist, in einer Berechnung mit floats benutzt, ist der Compiler in der Regel gezwungen, suboptimalen Maschinencode zu erzeugen, da er Typumwandlungen etc. berücksichtigen muss. Mit meiner Variante hab ich die Konstaten in allen möglichen Typen zur Verfügung und kann mir immer den richtigen aussuchen. Natürlich könnte man auch einfach direkt mehrere Varianten von pi mit unterschiedlichen Namen definieren, z.B. pi als double, fpi als float, etc. Das funktioniert aber leider nur so lange, bis du ein template schreiben willst, in dem du typabhängig das richtige pi verwenden willst. Wenn auch etwas mehr Schreibaufwand (math::constants<float>::pi()), bietet obige Lösung also eine Reihe von Vorteilen. Verbesserungsvorschläge sind natürlich immer willkommen...


EDIT: Hier eine etwas kürzere Variante

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
namespace math
{
  template <typename T>
  T pi();

  template <>
  inline float pi<float>() { return 3.1415926535897932384626434f; }

  template <>
  inline double pi<double>() { return 3.1415926535897932384626434; }

  template <>
  inline long double pi<long double>() { return 3.1415926535897932384626434; }

  // ...

}


// Verwendung

float two_pi = 2.0f * math::pi<float>();

Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von »dot« (14.06.2012, 20:31)


4

14.06.2012, 20:36

@Tobiking
Zusammen mit der gerade erkannten Tatsache, dass bei einer Box die GetPosition Funktion nicht eine Ecke sondern die Mitte zurückgibt funktioniert das ganze jetzt. Danke
Eines verstehe ich allerdings nicht: Für den Boden benutze ich ebenfalls SetAsBox und die funktioniert schon richtig so wie sie ist...

@dot
1) Das Programm wurde in ca. 5 Minuten zusammen kopiert, ich habe mir schlicht keine Zeit gelassen für besonders stilistisch schönen, perfomanceoptimierten, oder not-evil Code..
2) Nun meine Entschuldigung ist: Ich arbeite viel mit IntelliSense, wo solche Konstruktionen für mich bequemer sind als sich zu überlegen, als meine (oder hier auch andere) Enumerationsmember mit Rechtschreibung aus dem Gedächtnis zu kramen, um dann frustriert erst die Member über diese Konstruktion mir anzeigen zu lassen und dann das Ganze wieder umforme zu portablen Code während ich weiß, dass ich das Programm nie portieren werde und wenn doch, dieses hier das kleinste Problem sein wird.
Geständnis:
Persönlich finde ich schon die Möglichkeit Enumerationsmember ohne Zugriff auf die Basis nicht schön (als ob man statische Member einer Klasse auch ohne Zugriff auf die Klasse benutzt) und ich benutze auch die Möglichkeiten die mir der Visual C++ Dialekt bringt wie "#pragma once", "#pragma region" u.ä. :whistling:

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

5

14.06.2012, 20:40

Standardkonforme Lösung:

C-/C++-Quelltext

1
2
3
4
5
enum class Bla
{
  bli,
  blo
};


Ohne C++ 11:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
namespace Bla
{
  enum
  {
    bli,
    blo
  };
}

;)

EDIT: Ok in einer Klasse wird die Lösung per namespace leider nicht klappen. Du könntest dort stattdessen aber ein struct verwenden.

6

14.06.2012, 20:52

Weiterer Unterschied:
Bei einer enum class (wie sie auch in C++/Cli existiert leider noch nicht in Native Visual C++ :( ) kann ich direkt eine Instanz erstellen

C-/C++-Quelltext

1
Bla meineInstanz;


Bei der Möglichkeit mit einem Namespace müsst ich dann doch den ebenfalls unschönen Weg über eine Nummer gehen?
Also

C-/C++-Quelltext

1
2
3
int meineInstanz=Bla::bli;
//oder vl. auch sogar
int meineAndreInstanz=(int)Bla::bli;

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

14.06.2012, 20:53

Das ist leider richtig.

Werbeanzeige