Jump to content
  • Advertisement

D Bits

  • entries
    32
  • comments
    46
  • views
    127471

About this blog

Game development with the D Programming Language.

Entries in this blog

 

Dolce Progress and A Word on Mixins

My time has been limited lately, but the core of Dolce continues to evolve. I think it's converging into something fairly decent. I'm still not completely happy with it, but I believe I'm most of the way there. As usual, I'm getting wild ideas for new projects. Like a renewed interest in building a 2D library from scratch in D. Or a more generic framework with pluggable backends for Allegro, SDL, SFML, and GLFW. You know, like I explicitly said at the beginning I had no interest in making. This time, though, I refuse to let myself get distracted. So I'm digging my heels in.

Recently, I've added event handling, mapping keycodes or mouse button ids to delegates. The first pass is usable, but it can be improved a good deal. D's delegates are great for this sort of thing. But delegates aren't what I want to talk about today.

Until now, my test app has only been using the keyboard. With the inclusion of the event handler, I added mouse support. While testing it out, I kept getting a puzzling result. Allegro has no constants for mouse buttons. They are numbered from 1 to whatever the maximum number is (which you can find out via al_get_mouse_num_buttons()). But my event handler kept getting button 0 passed to it, no matter which button I pushed. al_get_mouse_num_buttons was correctly reporting 3 (I dumped my 8-button gaming mouse some time ago). After several minutes of digging around, I started to suspect my DerelictAllegro binding. Sure enough, I had left out 4 fields from the struct declaration. I patched that up and all is well.

Looking at the binding source reminded me of a D feature that would be worth mentioning here. In my last post, I briefly mentioned D's string mixins. There is another type of useful mixin in D that I've made use of in the Allegro binding: the template mixin.

Allegro's event structs are all declared with the following macro at the top:


#define _AL_EVENT_HEADER(srctype) \
ALLEGRO_EVENT_TYPE type; \
srctype *source; \
double timestamp;


So the mouse event structure looks like this:


typedef struct ALLEGRO_MOUSE_EVENT
{
_AL_EVENT_HEADER(struct ALLEGRO_MOUSE)
struct ALLEGRO_DISPLAY *display;
int x, y, z, w;
int dx, dy, dz, dw;
unsigned int button;
float pressure;
} ALLEGRO_MOUSE_EVENT;


D has no #defines, and no preprocessor at all. But this C idiom can easily be replicated with template mixins. Here's what I did in the DerelictAllegro binding:


// First, declare the template that you intend to use as a mixin.
template _AL_EVENT_HEADER(T)
{
ALLEGRO_EVENT_TYPE type;
T* source;
double timestamp;
}

// Then, mix it in.
struct ALLEGRO_MOUSE_EVENT
{
mixin _AL_EVENT_HEADER!(ALLEGRO_MOUSE);
ALLEGRO_DISPLAY* display;
int x, y, z, w;
int dx, dy, dz, dw;
uint button;
float pressure;
}


Simple. String mixins could also be used here.


// You would want to use a compile time function with a signature something like this
string allegroEventHeader(string type) { ... }

// Then call it like so:
mixin(allegroEventHeader("ALLEGRO_MOUSE"));


Personally, I prefer template mixins in situations like this.

Take a look at the template mixin documentation over at the shiny, new d-programming-language.org website for more info.

Aldacron

Aldacron

 

Operator Overloading in D

I didn't add much of anything new to Dolce in the past week. The time I had to work on it was mostly spent refactoring. I'm patching this thing together haphazardly as I go along. I have a good idea of what I want it to do, but how to organize it all wasn't clear from the beginning. Package hierarchy, structs vs classes, module content, and so on. My little test app has served two functions: making sure it all works and understanding how it will be used. It's the latter that led to the refactoring.

So now that the plumbing is done and in working order, it's time to start looking at game-related utility code. For that, my little test app is woefully inadequate. So I'll be porting the a5teroids demo that ships with the Allegro package. This will get me started. But before I begin the port, I'm looking at two additions: entities and a math package/module. Since entities will likely need the math, I'm starting with that.

I'm not yet sure if I want a whole new package for the math stuff or just a single module, but for now I'm going with the former. The first module in the math package provides an implementation of a 2D vector. It's been a while since I've done any operator overloading with D, and I had completely forgotten how different it is between D1 and D2.

