mirror of
https://bitbucket.org/King_DuckZ/jumping-in-d.git
synced 2024-10-30 02:49:01 +00:00
Clean up code and add more comments.
This commit is contained in:
parent
6d11e98499
commit
4b0ca89d7f
4 changed files with 79 additions and 22 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
31
src/main.d
31
src/main.d
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue