Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] Dziedziczenie polimorficznej funkcji draw

Ostatnio zmodyfikowano 2015-07-17 23:29
Autor Wiadomość
arczi14
Temat założony przez niniejszego użytkownika
[SFML] Dziedziczenie polimorficznej funkcji draw
» 2015-07-17 19:29:03
Witam,
(kod poniżej)
Jak widać klasa SpriteEffect działa na zasadzie że dziedziczy funkcje wirtualną, która odpowiedzialna jest za aktualizacje stanu obiektu. Wszystko ładnie dodaje w klasie engine, która w pętli pobiera z kontenera kolejno obiekty i aktualizuje.

Problem w tym, że nie mogę sobie poradzić aby w ten sam sposób utworzyć rysowanie. aby w main nie pisać osobno draw dla każdego obiektu tylko engine.draw(); który sam w pętli pobierze i wyświetli obiekty.

Niestety nie wiem jak się za to zabrać w SFML funkcję wirtualną draw dziedziczy się z klasy sf::Drawable i sf::Transformable, niestety nie mam pojęcia w jaki sposób to połączyć w spójną całość.

main:

C/C++
headers...
sf::Vector2i size( 128, 128 );
SpriteEffect fire( "fire.png", size, sf::Vector2f( 100, 200 ), 27, SpriteEffect::Repeat, true );
SpriteEffect blood( "blood.png", size, sf::Vector2f( 500, 200 ), 24, SpriteEffect::Repeat, false );

//Inicjalizacja systemu
System sys;

float accumulator = 0.0f;
const float TIME_STEP = 0.016;

if( !sys.create() )
     cout << "Something went wrong!";

sf::RenderWindow * WindowApp = sys.getHandle();

sf::Vector2i mouse;
sf::Clock clock;
Engine engine( * WindowApp );

engine.addObject( fire );
engine.addObject( light );

while( WindowApp->isOpen() )
{
    sf::Event event;
    mouse = sf::Mouse::getPosition( * WindowApp );
   
    while( WindowApp->pollEvent( event ) )
    {
        if( event.type == sf::Event::Closed )
             WindowApp->close();
       
    }
   
    accumulator += clock.restart().asSeconds();
   
    while( accumulator >= TIME_STEP )
    {
        engine.Update( TIME_STEP );
       
        accumulator -= TIME_STEP;
    }
   
    WindowApp->clear( sf::Color::Black );
    WindowApp->draw( fire );
    WindowApp->draw( blood );
    WindowApp->display();
   
}

return EXIT_SUCCESS;
}

Updateable.h
C/C++
#include <SFML/Graphics.hpp>

#ifndef UPDATEABLE_H
#define UPDATEABLE_H

using namespace std;
class Updateable
{
   
public:
    virtual void Update( float DeltaInSeconds ) = 0;
    virtual bool Destroy() const = 0;
   
};

#endif // UPDATEABLE_H

SpriteEffect.h
C/C++
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Updateable.h"

#define PATH "system\\data\\effects\\"

using namespace std;


class SpriteEffect
    : public Updateable
     , public sf::Drawable
     , public sf::Transformable
{
   
public:
   
    enum STATUS
    {
        Once = 0x10A,
        Twice = 0x20A,
        Repeat = 0xA0A
    };
   
    enum Activity
    {
        Active = 0x21D,
        Disactive = 0x21C
    };
   
    // Constructors
    SpriteEffect( string /* Image path(name) */,
    sf::Vector2i /* Solo image size */,
    sf::Vector2f /* Sprite position */,
    int /* FPS speed*/,
    int /* Status */,
    bool /* Object will be automatically deleted from memory */ );
   
   
   
public: // Public function
    void setActive( int /* if true update state */ );
    void outOfContrainet(); // If u do not want to continue updating data in container u can use this function
    sf::Sprite * getElement();
   
protected: // Protected functions
    virtual void Update( float );
   
private: // Private functions
    virtual bool Destroy() const; // Function charge of delete object from memory when bool in constructor is TRUE
    virtual void draw( sf::RenderTarget & target, sf::RenderStates states ) const;
   
   
private: // Private variable & class
   
    int status;
    int frame;
    float FPS_PER_SEC;
    bool autoDelete;
    bool destroyQueue;
    int activity;
    sf::Texture Texture;
    sf::Vector2i spriteSize;
    sf::Vector2f lengthOfTexture;
    sf::Sprite drawableSprite;
    sf::Time time;
   
};

SpriteEffect.cpp

C/C++
#include "../../headers/SpriteEffect.h"

using namespace std;

SpriteEffect::SpriteEffect( string path,
sf::Vector2i _spriteSize,
sf::Vector2f _position,
int _FPS_limit,
int _status,
bool _autoDelete )
    : spriteSize( _spriteSize )
     , status( _status )
     , autoDelete( _autoDelete )
{
   
    if( !Texture.loadFromFile( PATH + path ) )
    {
        cout << "Texture missing! " << endl;
        exit( 0 );
    }
   
    FPS_PER_SEC = 1 / float( _FPS_limit );
    frame = 0;
    activity = Activity::Active;
    destroyQueue = false;
   
    // Counting how many sprites we have to draw
    lengthOfTexture.x =( Texture.getSize().x / spriteSize.x );
    lengthOfTexture.y =( Texture.getSize().y / spriteSize.y );
   
    // Setting first image to sprite.
    drawableSprite.setTexture( Texture );
    drawableSprite.setTextureRect( sf::IntRect( 0, 0, spriteSize.x, spriteSize.y ) );
   
    // Setting position of sprite
    drawableSprite.setPosition( _position );
   
   
}