In D1, operator overloading is similar to the way it works in C++. The biggest difference is that instead of overloading by symbol, you overload by purpose. The following overloads the '+' operator.


struct Foo
{
int x;

// Foo + int
int opAdd(int i)
{
return x + i;
}

// int + Foo
int opAdd_r(int i)
{
return i + x;
}
}


The rationale behind naming overloads based on purpose, rather than the symbol, is that it encourages programmers not to change the meaning of the symbol. This way you can expect Foo + Bar to actually do some sort of addition, rather than an unrelated operation. I thought it was a pretty good idea when I first saw it. Whether or not it actually meets that goal in practice I couldn't say. Regardless, I'm not using D1, and D2 has changed this dramatically. For more on operator overloading in D1, see the D1 documentation.

In D2, most (not all) operator overloads are templated. You have a few basic templates that can be constrained based on the type of operator you want to overload. opUnary is for unary operations, such as negation. opBinary is for binary operators, such as addition. You can implement the templates once for each operator, or combine several into one instance, using template constraints in both cases. The following example demonstrates..


bool isMathOp(string op)
{
return op == "+" || op == "-" || op == "*" || op == "/";
}

struct Foo
{
int x;

// Overload the negation operator '-'
int opUnary(string op)() if(op == "-")
{
return -x;
}

// Overload addition, subtraction, multiplication and division for the case of Foo op int.
int opBinary(string op)(int i) if(isMathOp(op))
{
mixin("return x" ~ op ~ "i;");
}

// Overload the same for the case of int op Foo.
int opBinaryRight(string op)(int i) if(isMathOp(op))
{
mixin("return i" ~ op ~ "x;");
}
}


This code example uses two other nifty features of D. isMathOp is a function that qualifies for execution at compile time (the reasons for which I may talk about in a future post). Compile-time function execution (CTFE) is an awesome tool for generating code. In this case, it's not particularly needed, but it does make the template constraints more concise. Plus, I wanted to show off.

The second feature is string mixins. A string mixin basically inserts the given string into the code during compilation. In this case, it is generating strings of code like "x + i" or "x - i", for each template instance, using the append/concat operator. It's possible to call CTFE functions inside a mixin if you need to. That's a technique I use in Derelict to ensure compatibility between D1 and D2 (Dolce is D2 only, but Derelict has to support both).

So you can use CTFE and mixins, together with template constraints, to cut down the amount of code you need to implement in order to overload operators in D2. There are several more options for operator overloading, including some that aren't template-based. You can read more about in the D2 documentation.

Now, back to work on Dolce.

Aldacron

Aldacron

 

D Arrays

Every D programmer loves D's arrays. All of them. I challenge you to find one who doesn't. Particularly, it's the ability to slice and manipulate arrays without worrying about memory that really pulls people in.

In my previous post, I talked about Dolce's game screens and the stack-based screen manager. Internally, the stack that the screen manager uses is not a custom-built stack class, nor any sort of container from an existing library. It's an array. You can implement a stack quite easily with a D array.


// Declare an empty dynamic array to use as your stack.
Item[] stack;

// To push an item onto the stack, use the array append/concat operator.
stack ~= something;

// To pop, simply decrease the length of the array.
stack.length -= 1;

// What if we have a queue instead of a stack and want to take the first item?
// First, save the item at the head of the queue.
auto item = queue[0];

// Then reassign the array to a slice of itself, starting from index #1 to the last index.
// The $ is a substitute for the length property of the array. The first number in a slice
// is inclusive, the second exclusive. So the resulting slice below will contain all elements
// from queue[1] to queue[queue.length - 1].
queue = queue[1 .. $];


D's arrays are quite powerful. In addition to being arrays, they are also ranges, which is something that would take more than a simple post to explain. I don't quite fully grok them myself yet. But, when used in conjunction with the std.algorithm module, there's a great deal of power behind them.

You can read more about D's arrays in this article on slicing in D by Steven Schveighoffer. Also, see the documentation for the std.range module to get an idea of what ranges are.

But wait, there's more! The registry the screen manager uses is a built-in associative array. While there are some more sophisticated hashmap implementations out there for when you really need them, the built-in AAs will cover a lot of your use cases.


