Clean up code and add more comments.

This commit is contained in:
King_DuckZ 2015-09-16 18:05:18 +02:00
parent 6d11e98499
commit 4b0ca89d7f
4 changed files with 79 additions and 22 deletions

View file

@ -18,25 +18,44 @@
module gameobj;
import derelict.sdl2.sdl;
interface GameObj {
void prepare();
void destroy();
void exec();
bool wants_to_quit() const;
}
class GameObjBase : GameObj {
//We want to provide an interface for all game scenes. A game scene should
//implement some basic methods, such as prepare() and exec(), so the main loop
//can call them.
//We also want our interface to store a renderer. This forces us to declare
//an abstract class instead of an interface, becaues interfaces can't have
//constructors nor properties.
abstract class GameObj {
public:
this (SDL_Renderer* parRenderer) {
m_renderer = parRenderer;
assert(m_renderer);
}
//Derived classes must implement those;
//failure to do so results in a compilation error.
//You need to mark them as "abstract" to let the compiler know there is
//no implementation for them. If you don't mark them as abstract and you
//forget to implement them in the derived game object, you will get a
//linker error instead, and potentially the wrong behaviour as you call them
//from a pointer to the base class.
abstract void prepare();
abstract void dispose();
abstract void exec();
abstract bool wants_to_quit() const;
protected:
SDL_Renderer* renderer() {
//Allow game objects inheriting from GameObj to retrieve the renderer they
//stored through GameObj's constructor. final means it can't be overridden
//(so no v-table entry required for this method); @property means you must
//not use () when you call renderer on an object, eg: r=my_game_obj.renderer
@property final SDL_Renderer* renderer() {
return m_renderer;
}
//Remember: private members can still be accessed from within this module.
//Or, from a different point of view if you come from a c++ background, within
//the gameobj module, everything is friend to everything else.
//One more good reason for putting each class into a different module.
private:
SDL_Renderer* m_renderer;
}

View file

@ -21,8 +21,11 @@ import derelict.sdl2.sdl;
import texture;
import std.stdio;
class GameObjJumping : GameObjBase {
//Don't disable this(), it's disable automatically when you define a
//This is one possible game scene. It will draw a texture on the screen
//and it will report user wants to quit if you press the close button in the
//window decoration.
class GameObjJumping : GameObj {
//Don't disable this(), it's disabled automatically when you define a
//custorm constructor.
//@disable this();
@ -32,28 +35,40 @@ class GameObjJumping : GameObjBase {
m_wants_to_quit = false;
}
//Remember: class destructors are not guaranteed to be called
//deterministically, so all resource-freeing code should rather go in the
//dispose() method.
~this() {
writeln("Destroying GameObj");
//m_texture's type is a struct, so destruction is implicit at this point
}
void prepare() {
override void prepare() {
string texture_name = "bcruiser_normal.bpg";
writeln("Loading texture %s", texture_name);
m_texture = Texture(texture_name, renderer());
}
void destroy() {
override void dispose() {
writeln("GameObjJumping dispose()");
//m_texture.dispose() is what you would normally expect to find here,
//but to demonstrate the use of clear() from the caller code, this
//method is intentionally empty.
//You can verify the program's output as you quit by uncommenting the
//following line:
//m_texture.dispose();
}
//This is the function that is called at every frame
void exec() {
//This is the function that is called at every frame. It represents the
//main loop's body.
override void exec() {
SDL_RenderClear(renderer());
m_texture.draw();
SDL_RenderPresent(renderer());
m_wants_to_quit = do_events();
}
bool wants_to_quit() const {
override bool wants_to_quit() const {
return m_wants_to_quit;
}

View file

@ -21,13 +21,14 @@ import std.string;
import std.conv;
import derelict.sdl2.sdl;
import gameobjjumping;
import std.typecons : scoped; //Only import scoped from std.typecons module
void run_main_loop (ref GameObj parGame) {
void run_main_loop (GameObj parGame) {
parGame.prepare();
do {
parGame.exec();
} while (!parGame.wants_to_quit());
parGame.destroy();
parGame.dispose();
}
int main() {
@ -53,9 +54,31 @@ int main() {
scope(exit) { writeln("Destroying renderer..."); SDL_DestroyRenderer(renderer); }
//This is our game object
GameObj game = new GameObjJumping(renderer);
scope(exit) clear(game);
//Because GameObjJumping implementation purposedly doesn't destroy resources
//in its dispose method (for this example's sake), you must make sure its
//destructor gets called. You have several choices:
//
//1 - call clear() (deprecated and removed in recent D compilers)
// GameObj game = new GameObjJumping(renderer);
// scope(exit) clear(game);
//
//2 - call destroy()
//note that while game.destroy() is semantically equivalent to the
//destroy(game) syntax, you should never write it that way in case game
//provides its own destroy() method. In that case the method will be
//preferred, and you will end up doing something potentially different than
//what you intended to. For the same reason, don't provide a destroy()
//method in your classes.
// GameObj game = new GameObjJumping(renderer);
// scope(exit) destroy(game);
//
//3 - use scope (deprecated)
// scope GameObj game = new GameObjJumping(renderer);
//
//4 - use scoped (no need for new)
// auto game = scoped!GameObjJumping(renderer);
auto game = scoped!GameObjJumping(renderer);
run_main_loop(game);
return 0;
}

View file

@ -44,10 +44,10 @@ public:
~this() {
writefln("Destroying texture %s (%s)", m_filename, m_texture);
destroy();
dispose();
}
void destroy() {
void dispose() {
if (m_texture) {
SDL_DestroyTexture(m_texture);
m_texture = null;