[C++] SDL2-basiertes Framework

Präsentation und Organisation von eigenen Projekten
Antworten
Glocke
Beiträge: 332
Registriert: Fr Okt 26, 2012 8:39 am

[C++] SDL2-basiertes Framework

Beitrag von Glocke » Do Feb 20, 2014 4:54 pm

Hi,

vor einiger Zeit hatte ich an einem OO-Wrapper sdlApp für SDL geschrieben und diesen vor kurzem auf SDL 2 migriert. Bisher bietet er natürlich nur ein Interace für eine Untermenge an Funktionen ^^ Hier habe ich mal einige Features (die btw über reines SDL hinaus gehen), die das Framework bietet. Eine genaue Info findet ihr auf der GitHub-Seite [1]
  • Implementierung eines Zustandsautomaten (I)
  • Begrenzung der Framerate
  • Unterstützung Framerate-unabhängiger Programmlogik
  • Abstrahierte Eingabeunterstützung für Maus und Tastatur (II)
  • Threadsicheres Resource-Caching für Images, Sounds, Music und Fonts
  • Schnelles Font-Rendering mit Glyph-Atlas (III)
  • Geometrische Objekte Punkt und Rechteck
  • Sprites und Animationen (IV)
(Bemerkungen siehe unten)

Dependencies
  • C++11 bzw. die abgespeckte C++0x-Version von GGC 4.6.4
  • SDL 2, inklusive die SDL_image, SDL_mixer und SDL_ttf
Lizenz
BSD 2 - siehe GitHub [3]

Bemerkungen
Seit SDL2 ist die Verwendung mehrerer Fenster (und damit Renderkontexte) möglich. Allerdings können Texturen nur über die Renderer dargestellt werden, über die sie geladen wurden. Da ich das Feature bisher noch nicht gebraucht habe, habe ich mich nicht weiter mit dem Problem gefasst.

Um das ganze etwas wirken zu lassen, hier ein Beispiel [2]. Ich verzichte hier absichtlich auf ausschweifende Kommentare. Eines der Ziele, die ich mit sdlApp verfolge, ist eine möglichst intuitive Usability des Frameworks ;-)

Deklaration eines Zustands (siehe I)

Code: Alles auswählen

#include <iostream>
#include <cstdlib>
#include <ctime>

#include <sdlApp/sdlApp.hpp>

using namespace sdlApp;

class DemoState: public State {
    protected:
        Animation ani;
        Sprite sprite;
        Text txt, fps;
        
        float s, r;
        
        Sound snd;
        Music mus;

        bool onKeyPress(Event & event);
        bool onMouseClick(Event & event);
        bool onWindowEvent(Event & event);

    public:
        DemoState();
        virtual ~DemoState();

        void logic(Time const & elapsed);
        void render(Time const & elapsed);
};
Implementierung von DemoState

Code: Alles auswählen

DemoState::DemoState()
    : State()
    , ani("example/animation.png", 250)
    , sprite("example/sprite.png")
    , txt()
    , snd("example/sound.ogg")
    , mus("example/music.ogg")
    , s(1.f)
    , r(0.f) {
    this->ani.setSize({48, 56});
    this->txt.text = "Display Driver\nis " + Engine::getVideoDriver();
    std::cout << this->txt.getSize().toString() << "\n";
    this->fps.color = Color::Grey;
    this->fps.alpha = 200;
}

DemoState::~DemoState() {
}

bool DemoState::onKeyPress(Event & event) {
    if (event.key == SDLK_m) {
        if (this->mus.isPlaying()) {
            this->mus.pause();
        } else {
            this->mus.play();
        }
    }
}

bool DemoState::onMouseClick(Event & event) {
    if (event.button == LEFT_BUTTON) {
        this->snd.play();
    }
}

bool DemoState::onWindowEvent(Event & event) {
    if (event.window == CLOSE_EVENT) {
        this->quit = true;
    }
}

void DemoState::logic(Time const & elapsed) {
    Point d;
    if (Keyboard::isDown(SDLK_UP))    { d.y = -1; }
    if (Keyboard::isDown(SDLK_DOWN))  { d.y = 1; }
    if (Keyboard::isDown(SDLK_LEFT))  { d.x = -1; }
    if (Keyboard::isDown(SDLK_RIGHT)) { d.x = 1; }
    this->sprite.screenPos += d * elapsed / 5;
    this->ani.step(elapsed);
    if (this->ani.isFinished()) {
        // restart animation with random row
        int i = (int)(3.0f * std::rand() / RAND_MAX);
        this->ani.setOffset({0, i});
        this->ani.restart();
        // scale animation
        if (this->s < 2.f) {
            s += 0.05f;
        } else {
            s = 0.1f;
        }
        // rotate animation
        if (this->r < 360.f) {
            this->r += 5.f;
        } else {
            this->r = 0.f;
        }
    }
    this->fps.text = std::to_string(Engine::clock.getFps());
}