// Declare an AA that uses strings as keys.
Item[string] registry;

// Add an item.
registry["foo"] = myItem;

// Remove an item.
registry.remove("foo");

// Test if an item exists in the AA .
// The in operator returns a pointer.
auto item = key in registry;
if(item !is null)
// Do something

// Efficiently iterate all the values via a delegate
foreach(val; registry.byValue())
writeln(val);

// Or the keys
foreach(key; registry.byKey())
writeln(key);

// But don't try to remove anything while iterating the delegates as above. Instead,
// iterate over an array of keys or values using the .keys or .values properties.
foreach(key; registry.keys)
{
auto item = registry[key];
if(item.removeMe)
registry.remove(key);
}



D's arrays and AAs will get you farther than those of C++ or Java. But, when you need more, there is also a range-based container module, std.container, which will eventually hold a number of container types. Also, as an alternative, Steven Schveighoffer's DCollections library is quite awesome.

Aldacron

Aldacron

 

Game Screens

The concept of a game screen is a fairly common one in game engines and frameworks these days. Usually it's an interface or base class that defines a lifecycle for what once upon a time was commonly known as a game state. It eliminates the need for those ugly if-else trees, or long switch statements, that used to take up a large portion of the game loop in the good old days. Each game state, or screen, can be encapsulated in its own class and activated as needed. And often there is a manager that handles initialization, shutdown, and calling the tick method (or update/render methods in some implementations) of the screen object at regular intervals. A simple game screen manager might operate on one screen at a time. But you can get a good deal more flexibility if you let it run through a list of screens instead. Or, more conceptually appropriate, a stack of them.

So I've implemented a screen manager in Dolce that does just that -- maintains a stack of game screens, any of which may be active or suspended, and loops through the stack each frame, calling a tick method on the ones that are active. The implementation is based on one by James Boer from Game Programming Gems 5, one that I have implemented so many times I can do it blindfolded now.

First, you register a screen under a unique name of your choosing. When you are ready to activate it, you issue a push command to the screen manager. Internally, when a screen is pushed onto the stack, its init method is called. Then it is eligible to be ticked each time the screen manager's tick runs. It can push other screens on top of itself without interrupting any current tick processing, as all stack commands are delayed until the next time the screen manager ticks. When another screen is pushed onto the stack, the pause method is called on the screen at the top. When a screen is popped, its term method is called. And when a screen becomes the new top as the result of a pop, its resume method is called.

One aspect of the system that gives it flexibility is that whenever a stack command is issued, the name of a particular screen is passed to the lifecycle method being called. A screen's init method is given the name of the screen which it is being pushed on top of, its pause method the name of the screen which is pushed on top of it, its resume method the name of the screen popped off of it, and its term method the name of the screen beneath it. This allows you to do things like use the same instance in multiple places on the stack.

As an example, consider a game's main menu. You will show it once at game start up. Let's say the player selects 'PLAY NOW'. Now, the main menu screen pushes the game play screen, and the screen manager calls the pause method on the main menu, giving it the name of the game play screen as an argument. Since the main menu knows it pushed the game play screen on top of itself, it can check the name to recognize that the game play screen is active. Later, the player presses the escape key to see the main menu again. So, the game play screen issues a push command to the screen manager, giving it the registered name of the main menu. The main menu instance, the same one already on the bottom of the stack, is fetched again from the registry and its init command called with the name of the game play screen. It realizes it is already initialized and was paused. So, instead of pausing, it simply resumes. You obviously don't want to display 'PLAY NOW' when the player is already playing, so you verify that the name given to the init method matches the game play screen's name and change the text on the button, and its corresponding action, to 'RESUME GAME'. Everything else stays the same. When that button is clicked, the main menu pops itself off the stack. When the pop command is executed, its term method is called with the name of the game play screen. By checking the name, the main menu realizes it's not really time to terminate yet, so pauses instead. Game play resumes.

Anyway, it's a really basic system that gives several options for handling game states. You can instantiate as many screen managers as you want. No, it isn't a singleton. I figure there are use-cases for multiple screen managers. Consider an in-game editor. I think it'd be more appropriate to have separate managers going -- one for the game and one for the editor -- than trying to manage everything on one stack. You wouldn't always want to terminate the editor when switching into game mode. Instead, you just switch between the screen managers.