void SpriteEffect::Update( float dT )
{
    if( activity != Activity::Active )
         return void();
   
    time += sf::seconds( dT );
    int x, y;
   
    if( time.asSeconds() >= FPS_PER_SEC )
    {
        // Reset time
        time = sf::seconds( 0 );
       
        y = int( frame / lengthOfTexture.y );
        x = frame % int( lengthOfTexture.x );
       
        drawableSprite.setTextureRect( sf::IntRect( x * spriteSize.x, y * spriteSize.y,
        spriteSize.x, spriteSize.y ) );
       
        //What to do if texture get to the end
        if(( lengthOfTexture.y * lengthOfTexture.x ) - 1 <= frame )
        {
            switch( status )
            {
            case STATUS::Repeat:
                {
                    frame = 0;
                }
                break;
               
            case STATUS::Once:
                {
                   
                    activity = Activity::Disactive;
                    frame = 0;
                    time = sf::seconds( 0 );
                   
                    if( autoDelete )
                         destroyQueue = true;
                   
                }
                break;
               
            }
        }
       
        // After update increase frame
        frame++;
       
    }
}

void SpriteEffect::draw( sf::RenderTarget & target, sf::RenderStates states ) const
{
    if( activity == Activity::Active )
         target.draw( drawableSprite );
   
}

// If object is not currently in use deleting from container.
bool SpriteEffect::Destroy() const
{
    return destroyQueue;
}

void SpriteEffect::setActive( int _activity )
{
    if( _activity == Activity::Active ) {
       
        activity = _activity;
        return;
    }
   
    if( _activity == Activity::Disactive )
         activity = _activity;
   
}

sf::Sprite * SpriteEffect::getElement()
{
    return & drawableSprite;
}

void SpriteEffect::outOfContrainet()
{
    destroyQueue = true;
}

engine.cpp

C/C++
#include "..\headers\Engine.h"

using namespace std;

Engine::Engine( sf::RenderWindow & win )
{
    WindowApp = & win;
}

/*void Engine::draw()
{
    for( auto It = allObjectsContainer.begin(); It != allObjectsContainer.end(); It++ )
    {
        WindowApp->draw( ( * It ) );

    }
}*/

void Engine::Update( float deltaInSeconds )
{
    for( auto It = allObjectsContainer.begin(); It != allObjectsContainer.end(); It++ )
    {
        ( * It )->Update( deltaInSeconds );
       
        if(( * It )->Destroy() )
             allObjectsContainer.erase( It );
       
    }
   
}

void Engine::addObject( Updateable & object )
{
    allObjectsContainer.push_back( & object );
   
}

engine.h

C/C++
#include <SFML/Graphics.hpp>
#include "Updateable.h"
#include "SpriteEffect.h"
#include <vector>

using namespace std;

class Engine
{
   
public:
   
    Engine( sf::RenderWindow & win );
    void Update( float );
    void addObject( Updateable & object );
    void draw();
   
private:
    vector < Updateable *> allObjectsContainer;
    sf::RenderWindow * WindowApp;
};
P-134844
Patrycjerz
» 2015-07-17 22:30:07
Nie za bardzo chce mi się analizować ten kod :(, ale spróbuję odpowiedzieć.
Stwórz sobie klasę abstrakcyjną dla wszystkich obiektów w grze i zaimplementuj w niej metody update, draw itp. Potem z klasy Engine wywołasz np. draw bez problemu.

I jeszcze jedno. Nie rozumiem m.in. tego:
C/C++
( * It )->Update( deltaInSeconds );

Przecież tą gwiazdką wyłuskałeś obiekt z wskaźnika i możesz używać normalnie:
C/C++
( * It ).Update( deltaInSeconds );
//lub
It->Update( deltaInSeconds );

PS: Jeśli bredzę, to proszę o poprawę, też się uczę :)
P-134853
arczi14
Temat założony przez niniejszego użytkownika
» 2015-07-17 22:44:41
Można by tak zrobić ale co by miało być elemtem przekazywamym w draw gdyż sfml ma.wiele obiektów do wyświetlenia. Trzeba funkcje draw rozwiązać Przy pomocy wbudowanej w sfml klasy drawable która daje wirtualna funkcje draw. I na.tym polega problem
P-134854
Patrycjerz
» 2015-07-17 23:29:32
Jeśli chcesz użyć dziedziczenia dla użycia tej metody draw z SFML, to możesz stworzyć w klasie Engine wektor obiektów twojej gry oraz metodę do rysowania jego elementów. Po prostu we wnętrzu tej metody będzie jedna pętla for, która będzie wywoływać wielokrotnie metodę draw dla okna i przypisująca do argumentu twoje obiekty. Oczywiście to są moje luźne przemyślenia, może można to zrobić lepiej :)

Oto przykładowy kod:
C/C++
void Engine::draw( sf::RenderWindow & WindowApp )
{
    for( auto It = allObjectsContainer.begin(); It != allObjectsContainer.end(); It++ )
    {
        WindowApp.draw( * It );
    }
}
P-134856
« 1 »
  Strona 1 z 1