void DemoState::render(Time const & elapsed) {
    this->sprite.render();
    this->ani.render({250, 50}, this->s, this->r, true); // render centered
    this->txt.render({25, 25});
    this->fps.render({0, 0});
}
Einbettung in main()

Code: Alles auswählen

int main() {
    std::srand(std::time(0)); // see logic()

    // enable part of ASCII code for font rendering
    for (std::uint16_t c = '!'; c <= '~'; c++) {
        Engine::supportedChars.insert(c);
    }
    // initialize engine and create a primary window
    Engine::init();
    Engine::window.primary = new Window("sdlApp demo", false, {640, 480});
    // just some output
    std::cout << "Application directory: " << Engine::getAppDir() << "\n"
        << "Preference directory: " << Engine::getPrefDir() << "\n";
    // now (we have a renderer) we can preload the default font
    Engine::default_font.name = "example/font.ttf";
    Engine::default_font.size = 24;
    Engine::font.preload(Engine::default_font);
    // just push a state to the state machine and run
    Engine::state.enter(new DemoState());
    Engine::run();
    Engine::quit();

    return 0;
}
Bemerkungen:
(I) Implementierung eines Zustandsautomaten: Alle Komponenten werden in einem State-Objekt hinterlegt. Dieses realisiert mittels logic- und render-Methoden die Darstellung des Zustands. Der Main-Loop der Anwendung wird dabei von der statischen Engine-Klasse übernommen.

(II) Abstrahierte Eingabeunterstützung für Maus und Tastatur: Alle Maus- und Tastatur-Ereignisse können durch Event-Callbacks behandelt werden. Verfügt ein State über keine Callback für ein Event, wird es ignoriert. Allerdings wird im Hintergrund der Zustand von Maus und Tastatur (d.h. welche Taste gerade gedrückt wurde / zur Zeit gedrückt gehalten wird / gerade losgelassen wurde / nicht gedrückt ist) gespeichert, so dass dies auch ohne die Callback-Methode abfragbar ist.

(III) Schnelles Font-Rendering mit Glyph-Atlas: Um bei jeder Änderung eines Textes (z.B. FPS-Sprite) die Textur nicht neu zu erzeugen und auf die Grafikkarte zu laden, werden die Glyphen vorgerendert und ein Glyph-Atlas (pro Schriftart) auf der Grafikkarte hinterlegt. Dies ist zur Zeit für TrueType Fonts implementiert.

(IV) Sprites und Animationen: Animationen verwenden dabei eine Grafik, auf der vertikal verschiedene Animationsarten und horizontal verschiedene Frames angeordnet sind.



So, das war's erstmal. Ich poste das hier in der Hoffnung auf konstruktives Feedback und den ein oder anderen Tester.

Vielleicht noch einen Ausblick, was ich mit dem Framework vor habe: Auf Basis von sdlApp habe ich eine kleine Widget-Lib (sdlGui, siehe GitHub [4]) geschrieben. In den nächsten Wochen werde ich die Arbeit an einem 2D Dungeon Crawler auf Basis von sdlApp und sdlGui beginnen. Denkbar ist auch die Erweiterung von sdlApp um einen OpenGL-Kontext.

LG Glocke

---------

[1] https://github.com/cgloeckner/sdlApp
[2] https://github.com/cgloeckner/sdlApp/bl ... e/main.cpp
[3] https://github.com/cgloeckner/sdlApp/bl ... er/COPYING
[4] https://github.com/cgloeckner/sdlGui

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: [C++] SDL2-basiertes Framework

Beitrag von Architekt » Sa Mai 24, 2014 12:22 pm

Hallo Glocke,
Schön das es noch weitere SDL Jünger gibt. ;)

Ich arbeite an einem ähnlichen Projekt: Ein Framework welches auf der SDL 2 und OpenGL basiert, genannt Simple Graphic / Game Library.
Es fehlt noch viel an Kommentierung und ist noch nicht vollends stable, aber eventuell kann man sich ja mal austauschen und Kritik üben. Der Link zu meinem Framework: https://github.com/Dgame/sgl

Antworten