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)
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
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);
};
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});
}
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;
}
(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