Arkanoid 3D krok po kroku III

02.08.2010 - Olgierd Humeńczuk
TrudnośćTrudność
1
2
3
4
5
6
7
8
9
10
11
        /**
         * @brief Wyswiela wszystkich aktualnie przechowywanych w scenie aktorow
         */
        void renderAllObjects( void )
        {
            std::for_each( sceneObjects.begin(), sceneObjects.end(),
                std::tr1::bind(
                      &sceneManager::renderObject
                    , this
                    , std::tr1::placeholders::_1 ) );
        }

Metoda renderAllObject służy wyświetleniu wszystkich aktualnie przechowywanych w mapie aktorów. Korzysta w prosty sposób z metody renderObject.

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
        /**
         * @brief Wywoluje podany w parametrze szablonu funktor
         * dla kazdego aktora w scenie w celu jej aktualizacji.
         */
        template< typename updaterType >
        void updateScene( void )
        {
            updaterType updater;
            std::for_each( sceneObjects.begin(), sceneObjects.end(),
                std::tr1::bind( 
                      &updaterType::operator()
                    , &updater
                    , std::tr1::placeholders::_1 ) );
        }
 
        /**
         * @brief Wywoluje podany funktor dla kazdego elementu w scenie
         * w celu sprawdzenia kolizji miedzy obiektami.
         */
        template< typename collSolver >
        void solveCollisions( void )
        {
            collSolver solver;
            const sceneObjectsContainer& objects = sceneObjects;
 
            std::for_each( sceneObjects.begin(), sceneObjects.end(),
                std::tr1::bind(
                      &collSolver::operator()
                    , &solver, std::tr1::placeholders::_1
                    , this
                    , std::tr1::cref( objects ) ) );
        }

Dwie powyższe metody - updateScene oraz solveCollisions są prawie identyczne, jeżeli chodzi o sposób działania. Mają za zadanie wywołać podany w parametrze szablonu tzw. funktor dla każdego aktora w scenie. Od implementacji danego funktora będzie zależeć wykrywanie kolizji i aktualizacja sceny. Dzięki takiej konstrukcji, w łatwy sposób będziemy mogli testować różne rozwiązania.

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:
        /**
         * @brief Metoda pomocnicza wyswietla pojedynczego aktora
         * @param value krotka zawierajacego aktora ktory ma byc wyswietlony
         */
        void renderObject( const sceneObjectsContainer::value_type& value )
        {
            const baseSceneObject& object = value.second;
 
            glPushMatrix();
 
            glTranslatef( object.position.x, object.position.y, object.position.z );
 
            glVertexPointer( 3, GL_FLOAT, sizeof( vertexes::value_type )
                , &objectsMesh[ object.objectType ][ 0 ] );
            glNormalPointer( GL_FLOAT, sizeof( vertexes::value_type )
                , &objectsMesh[ object.objectType ][ 0 ].normal );
 
            setMaterial(
                  &objectsMaterial[ object.objectType ].ambient_col.x
                , &objectsMaterial[ object.objectType ].diffuse_col.x
                , &objectsMaterial[ object.objectType ].specular_col.x
                , objectsMaterial[ object.objectType ].shiness );
 
            glDrawArrays( GL_QUADS, 0, objectsMesh[ object.objectType ].size() );
 
            glPopMatrix();
        }

Kod metody renderObject jest przeniesiony z funkcji main i wzbogacony o wywołania funkcji glPushMatrix i glPopMatrix tak, aby każdy obiekt przesunięty został w przestrzeni niezależnie od reszty.

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
        /**
         * @brief Metoda pomocnicza, wypelnia tablice materialow
         */
        void initializeMaterials( void )
        {
            objectsMaterial[ OBJECT_TYPE_PLANE ] = T::planeMaterial;
            objectsMaterial[ OBJECT_TYPE_WALL_N ] = T::wallNMaterial;
            objectsMaterial[ OBJECT_TYPE_WALL_EW ] = T::wallEWMaterial;
            objectsMaterial[ OBJECT_TYPE_BALL ] = T::ballMaterial;
            objectsMaterial[ OBJECT_TYPE_PADDLE ] = T::paddleMaterial;
            objectsMaterial[ OBJECT_TYPE_BRICK ] = T::brickMaterial;
        }
 
        /**
         * @brief Metoda pomocnicza wypelnia tablice siatek
         */
        void initializeMeshes( void )
        {
            objectsMesh[ OBJECT_TYPE_PLANE ] 
                = createPlane( T::planeWidth, T::planeHeight );
            objectsMesh[ OBJECT_TYPE_WALL_N ] 
                = createRectangular( T::wallNWidth, T::wallNHeight, T::wallNThickness );
            objectsMesh[ OBJECT_TYPE_WALL_EW ] 
                = createRectangular( T::wallEWWidth, T::wallEWHeight, T::wallEWThickness );
            objectsMesh[ OBJECT_TYPE_BALL ] 
                = createSphere( T::ballRadius, T::ballSlices, T::ballSegments );
            objectsMesh[ OBJECT_TYPE_PADDLE ] 
                = createRectangular( T::paddleWidth, T::paddleHeight, T::paddleThickness );
            objectsMesh[ OBJECT_TYPE_BRICK ] 
                = createRectangular( T::brickWidth, T::brickHeight, T::brickThickness );
        }