Aldacron

Aldacron

 

Dolce and Free Functions

There's one thing that's missing from D that I really wish it had: namespaces. There have been discussions about this on the D newsgroup in the past. Walter Bright, D's daddy, has taken the position that D's modules *are* namespaces. In a way, they are. But, not really.

For those new to the party, D eliminates the need for header files. Interface and implementation are all part of the same code unit, called a module. Instead of #including headers, you import modules. Modules can reside in a package hierarchy, much like Java classes. So, given this source file:


// This is the module 'bar' in the package 'foo'.
module foo.bar;

void baz()
{
// Do something.
}

You would use it like this:

module myapp.mymod;

// Import the foo.bar module so its symbols are accessible by this module
import foo.bar;

void myfunc()
{
// baz is visible, so it can be called like this
baz();

// Or, with the fully-qualified name
foo.bar.baz();
}

So yes, a module namespace does exist, but it isn't enforced by default. That means that there can be conflicts if multiple imported modules export functions/types with the same signature. Now, from the user side, it can be enforced two ways. First, is by using static import.

static import foo.bar;

Now, everything you access in the foo.bar module must be prefixed with "foo.bar". You can go one better and add an alias that let's you call foo.bar by another name:

alias foo.bar Foobar;

This allows you to call Foobar.baz(), but it's still foo.bar.baz() that is being enforced under the hood. Remove the static keyword from the import, and baz() will still be accessible by itself. The alias is just an alternative, not an enforced namespace.

The second approach is to use a feature called "named imports", which effectively combines the two steps above, but with enforcement of the given name instead of the module name.

import Foobar = foo.bar;

Now, everything you access in the foo.bar module must be prefixed with "Foobar".

These approaches are all fine, but they put the onus on the user to create his own namespace. As a solution for solving namespace conflicts in client code, I think they're just peachy. In fact, I really like them. But from the perspective of a library designer, I have one major issue with it. I want to wrap my functions in a namespace, so that the client can't have conflicts to begin with and doesn't have to remember to use a named or static import.

Currently, in Dolce, I'm presented with the dilemma of how to handle certain functions that really ought to be free functions. I really don't want to take the C approach with function naming conventions, because it just doesn't mesh with the parts of the library that aren't free functions. On the other hand, I don't want to pollute the global namespace with function names that could easily clash with other libraries or client code. One option is to use static methods in a struct, or a non-instantiable class. But, to me, that just really feels dirty. Plus, every new type you define in a D program adds a TypeInfo instance to the final executable. It's not a lot, but it's just useless bloat for a class that is serving as a namespace hack. It offends my sensibilities, I suppose.

So one more option that I'm considering is having a sort of wrapper module that publicly imports the modules with free functions, giving them a name.

module foo.bar;

public import Bar = foo.realbar;

Imported symbols, by default, are accessible only in the module that imports them. So if module A imports module B, and you then import module A , you can't access B's symbols. However, if module A publicly imports B, then you can access B's symbols just by importing module A. So this enforces the namespace on the client if they import this module. Furthermore, clients will have a choice. They can import foo.bar and get access to the Bar namespace, or they can import foo.realbar and get the free functions, or create their own namespace if they need it. I only see a couple of drawbacks. The only problem I have is deciding if this really improves the situation or not.

Of course, the other option is just not to worry about it. Phobos is full of free functions, after all. Ultimately, my point of view is that I'm creating this library for myself, so whatever makes me happy is the way I'm going. I just have to decide which approach makes me happy.

Aldacron

Aldacron

 

To Wrap or Not To Wrap

One issue I briefly considered with Dolce is whether or not I should wrap Allegro in a nice, D-style API. I played around with it a little bit, wrapping an audio stream, first as a class and then as a struct. For those of you unfamiliar with D, yes, there is a difference.

Structs in D are, by default, value types. Classes are reference types. You generally allocate structs on the stack and classes on the heap (though the reverse is possible, but uncommon). Furthermore, structs have no vtables -- they aren't inheritable. This is a difference that C++ programmers sometimes have difficulty with. Typically, you'd use structs for POD types and classes for everything else. Of course, there are exceptions to the rule.

