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; module gameobj;
import derelict.sdl2.sdl; import derelict.sdl2.sdl;
interface GameObj { //We want to provide an interface for all game scenes. A game scene should
void prepare(); //implement some basic methods, such as prepare() and exec(), so the main loop
void destroy(); //can call them.
void exec(); //We also want our interface to store a renderer. This forces us to declare
bool wants_to_quit() const; //an abstract class instead of an interface, becaues interfaces can't have
} //constructors nor properties.
abstract class GameObj {
class GameObjBase : GameObj {
public: public:
this (SDL_Renderer* parRenderer) { this (SDL_Renderer* parRenderer) {
m_renderer = parRenderer; m_renderer = parRenderer;
assert(m_renderer); 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: 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; 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: private:
SDL_Renderer* m_renderer; SDL_Renderer* m_renderer;
} }

View file

@ -21,8 +21,11 @@ import derelict.sdl2.sdl;
import texture; import texture;
import std.stdio; import std.stdio;
class GameObjJumping : GameObjBase { //This is one possible game scene. It will draw a texture on the screen
//Don't disable this(), it's disable automatically when you define a //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. //custorm constructor.
//@disable this(); //@disable this();
@ -32,28 +35,40 @@ class GameObjJumping : GameObjBase {
m_wants_to_quit = false; 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() { ~this() {
writeln("Destroying GameObj"); 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"; string texture_name = "bcruiser_normal.bpg";
writeln("Loading texture %s", texture_name); writeln("Loading texture %s", texture_name);
m_texture = Texture(texture_name, renderer()); 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 //This is the function that is called at every frame. It represents the
void exec() { //main loop's body.
override void exec() {
SDL_RenderClear(renderer()); SDL_RenderClear(renderer());
m_texture.draw(); m_texture.draw();
SDL_RenderPresent(renderer()); SDL_RenderPresent(renderer());
m_wants_to_quit = do_events(); m_wants_to_quit = do_events();
} }
bool wants_to_quit() const { override bool wants_to_quit() const {
return m_wants_to_quit; return m_wants_to_quit;
} }

View file

@ -21,13 +21,14 @@ import std.string;
import std.conv; import std.conv;
import derelict.sdl2.sdl; import derelict.sdl2.sdl;
import gameobjjumping; 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(); parGame.prepare();
do { do {
parGame.exec(); parGame.exec();
} while (!parGame.wants_to_quit()); } while (!parGame.wants_to_quit());
parGame.destroy(); parGame.dispose();
} }
int main() { int main() {
@ -53,9 +54,31 @@ int main() {
scope(exit) { writeln("Destroying renderer..."); SDL_DestroyRenderer(renderer); } scope(exit) { writeln("Destroying renderer..."); SDL_DestroyRenderer(renderer); }
//This is our game object //This is our game object
GameObj game = new GameObjJumping(renderer); //Because GameObjJumping implementation purposedly doesn't destroy resources
scope(exit) clear(game); //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); run_main_loop(game);
return 0; return 0;
} }

View file

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