Metody inicjalizujące tablice siatek i materiałów aktorów. Metody te pobierają dane do inicjalizacji z typu T podanego w parametrze szablonu klasy sceneManager.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    private:
        /// identyfikatory obiektow tworzone z wartości przechowywanej
        /// w tej zmiennej
        int32_t ids;
 
        /// kontener do przechowywania obiektów sceny - aktorow
        sceneObjectsContainer sceneObjects;
 
        /// tutaj bedziemy pamietac material dla kazdego typu obiektow
        material objectsMaterial[ OBJECT_TYPE_COUNT ];
 
        /// tutaj bedziemy pamietac siatke dla kazdego typu obiektu
        vertexes objectsMesh[ OBJECT_TYPE_COUNT ];
};

Oto i cała klasa sceneManager. Spróbujmy użyć jej w celu wyświetlenia planszy, na której umieścimy kulę oraz rakietkę sterowaną przez gracza.

Najpierw zdefiniujemy obiekt, który będzie posiadał opis wymiarów i materiałów aktorów gry.

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
/**
 * @brief Definicja wymiarow i materialow aktorow uzywanych w grze
 */
struct defObj
{
    /// deklaracja rozmiarow kuli
    static const GLfloat ballRadius;
    static const GLint ballSlices;
    static const GLint ballSegments;
 
    /// deklaracja rozmiarow plaszczyzny
    static const GLfloat planeWidth;
    static const GLfloat planeHeight;
 
    /// deklaracja rozmiarow sciany pn
    static const GLfloat wallNWidth;
    static const GLfloat wallNHeight;
    static const GLfloat wallNThickness;
 
    /// deklaracja rozmiarow sciany w/z
    static const GLfloat wallEWWidth;
    static const GLfloat wallEWHeight;
    static const GLfloat wallEWThickness;
 
    /// deklaracja rozmiarow rakietki
    static const GLfloat paddleWidth;
    static const GLfloat paddleHeight;
    static const GLfloat paddleThickness;
 
    /// deklaracja rozmiarow cegly
    static const GLfloat brickWidth;
    static const GLfloat brickHeight;
    static const GLfloat brickThickness;
 
    /// deklaracja materialow aktorow gry
    static const material planeMaterial;
    static const material wallNMaterial;
    static const material wallEWMaterial;
    static const material ballMaterial;
    static const material paddleMaterial;
    static const material brickMaterial;
};
 
/// definicja rozmiarow kuli
const GLfloat defObj::ballRadius = 1.4f;
const GLint defObj::ballSlices = 20;
const GLint defObj::ballSegments = 20;
 
.
.
.
 
// delej definicje wymiarów reszty obiektów
 
const material defObj::planeMaterial = material(
          vector4f( .0f, .1f, .0f, 1.f )
        , vector4f( .0f, .6f, .0f, 1.f )
        , vector4f( .0f, .6f, .0f, 1.f )
        , 20.0f );
 
.
.
.
// dalej definicja materialow pozostalych aktorow

Obiekt ten podamy jako parametr szablonu klasy sceneManager i użyjemy go w metodach inicjalizujacych materiały i siatki aktorów. Zmodyfikujmy zatem kod funkcji main i wykorzystajmy napisane przez nas obiekty.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    typedef sceneManager< defObj > arkanoidSceneManager;
    arkanoidSceneManager my_manager;
 
    my_manager.addSceneObject( OBJECT_TYPE_PLANE, vector3f( .0f, .0f, .0f ) );
 
    int32_t ballId = my_manager.addSceneObject(
              OBJECT_TYPE_BALL, vector3f( 0.0f, defObj::ballRadius, 30.0f )
            , vector3f( 1.0f, 0.0f, -1.0f ).normalize(), 0.5f );
    int32_t paddleId = my_manager.addSceneObject(
            OBJECT_TYPE_PADDLE, vector3f( .0f, defObj::paddleHeight * 0.5f, 39.0f ) );
    int32_t wallNId = my_manager.addSceneObject(
            OBJECT_TYPE_WALL_N, vector3f( .0f, defObj::wallNHeight * 0.5f, -42.0f ) );
    int32_t wallEId = my_manager.addSceneObject(
            OBJECT_TYPE_WALL_EW, vector3f( -52.0f, defObj::wallEWHeight * 0.5f, 0.0f ) );
    int32_t wallWId = my_manager.addSceneObject(
            OBJECT_TYPE_WALL_EW, vector3f( 52.0f, defObj::wallEWHeight * 0.5f, 0.0f ) );
    int32_t brickId = my_manager.addSceneObject(
            OBJECT_TYPE_BRICK, vector3f( 0.0f, defObj::brickHeight * 0.5f, -10.0f ) );

Dodane przez nas linie kodu inicjalizują zarządcę sceny oraz dodają aktorów, którzy zostaną wyświetleni po użyciu metody renderAllObjects klasy sceneManager. Dodajmy zatem wywołanie tej metody. Nowa pętla przetwarzania naszej gry prezentuje się następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
        while( processInput() )
        {
            glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
            glMatrixMode( GL_MODELVIEW );
            glLoadIdentity();
 
            glPushMatrix();
 
            gluLookAt( 0.0f, 110.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f );
            {
                //dane światła
                GLfloat position[] = { 0.0f, 100.0f, 0.0f, 1.0f };
                GLfloat ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f };
                GLfloat diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };
                GLfloat specular[] = { 0.8f, 0.8f, 0.8f, 1.0f };
 
                setPointLight( GL_LIGHT0, position, ambient, diffuse, specular );
            }
 
            my_manager.renderAllObjects();
 
            glPopMatrix();
            SDL_GL_SwapBuffers();
        }    

W stosunku do poprzedniej wersji, otrzymaliśmy znacznie bardziej przejrzystą i zwięzłą implementację, dzięki użyciu klasy zarządcy sceny. Teraz zajmiemy się aktualizacją aktorów sceny.

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com