So I tried both types to see how they would play with my resource manager. It was immediately clear that classes make more sense, as I'd not want to be passing structs around by value everywhere and would likely allocate them on the heap anyway. In both cases, it was nice applying D's property syntax to some of the wrapped Allegro functions. 'al_set_audio_stream_playing(stream, true)' is a lot harder on the eyes, even for a C programmer like me, than 'stream.playing = true'.

But no. Let's not go there. It looks nice, but it really isn't my goal with this library. All I really want is boilerplate and utilities. Right now, you can load and initialize the Allegro shared library, along with any addons you want to use, set up default configuration, and create the display, all with one function call that takes three parameters. Or you can initialize each addon and create the display in your own time. That's what I mean by boilerplate. And by utilities, I don't mean wrappers. I mean a resource manager to help you with loading and managing resources, helper functions that reduce a multi-function-call process to one, and game tools like a simple math package, a 2D scene graph, entities and the like.

The goal is not to give Allegro an OOP interface, but to enable the client to get straight to the game code without mucking about with the other stuff first. Allegro gets you a long way toward that goal already, but because it's mostly just an OS abstraction, there's still another layer that can sit on top of it and give you a bit more utility. That's what Dolce is about.

Aldacron

Aldacron

 

Got the Bug Again

The last time I made a complete game was too long ago to think about. Over the years, I've lurked at GDNet semi-religiously. I've false-started more projects than I can count. Games, game libraries, game engines, all using several languages. Java, C, C++, D. What have I got to show for it? A hard drive full of archived source code. Well, not full, but you get the idea.

Despite the fact that I haven't completed a game in a long, long time, I have learned a lot over the years. I also have one personal project that hasn't faded away yet. For seven years (seven!?!), I've been maintaining Derelict, a collection of bindings to C libraries for the D Programming Language. Game-centric of course. Derelict 2, a branch I've been working on off-and-on for a while, will soon be moved to the trunk. Derelict 2 includes bindings for more libraries than the original. One of those, recently added, is Allegro 5.

Back in the late 90s, when I was first learning C at the ripe old age of 27, Allegro was the first game development library I ever used. I hadn't looked at it in years, until I noticed someone mention Allegro 5 recently on a forum somewhere (maybe here). I looked into it, liked what they've done with it, and immediately added a binding for it to Derelict. But that wasn't all. Working with Allegro again has reawakened the gamedev bug in me.

My latest endeavor is Dolce, a simple framework that sits on top of Allegro. The goal is to handle all of the boilerplate and provide some utilities that can reduce the amount of code needed to write games with Allegro. It's Allegro-specific, with no plans or desire to make it abstract enough to use with other libraries (like SDL or SFML, which Derelict also provides bindings for). I'm writing it for myself, to help me get some games off the ground, but I'll be putting it up in a repository somewhere once I've got it polished and documented. Most likely DSource or github. Dolce is being written with version 2 of the D Programming Language.

I've been deeply involved with D off and on since before I started Derelict. It has come a long way since the early days and has turned into a language that makes programming fun for me again. If you're interested in following D news but don't want to bother with the newsgroups or mailing lists, subscribe to my other D blog, The One With D. Here at D Bits I'll be posting my game-related D adventures. That includes writing about Dolce, Derelict, and my future unfinished game projects.

Working on Dolce has already brought me one instance of good luck. Being a big fan of KOTOR and Dragon Age: Origins, six or seven months ago I bought Mass Effect on Steam. To my great disappointment, it was totally unplayable on my system thanks to slideshow graphics. After a significant effort in troubleshooting with no results, I gave up and uninstalled. Fast forward to a few days ago. I got the Dolce test app up and running for the first time in VisualD, the D plugin for Visual Studio. I noticed a good deal of D3D debug spew in the console. Then I remembered that I had turned on the debug version of D3D some time last year for another project I was working on. A couple of days later, while reading a blog post about Mass Effect 2, the old light bulb popped up. I turned off the debug runtime and reinstalled Mass Effect. No more slideshow. And I can finally experience the adventures of Commander Shepard. Right when I don't need the distraction, of course.

Aldacron

Aldacron

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!