Jump to content
  • Advertisement
  • entries
    109
  • comments
    175
  • views
    117902

About this blog

This journal is approved by the Get A Life Commitee. I swear.

Entries in this blog

 

I feel green !

That's pretty much how I feel today. Even if it was somewhat expected, this was still a surprise when I went to my cybercafe of choice in Stuttgart yesterday evening. So now, I'm a full staff member, complete with the staff color and the terrible power that comes with it (something that, I must admit, scares me).

I now need to change my behavior. When I was a regular user, I saw no problem in downrating. That's now something I see as odd - I would definitely expect a staff member to support a user that deserves it, but not to negatively judge a user that, from his own opinion, deserve it as well. So while I may continue to support you by giving you positive ratings, I will stop giving negative ratings.

I also need to stay in my own role, as a news editor. I'm not a moderator, and I have little knowledge about the system and so on. So I won't ban/suspend/warn/close thread/and so on. I'll post news. That's already a cool job, believe me [smile]. In fact, I don't want to act as a moderator, so I won't do it (unless it becomes one of my task; but as of today this is not what I want). The current mods do their job very very well, keeping the noise ratio very low.

I also wonder how I can make up some kind of Rejected News (not news that haven't been approved for whatever reason, but news that wouldn't be featured as gamedev.net news, mostly because they are not targeted to hobbyist game programmers).

Last thing I want, 10 million EUR. If you can help, please tell me [smile]

Emmanuel Deloget

Emmanuel Deloget

 

It's about frustration...

Granted, I rarely play games. As a direct consequence to this, there are lots of things I'm not good at when playing: I am slow at typing specific keys, I don't see patterns that have been added to please hardcode gamers (you know, things like "if you find a hidden way to a hidden place, then you'll find good stuff at this place").

And since I'm the proud father of a 15 month baby, I have limited time to play anyway, so there is little chance for me to enhance my playing skills (not to mention that I'm not sure that I really want to enhance them).

But mind you, I play. Mostly single-player RPGs or RTS - I tend to prefer the former. I've been long time adept of pen & paper role playing games, and when I believe a CRPG is to be tested, I buy it. Over the time, my collection has become quite interesting :


Baldur's gate I & II
TES 1: Arena
TES 2: Daggerfall
TES 3: Morrowind
TES 4: Oblivion
Neverwinter Nights 1
Return to the Temple of Element Evil
Ultima Undeworld I & II
Dragon Age: Origins
The Witcher 1 & 2


I believe I have some other CRPG lying around - and this is not counting adventure games (Raymon, I still have my copy of Morning Wrath) or hack-n-slash with a RPG twist. I just love them.

When I play, I tend to follow a simple pattern: when possible, I set the difficulty to the lowest level (I cannot afford to play the same section of the game again and again), I try to be always higher level than needed (to minimize the risks of restarting the same sequence again and again because I'm too weak to overcome the challenge) and I try to follow the base story as much as I can (in sandbox-like games such as the TES series, I try to avoid side quests unless they offer a very good reward or unless I need to level more in order to fight a big boss). The goal is simple: since they are games whose duration lie between 30 and 150 hours, and since I can play at most 4-6 hours per week, I must make sure that these hours are not lost doing things that are unnecessary. I'm looking for fun, not completeness.

Most of the time this simple strategy works fine. In fact, until recently, it worked each time I played - and it was fun.

I think what I find of interest in a CRPG is not the challenge. I don't mind challenge - especially when it fits the story - but I prefer the discovery of the story (this is why I'm not going to spend much time in a MMORPG: the story is secondary in these games). What I really dislike is everything that can frustrate me of this pleasure.

And this is here I wanted to come. I cannot understand why recent CRPGs are more akin to create moments of frustration than older ones. I remember I spent 30 minutes to one hour to understand how Morrowind's Shrine of Azura worked (my fault ; I don't remember why it took so much time, but I beleive I misread something at some point) - but I can afford to lost 30 minutes here and there. I also remember a few particular frustration moments in this game or that game but I was able to overcome the difficulty (sometimes by changing my way of doing something). After all, I have saves to allow me to start again later if I'm a bit stuck.

Take Obivion for instance: the choice made by designers to level the monsters as you level is just plain silly. Let me play as I want to play, damn it! But then, most of the time the difficulty during fights only happen on random encounters, so you just have to reload the latest save and you're done.

Then comes DA:O. If you play the whole game in "Easy" difficulty (or whatever it's named), then the whole game will be quite kind with you (you will die a few time - the challenge does not disapear) - that is, until you get to the final part of the game. Then you'll have to start and start again (and again, and again, and again) the same stupid fights because suddenly the game difficulty changed drastically. After 6 hours full of restarting again and again the same fight, I uninstalled the game with the following verdict: "that was a good game, too bad the game designers went kind of stupid at the end".

And more rencently, I decided to go for a ride with The Witcher 2. And frankly, this is a good game. Except that I'm stuck in a stupid village, trying to rescue this stupid bard, with this stupid "please, no blood" attitude. Which mean "you have to fight with your bare hands and if you lose, you die" (how stupid: they are allowed to kill me, but I can't?).

Dear Red Projeckt game designer, if you ever read this: this is just plain silly. I set the difficulty level to the lowest possible value because I'm not good at gaming. I only have my keyboard. And you ask me to punch a guy in the face with the following conditions:

if he touches me 3 to 4 times, I'm dead
I need to punch them 6 to 8 times to k.o. one guy
there are two of them (or more, depending on the dialog) in a row
I cannot save between the fights (meaning that if I die before I k.o. the last one, I must redo everythign, including the lengthy dialog at the beginning)


Not only the conditions are unfair but they are also directly responsible for the frustration moment. That and the (damn "I don't play on a console, you freak" stupid) fight system


wait for one to two second. The tempo is just here so that you cannot rely on the system to know when you're going to be active. In other words, it's here to artificially increase the difficulty.
press the key (one of QSDZ) that appear on the screen. you have less than (I'd say) 300ms to do that, and you must press the key only once (which means: go away, you little guy with disabilities, we don't want you to play) . At this point, if you fail, you might take a severe hit and loose 1/3 of your life. If you success then...
one time out of three? (random: it can be 3 times in a row) you have to press another key in - guestimate - (300 - the time you used to press the first key) millisecond. If you fail, you might take a severe hit again. Remeber that you have to press the first key once or you second keypress will directly be interpreted as a wrong key - and you'll take a hit.
If you succeed, then once in a while you will punch the stupid guy in front of you. A bit. Enough for him to scream "my nails!" or something like that.


Having two fights in a row using this system is just plain impossible for many people, including me. Now, if I manage to do a perfect on the first fight (I did this one - out of 20 tries), you still have to defeat the second guy. If you fail, you start over - and over - and over - and over... Repeat ad nauseam.

So here is my verdict, after 3 hours of play: "it's a good game, too bad it's not meant to be played". Uninstall (bonus: frees a few GB on the drive), sell DVD, and insult designers (during the frustration rampage).

The frustration is doubled, because it hurts to buy a 50EUR game just to see that a stupid game design decision prevent you to have any kind of pleasure once you hit that stupid village. That's 2 or 3 hours of game if you decided to let the cinematic play as I did.

Red Projekt guys, this is a failure - and an upseting one.

--
Bonus: count the number of time I wrote the word "stupid" and win a bonus cookie!

Emmanuel Deloget

Emmanuel Deloget

 

A bunch of creatures...

Yes, I know, I tend to multiupdate my journal these days.

First, thanks everyone for your support about this tiny RPG system project. I really hope you like it (after all, that's why I discuss this in my journal) and I hope you'll like the ending part much more - you know, the parts where I'm going to explain everything in details :)

I added a bunch of creature classes - it took me 30 minutes to do so (you can't believe how easy it is now [smile]).

CreatureRaceHuman: trivial - the typical human; good at everything (read: it has a bonus for each skill)
CreatureRaceDwarf: the dwarf (resistant).
CreatureRaceElven: the elven race (wise).
CreatureRaceZombi: people who look like Evolutional
CreatureRaceVampire: er.. vampires. You don't want to fight them right now.
CreatureRaceSkeletton: a skeletton warrior. It is pretty easy to defeat
CreatureRaceGiantRat: a giant rat
CreatureRaceGiantSpider: a giant spider (ouch)
CreatureRaceBrownBear: a bear that can really hurt you
CreatureRaceOrc: our beloved orc
These creature races inherit the existing CreatureRaceBase class, which holds the natural weapon as a InventoryObjectWeapon (all the creatures, even humans, have a natural weapon). This weapon is, in fact a NaturalWeapon instance which implements natural weapons using a template - enabling me to create different type of natural weapon without having to create a new class each time (the only thing that is different bewteen two natural weapon is their names and max damages).

As for now, the world namespace look like this:

[clicky for full resolution]



See you later!

Emmanuel Deloget

Emmanuel Deloget

 

Text RPG update (post n100 in this journal!!11one)

Nothing much interesting. I was testing StarUML - the open source UML modeler - at work, so I decided to create a class diagram of the RPG rule system (read: what I've implemented so far).

Click on the image for full resolution.



The StarUML C++ reverse engineering tool is one of the best I ever used. The class diagram is rather easy to construct - but the window organisation of StarUML needs a big screen (this is not really a problem since you have a big screen, isn't it?).

I have to test the C++ code generation to see whether it can be used in a production environment.

Anyway, it's an impressive product!

Emmanuel Deloget

Emmanuel Deloget

 

A follow up, databases, and more

The followup(1)
Nice to see that Radupvr is not going to leave right now, and will continue to update his journal. But it doesn't change anything to the behavior of those who bashed him. Believe it or not but I find user-bashing rather imature and childish. User bashers tend to think that they are the only ones who are right in the entire universe, and that their way of thinking is the best one can possibly have. They deny you the right to be wrong - in their opinion, but since their opinion should be Teh Opini0n... - or to think differently than they are - omg! j00 r not fol0win Teh Opini0n!!!!11.

They are responsible for a number of departure - or visible dismiss - here. Remember the dismiss of S1CA? Even if he came back, who can say that bashing S1CA was a good thing? Wasn't he helpfull?

I happen to believe that Radupvr, S1CA and a lot of others are far more important for this board than any lounge lizard user basher.

The more technical stuff
I nearly finished the SQLite driver code - it is not so bad, but I had a number of problems and it not completely tested (for example, I didn't test the transaction mode). Anyway, it is cool because I can now continue to program on my laptop or on my main PC. I must now do the PostgreSQL code - it will probably be easy too, since libpq is rather easy to use. I may give you the code once it will be ok (and if you want it, of course).

Now, I have to create my tables - they have to be simple enough to be understood by both SQLite and PostgreSQL.

I am not happy at all with my network code - maybe you can help me, guys - actually, I have two problems:
when I run my code in the vc++ debugger, the socket I creates have socket descriptor > 4096, while FDSET_SIZE is defined to 64. You have to change the definition of FDSET_SIZE (this is legal, so there is no real problem here... except that your fdsets are now very big and that select() performs poorly). I believe that this is a Microsoft trick to enforce the use of these asynchronous sockets in their Winsock API but this is something I can't do, because my network code has to be (nearly) portable - my server will run on a linux box.
I may have to manage a large number of connections anyway (I still don't know exactly, but it would not be a bad idea to target more than 1000 tcp/ip connections). I still don't know whether it is a good idea to use tcp/ip or not, because I don't know how an ip stack will peform with such a high number of incomming, permanent connections. I may be able to redesign the communication process to use udp/ip - maybe... :S
If you, guys, have some ideas about this subjects, let me know! I guess I have to visit the Multiplayer and Network Programming forum ^_^.

Other project, other thoughts: I reread [SB-1](2) and I though to their terrain engine, and more precisely to their LOD system. In Scott Bilas's paper, no indication is given concerning the terrain LOD system they use, and maybe the reason is that they don't use a terrain LOD system. Tis would be crazy, but in fact most of time you don't see much of the terrain - because the view is centered on the player - so a brute force algorithm should be possible. If a LOD system is implemented, it has probably to do with the terrain node system - since the terrain is splitted in nodes, once can use a method similar to GeoMipMap to LOD far nodes.

So: brute force or LOD? I guess both have to be tried... :)


Update! H_o_p_s right side notes are finnaly nearly working! (1) edit: I tried to use H_o_p_s side notes, but was unable to use them. My HTML r the sukz0r.
(2) references are now put in the small references collection on the left of the journal entries.

Emmanuel Deloget

Emmanuel Deloget

 

And teh winnar is...

Noone.

No, it is even worse: if this situation continues, we are all the loosers.

Radupvr started this thread two days ago. His idea will probably never work, everything agrees on this point. What I find bad is not his post, but the answers he got.

All went right during the first posts. The idea he describe cannot work, ok. Even APC's first post was OK.

Then, the whole thing began to smell like a putrefied egg. I know that you'll say that these are out-of-context quotes, but I believe their context don't make them less harmful. We need to eliminate this sort of ignorance once and for all(1), speaking of Radupvr's original post. Raduprv, you have to be the most stubborn GDNet poster I know of, speaking of Radupvr's will to defend his idea - even proven inefficient and flawed; but are you sure that you never ever did the same thing? For someone who wrote an mmorpg, Rad is surprisingly dumb about some things. This is one of them. - What an interesting insight, but I beleive you can speak of someone without insulting him (the same guy continues with a superb "you guys should be ashamed of your ignorance", probably meaning that he knows everything on earth. "greet, now the idiots are rating me down" is also a good quote. I won't ask who are "the idiots", because I don't want to know the answer). Great idea! Raduprv, NASA is hiring. Just so you know... is also a clever insight - I just find it nastily sarcastic in this context, but who knows, maybe it was a proof of love?

During this conversation, I also saw some As for the rest of the thread - try to exercise a greater degree of diplomacy please, or Guys, it was just an idea; really now, no need to chew this guy to pieces. So clearly, there was something wrong, and some people saw it.

It is not difficult to understand why Radupvr was feeling attacked. Mockery, insults, condescension - I would probably had the same feeling.

Everything ended up with (2)

The rating problem is not very important - he still have more than a lot of people here. But the fact that he may be gone is more problematic.

Radupvr is a great guy. He his full of knowledge - even if his knowledge do not encompass astrophysics and planet studies: who cares? He exposed an idea, was bashed, and quit. I really hope he didn't leave GDnet permanently - but even if he want to come back later and just take a break, I really think that you, guys, should be ashamed by your behavior and your resulting "success". That's really a pity.

Really, there is no winner in this story - only loosers.



(1) APC managed to bash two user in one sentence. That's great. Clever, probably. If it wasn't a clever idea, he would not have posted that, right?
(2) I can't understand why I can't change the style of these quotes. If someone has an idea...

Emmanuel Deloget

Emmanuel Deloget

 

As always, I'm late

It seems that every other GDnet+ user already began their journal. I don't. Well, I didn't, until now.

Here are the reasons why I finally decided to begin one:

1) I want to give informations to newbies. I often saw recurring questions on the programming forums and I beleive these recurring questions should be answered in a sticky thread. This is not really feasible. Anyway, I'd like to centralize these recurring questions and answers in a non official FAQ.

2) there is no such thing as point 2.

3) I want to speak of my projects. To give informations about my code. To share my coding experience with gd.net users. Gamedev is really a great place, with tons of great individuals. Because I really like this place, I want to give a contribution to all the gamedev users.

4) I want to the master of the world.

Of course, I don't encourage anyone to read this devlog. I hope to fill it with interesting stuff/ideas/infos, and I hope I'll be able to update it on a regular basis. But if I spend too much time playing with it, I won't be able to master this world.

Mastering the world needs some time, you know...

Emmanuel Deloget

Emmanuel Deloget

 

Preparation of the fmx/07

As you may have noticed, since I posted many times on the subject, I will attend the fmx/07 in Stuttgart in a few days.

So I'm preparing it, and I must admit that it's not that easy. Even if I consider that I will be able to organize my time as I want (but that mean that I won't have much time to discuss with the cool people out there), I have still some problems to choose what would be the best talks to attend. Let's take an exemple: on friday, at 12:00AM, there are 3 game development related talks in competition:

* Intelligent believable characters, by Henry LaBounta of EA
* Professions in games, by Matthew Jeffreys of EA
* Flash on alternate devices (I'm pretty sure that John Hattan would want me to attend this one), by Peter Elst. This one is particular since it deals with consoles as well, and most notably the Wii (yeah, John, have you ever though about pushing your games on the Wii?)

In addition to these talks, there is also an NVidia workshop.

So you see, choosing between all will NOT be easy - and that's why I need your help. Here is your mission:


I'm not sure I will be able to do it, but if you are really interested in something in particular, I'll do my best to cover it.

BTW, there is no way for me to miss the NVidia opening reception or the EA closing party. Really.

Emmanuel Deloget

Emmanuel Deloget

 

No technical informations :'(

Investigation
No French game developper contacted me. I guess that I have to explain why I asked this in order to get some contacts.
First, I don't want your money. I don't want to contact you in order to get a job in your team. I am quite pleased with my current job (software architect is somewhat cool, you know) and I'm not going to change now. Next, I'm not going to sell you something. I don't have any ubertool for you, and you won't buy my game ideas - because you already have tons of them.
The reason why I want to get in touch with you is that I'm trying to promote game development in the south of France. I'm just beginning this neverending quest, and I still don't know what to do. That's why I want to speak to professional game developpers. I just want to find some informations about their needs, their dreams, and maybe their gf but this has nothing to do with the subject.

Project management
Disclaimer I'm dreaming. I don't have enough time to implement such kind of software, but I find the concept rather cool. Last year, I thought to how I would do a design document. My idea was to use a wiki-based system with an automatic ordering mecanism associated to some PDF printing capabilities. Unfortunately, like every idea I have, I don't have time to write this (I did some experimentation with phpwiki, but nothing very advanced). Here is a requirement list for such kind of tool:
User is allowed to write formatted text
User is allowed to link documents to the text (images, MS Word documents, PDF, web links, ...)
Since it is a wiki, the user must be able to create links to the other pages he created

I thought to this during the past month, and I believe that such system can be easily extended to something more usable.
First, the design document is not restricted to a gameplay document. It should also defines what are the needed resources - and if it defines the resources, why don't it store them? As a consequence, I need to integrate a resource management system that would allow me to store different versions of the same resource. The versionning system could also be used to track down the changes in the design document itself.
Next, I can't see why I should be able to manage the resources and not the code itself. Thus it would be nice to integrate a sophisticated software configuration management tool (SCM) - a la Perforce - along with a bug list management tool.
I can now manage the design document and the source code. There is a missing step between these two items: the software specification document and the detailed software design. This is more tricky part, because it would be nice to be able to create and store UML diagrams. Of course, this could be done in an external software (er... writing an UML modeler using AJAX is probably not teh funnay).
Having both the source code and the software design document in a centralied place, I can link them to have a clear idea of what's have to be done. The only mising point here is how long it will take to implement a feature - thus we need a project management software. We'll be able to define teams, individuals, assign their roles, watch out their progress, and so on.
Everything centralized in a web browser. I guess the idea is cool, but I believe the development time of such a beast is something along the line of 1 to 2 years. Anyway, I also believe their is a market for this tool - as it specifically target software development teams (and not only game dev teams). Anybody wants to fund a company?

Time, lack of
This is not god. I tried to do an entry for Ravuya's contest, and I came up with a good idea, a good enough code base. But in the end, my worst enemy (time, the one which tries to kill you during your whole life, and which will win at the end) stopped me. I needed two more days, but I wasn't able to find them.

Work, loads of
The company I work for is actively hiring software engineers in the south of France - and in other areas of France as well. There are no game development relate jobs (but who knows...), but the company is a good one - it is fair and loyal to its employees (not like most of the service industry) and the projects are challenging. Contact me for further informations ;) (and yes, the south of France is really great).

Emmanuel Deloget

Emmanuel Deloget

 

Encapsulation For Openssl Rsa/Blowfish In C++

Now, that's not a wonderful piece of code, filled with incredible meta-programming snippets. But it's a piece of code that's usable and - more importantly - that can be modified to suit your needs.

So let me introduce you a small part of a large library en devenir. This code encapsulates the blowfish symetric cipher and the RSA asymetric cipher from OpenSSL. As of today (version 0.2), only Linux is supported, but a port to Windows is planned as soon as I get my own PC back. As a bonus, a modern C++ implementation (little endian) of the SHA256 hashing algorithm is provided as well (used together with RSA private encryption, this allows you to sign messages ; which is kind of cool).

If you are interested by the code, just remember the following:
you're not going to see a lot of comments ; this shall not be needed (my code is not that horrible). a large part of the code uses templated policies. whenever I can, I play with the standard library. While waiting C++1x, this code uses a small portion of the TR1 (tr1::uint32_t and tr1::uint64_t). You finally have the possibility to watch some code of mine in action! (ok, that's not really of interest).
Licensing information is not included yet. You can consider that this code is licensed under some kind of BSD-style licence (that is: do whatever with this code ; just remember I wrote it, but if you want to act like you are the original writer, you are allowed to do that).

Class list:
ekogen::security::rsa_cryptograph ekogen::security::rsa_key ekogen::security::rsa_public_key ekogen::security::blowfish_cryptograph ekogen::security::blowfish_key ekogen::security::xor_cryptograph ekogen::security::xor_key ekogen::security::default_rng ekogen::security::xor_rng ekogen::security::symetric_cryptographekogen::security::symetric_keyekogen::security::asymetric_cryptographekogen::security::asymetric_keyekogen::security::public_asymetric_keyekogen::encoder::base64 ekogen::encoder::hex ekogen::encoder::basic_encoder
Function list
ekogen::security::sha256(const unsigned char*, std::size_t, std::vector&);ekogen::security::sha256(const unsigned char*, std::size_t, E::output_type&, E)ekogen::security::sha256(Iterator, Iterator, std::vector&);ekogen::security::sha256(Iterator, Iterator, E::output_type&, E)
PDF Documents that explains part of the interface are in directory /doc. The build system is very rudimentary and shall be replaced by a more advanced one in a few days (probably cmake, since it works on all the platforme I target).

Details (in French, sorry) about the code can be found on my blog. For those who don't speak French, google makes a decent work at translating the text.

Happy... something !

Emmanuel Deloget

Emmanuel Deloget

 

Notes about singletons in C++

Wot! Did I stop to work on my text RPG? Que nenni. This is an article I prepared last year, when I was young and self confident about my own knowledge. Anyway I thought you could find this interesting (and don't forget to discuss about this in the comments. I tend to believe that I don't get anough comments - I'm pretty sure that it is because you find me boring and dull and stupid and... yes, I'm kidding).

Before reading anything else, you have to read another journal - [Washu]'s one. Last year, he wrote a 3 part entry about singletonisis, a (helas) common programmer illness. For information, references as added to my Small reference collection at the top of my journal.

As it seems very simple, the singleton pattern is a very popular pattern. Unfortunately, it is not simple, and the consequence of this is that most users don't use it correctly. The goal of this journal entry is to spot a number of common mistakes in singleton implementations and to correct them while explaining the pro and the cons of the pattern itself.

But since I target a midbie audience (those who have a decent knowledge of C++ but don't consider themselves as gurus), I believe I have to say something about the pattern itself: what exactly is a singleton?

The definition
To quote the [GoF], the intent of the singleton pattern is to "Ensure a class only has one instance, and provide a global point of access to it".

The first point is "ensure a class only has one instance", and the key word in this point is not "single" but "ensure". The first goal of a singleton is not to declare an instance of a class - a global variable would act the same - but to ensure that you won't be able to create more than one instance. This is quite different.

The second point is probably even more important: "provide a global point of access to it". Again, the key word is not "global" here, but "point of access". What does this mean? Simply that any access to the singleton is controlled, which in turn means that I can do whatever I want to protect the accesses to my singleton object. This is rather important, probably more important than the first point.

Are you sure you carefully read the definition above? If you still think that a singleton is no more than a convoluted global variable, please, return to the previous paragraph and reread it until you fully understand it. This is really important, because the rest is only an implementation problem.

Studying common implementations
I did a fast survey on how you, gdnet users, implemented your singleton. There are some good ones, some less good ones, and some bad ones. First, let's see the bad ones.

Construction and destruction, the basics
The following (slightly modified) code has been borrowed from an gdnet thread
template ISingleton : public BaseClass
{
static BaseClass *mInstance;
public:
ISingleton() { ... }
virtual ~ISingleton() { ... }

static BaseClass* instance() { ... }
};

There are at least 2 major errors in this design, so let's spot them.

First, nothing forbids me to do :
MyBaseClass myVariable;
And one of the singleton intent is completely defeated by this single line. I get the same result if I write
ISingleton myVariable;
What is the problem here? The answer is simple: public constructor. When I have a public constructor in class A, nothing can forbid me to create any number of instances of A.

Next point: what will happen if I write
delete ISingleton::instance();
The code compiles just fine, bu of course any subsequent use of the singleton will crash the program. Again, this is due to another public member: the destructor, this time.

Construction and destruction, a no-no
I only saw this code once on gdnet, and I can't remember where I found it. The only thing I remember is a great black hole and then nothing. I believed I was in Hell.
Let's see the Horror in action (I heavily modified the code so that the OP will not be able to recognize it - I don't want him to kill me).
class Singleton
{
static Singleton * mInstance;
public:
Singleton() { mInstance = new Singleton(); }
~Singleton() { delete mInstance; }
static Singleton *instance() { return mInstance; }
};
Do you understand me? Here, I have the same problem as before, and even worse, because I modify the singleton when I instantiate it. But the worse is probably the recursive call in the constructor.
How do this singleton works? It doesn't work at all, and I suspect the OP to have written this on the board without testing. Let's examine how it should work then.
When I call Singleton::instance(), I get the static member mInstance, which is not initialised yet, because no instance of Singleton has been created. So let's create one:
Singleton singleton;
Yeah. Now, the instance is created and it creates the mInstance. And this instance creation is cool, because it creates a third instance. And then a fourth, and so on. Double yeah, this code is recursive and never-ending.
Let's correct the problem by adding guards then:
class Singleton
{
static Singleton * mInstance;
public:
Singleton() { if (!mInstance) mInstance = new Singleton(); }
~Singleton() { delete mInstance; }
static Singleton *instance() { return mInstance; }
}
Ok, we get rid of the recursivity. We still have a problem: to create the singleton instance, we need to instantiate the singleton. Sounds quite illogical since we want to have only once instance at at time. It is even worse: the destructor deletes my instance, so if I want this instance to last until the program death, I have to set up a global variable. You said it: OMG!

What is the problem here? Again, it is a constructor/destructor problem. None of them should play a role in the static instance creation, because it leads to at least one level of recursivity, meaning that I'll need to create an instance of a singleton to init my singleton's instance - and I'll have two singletons then.

Singleton, the Ogre 3D approach
One thing that bugs me with this otherwise wonderfull 3d engine is the singleton management. To create the Ogre::Root object, you must instantiate it using the new operator. Once you've done this, you are able to use it as a singleton.

// init
m_pOgreRoot = new Ogre::Root(szPlugins, szConfig, szLog);
// this call will crash if the Root object hasn't been created yet
Ogre::Root::getSingleton().doSomething();
// at the end of the software wa are allowed to delete the ogre singleton!
delete m_pOgreRoot;
IMHO, a better approch would have been to do an initialization function
// init
Ogre::Root::getSingleton().init(... param list...);
// this call won't crash, but doSomething() can throw an exception if
// something should have been initialized before
Ogre::Root::getSingleton().doSomething();
// to explicitely destroy the singleton, I need another method
// -- this is rather bad, since it may be called at any time and might
// -- do rather bad things in the end
Ogre::Root::destroySingleton();
This allow the singleton to be always valid - which is, I think, far better and less error prone.

A better approach
Let's write the code
class Singleton
{
static Singleton *mInstance;
Singleton() { }
~Singleton() { }
public:
static Singleton *instance() { ... }
};
What is the problem of such code? It seems good enough.
Well, not exactly. I can still play with it:
Singleton mySingleton(*Singleton::instance());
What did I do here? I created a copy of my singleton. What will the compiler say? Nothing. If you don't provide a copy constructor, it will provide a default one for you. Quite cool, isn't it?
Again, I have two instances of my singleton, which is obviously not a good thing.

The beginning of a solution
The last piece of code was rather good, expect for the copy constructor problem, but this problem is easily solved by adding a non-default private copy constructor:
class Singleton
{
static Singleton *mInstance;
Singleton() { }
Singleton(const Singleton& s) { }
Singleton& operator=(const Singleton& s) { return *this; }
~Singleton() { }
public:
static Singleton *instance()
{
if (!mInstance)
{
mInstance = new Singleton();
}
return mInstance;
}
};
Let's try to summarize the strength of this implementation:

we cannot create an instance of the singleton using the default constructor, because it is private.
we cannot create a copy of a singleton instance
we cannot destroy the singleton out of he singleton class because its destructor is also private
since we defined the copy constructor, we also define the operator=() - this is commonly refered as a good practice

The first two points make it clear than I cannot create more than one instance of my singleton object - thus the 'single instance' intent is really enforced. The third point avoids me to accidentally destroy my singleton instance - which is rather cool.

There is still at least one problem in this simple implementation: I have no way to control the lifetime of my singleton, mostly because I don't know when I should destroy it (moreover, I forbid myself to destroy it from the outside of the class, so I just can't delete it when I decide I'll no longer use it).

On singleton lifetime, the first approach
A common mistake I often saw of these boards is to use the following:
class Singleton
{
static Singleton mInstance;
Singleton() { }
Singleton(const Singleton& s) { }
~Singleton() { }
public:
static Singleton *instance()
{
return &mInstance;
}
};
It seems to resolve the life time problem - the singleton will de destroyed at the end of the program.
Actually, this solution is worse than the previous code, because you can't know if the singleton is created if you try to use it before main() is called (for example, in the constructor of another singleton). Singleton::instance() can return a pointer to invalid memory, and your program will crash.

On singleton lifetime, Meyers' approach
Another solution is to use what is called Meyers singleton - first expressed in [SM-2] IIRC (the third edition of [SM-1] also uses it).
class Singleton
{
Singleton() { }
Singleton(const Singleton& s) { }
~Singleton() { }
public:
static Singleton *instance()
{
static Singleton theInstance;
return &theInstance;
}
};
This code ensure that the singleton will be created when it is needed (because the C++ standard tell us that a static variable in a function is created the first time you enter in the function. Since it is a static instance, and not an allocated one, it will be destroyed at the end of the program. It seems good now - we have our final candidate!

Well, not exactly. There is still an embarrassing problem here, because of the order of destruction of static instances. The standard says they are destroyed in the inverse order of their creation - it means that if a static instance of A uses my singleton in its constructor, then my singleton will be destroyed before my instance of A. What if A destructor tries to use the singleton? Again, I'll crash my program.

The real solution is...
...nothing. There is no real solution to this problem. Meyers singleton is quite good, but has still some drawbacks. It can be extended - because it is rather easy to know if the instance of your singleton is still valid or not (monitor your singleton destructor) but once you detect it, it becomes tricky. If the singleton holds some important resources, they are probably destroyed too, and maybe you don't have any way to recreate them. If it is not the case, you can try to recreate the singleton - it is possible, using placement new.

Templated singletons
A templated singleton has to obey to every rule I described in the comments above, and these rules also apply to the client class (the template parameter). Moreover, the client class have to declare the singleton template class as friend because there is no other way to gain access to the constructor of the client class. Some of you might object that it is possible to set the constructors and destructors as protected (and since our singleton template class inherit the base class, it automatically gains access to the methods he needs). While this is a possible solution, it is not technically viable: what does forbid me to inherit a dumb class from the base class in order to bypass the singleton limitations?

There is at least one more problem with templated singleton:

the client class construction is limited to a very simple construction - either the default one or a particular one that have to be the same or each declared singleton.
to avoid this problem, you have to declare a construction policy - but then is creates another problem: there is no other way to declare the creation policy but to declare it as a template parameter, because feeding the instance() method with a function pointer is not an acceptable solution. Since it is a template parameter, it means that the singleton type you declare is dependant upon this policy, i.e. if you change the policy, you change the singleton type. Unfortunately, static members are common to every instances of a particular class. In our case, it means that we can create two singleton ot type MyType by changing the creation policy. This is weird.

[AA-1] gives an in-depth explaination of a templated singleton class that can be extended using policies - I suggest you to read it if you plan to implement this.

Using the singleton pattern
As you already know, this pattern should not be used instead of a global variable - mostly because you should not have any global variable. Again, the [GoF] tells us what is the applicability of the singleton pattern:

I think I have to put the emphasis on the second part of the first point: and it must be accessible to clients from a well-known access point. Using the singleton pattern just because you believe that only one instance of a particular object is needed is called lazyness (the first symptom of singletonisis). But if you need to control the access of this object then yes (for example, only calls from a particular thread can succeed), you just found a good singleton candidate.
You found it by yourself, singleton are rare, very rare. Fortunately, it is still possible to find good examples: a multi-threaded pool-based memory manager might be one of them (the fact that you are working in a multi-threaded environment force you to protect the memory manager access). [AA-1] cites some other possible uses of the singleton pattern, but some of these are questionable IMHO.

Avoiding the singleton pattern
As you might see from this entry, it is rather hard to implement a correct singleton. It is even harder to know when to use it. As a consequence, it should be nice to avoid it whenever possible. Of course, the singleton offers nice opportunities, and its rather cool to announce in a meeting "let's use the singleton pattern" (I did it several time just to see my coworkers' faces). But let's face it: it is difficult to use. How to avoid using it then? [Washu] gives a very good answer to this question: refactor!

Conclusion
There are still numerous issues with the singleton pattern that hasn't been addressed here. For example, is you singleton thread safe or do you need to protect its use? This question and similar ones are not the purpose of this journal entry. Moreover, there are some more complete resources about singletons - the best one I ever read is in [AA-1] - I suggest you to read it if you want to learn more about singleton and a lot of other subjects. In particular, [AA-1] address the problem of Meyer's singleton destruction in an elegant way, using the so-called phoenix singleton. It also deals with a lot of other implementation issues.

I tried to give you some solution to create a correct singleton. But correct is by no mean a synonym for perfect, and you can sill abuse the C++ syntax to do bad things with singleton.

Thou are warned :)

Emmanuel Deloget

Emmanuel Deloget

 

Video game source code

This is more or less a reminder, as most of you already know this stuff. Here is a (incomplete) list of games whose source code is available, together with links (or, when not available, information on how to retrieve the source code). The origine of this entry can be found on my own personnal French blog.

Gish, Cryptic Sea.
Aquaria, Ryan Gordon (via mercurial)
Quake III, id Software (direct zip d/l)
Quake II, id Software (direct zip d/l).
Quake, id Software (direct zip d/l)
0 A.D., Wildfire Games.
Rise of the Triad, 3DRealms
Ryzom, Nevrax (stopped business a few years ago ; the game is now supported by another company)
Seven Kingdoms, Enlight.
Arx Fatalis, Arkane Studio
Return to Castle Wolfentstein single player and multiplayer, id Software (sirect zip d/l).
MechCommander 2, Microsoft
No One Lives Forever 2, Sierra : toolkit contains the source code of the game
Wolfenstein: Ennemy Territory
Duke Nukem 3D, 3DRealms
Lugaru, Wolfire Games
Penumbra - Overture, Frictional Games, via git
Alien Swarm, on steam (the source code is available in the SDK (URL = steam://install/513 ; you might need to install Steam to get it)
Allegiance, Microsoft

This is not (by far) a complete list. Some old games have been removed (mostly Doom-generation and before), some other are missing (Allegiance ; anybody have a link on this one ? It seems it disapeared from MS Reasearch downloads ; Hexen, Heven II and Heretic: cannot find a reliable download site; The Operative - NOLF : went AWOL a few years back, the Wayback machine doesn't help much ; and so on).

Do not assume that this post is not connected to my previous entries. There are evil forces in this world you can't know anything about, and I'm in a deal with some of them.

Emmanuel Deloget

Emmanuel Deloget

 

Day 8 in L.A.

So, yesterday was a good day. I managed to meet Dave and Melissa, we had a very nice evening in a very good restaurant, loads of fun, talks about gamedev, and so on. To come and see them, I had a fairly long freeway drive, so I decided to try this Mustang a bit.

I'm pleased to say that I'm statisfied with this car. The driver position is confortable for long drives, and the car is responsive (despite the fact that it's an auto-gear. Do you ever drive manual gear cars, you American people? It's a lot more fun, you know). I pushed it to its limits (hoping that no policemen was roaming there), and it went nicely to its 120 mph. All in all, that was pretty fun. I definitely plan to buy one some day (when my MG ZR will die, I guess)

The dinner was really nice too! I tasted a red wine from Temecula (don't remember the name, but I think it was some merlot. Unsure about that though). It was quite good, with some distinctive points - although it was not that different from a French merlot. For the main dish, I chosed some chicken rolls with (made in America) French brie inside. That was tasty as well.

Of course, most of the discussion went on gamedev.net. I got a better view of the undergoing changes, our goals for the new millemnium (well, at leadt for this year), and on the organization itself (who is in charge of doing what there). That was rather informal, but very informative.

So yes, that was a very good day. The only bad thing is that I did some shopping at the Del Amo Fashion Center near my hotel, bought too many things, and had to buy another carry-on to be able to get everything back to France. Geez, when will I stop to spend my money? [smile]

Emmanuel Deloget

Emmanuel Deloget

 

Back to our work....

I have not been very gdnety these last weeks. Well, to be more exact, I have not been very active in the forums.

I had some good reasons - first, I set up a technical blog which is external to gdnet (and is written in French, and targets French engineers that don't want to read English blogs). The second reason is totally gdnet related : I had to catch up with my new gdnet responsabilities.

You probably notices a good amount of news that came from me. The reason is that I became a News Editor - I effectively have the power to post news, and to approve news that you post (expect me to send you a PM or a mail if I don't approve your news for whatever reason; this also mean that while I'm not greenefied, I'm a GDNet Staff member as well (a lesser one [smile])). So far, thanks to the cooperation of the staff and mods here (and in particular Rob and Sean), it goes quite well (IMHO). I no longer ask myself "Should I approve this news?" (which is a great improvement upon the first day: I really felt the "fear to approve something that should not be approved").

The good news (ah ah) for you is that now, news can be approved twice a day - since I'm in Europe, and most other news editors are in the US. It also means that now we begin to have quite a powerfull news team (well, it was already quite powerfull before I joined; not sure I'm adding anything in fact) so we should be able to find more news for you (on the overall, there has been a real improvement since last year, and it's even more obvious if you compare the news rate now and the news rate 2 years ago).

So... stay tuned, pals, and see you soon on the front page!

Emmanuel Deloget

Emmanuel Deloget

 

Design II : Attack of the GraphicEngine

Abstract of the previous shows
In the previous installment of my ongoing journal activity, I decided to speak about UML design. I now see that my wording is obscure and contains a lot of (sometimes kebod related) english errors. My bad. Nevertheless I decided to continue to speak about this subject - I find it interesting. Since noone really read this journal (thanks Mushu for the info), I bet it will not hurt your brains.

Good design, bad design
The preliminary design I got yesterday is a bit small. Some pseudo-C++ gives us:1


class GraphicEngine
{
public:
init();
uninit();
draw(World world);
draw(NonStaticObject object);
};


It raises an interesting question: who performs the drawing? Should we say that the GraphicEngine draws the World object or should we say that the World should draw itself using the GraphicEngine? The answer is not very simple.

First, let's consider a simple design issue: what if I decide to modify the graphic engine? I may decide to finally go the 3D way. I probably won't want to rebuild all the game code because of this modification- even it if is a large modification. Or I may decide to adapt the drawing to the device capabilities (a mobile phone is more restricted than a PC). Using this wording it seems that I should let my GraphicEngine draw the world. It means that my GraphicEngine will have to know how a World object is built. Here comes the problem...

OTOH the GraphicEngine is a base component that should not be aware of what the game really is. If I decide to modify my underlying World representation later - because of some important design issue - I will need to modify the GraphicEngine as well - even if the rendering itself do not change. Moreover, if I decide to add a new object type that have to be drawn then I'll have to modify the GraphicEngine as well.

Conclusion: there is something missing. We have to forget our preliminrary design and we need to find better answers to the questions I asked yesterday: who will use the graphic engine, and what will the actor do with it?

To be continued... :)

Emmanuel Deloget

Emmanuel Deloget

 

New news and other news

Hello you all!
I just published the second XNA blog entry of my Intro to XNA series (in case you wonder, yes, it's written in French). It presents the basic architecture of an XNA program, and explain some bits about game architecture to my audience (which is, in most part, not game development oriented). If you're really curious, the first one (also in French, of course) is still there. Of course, both are going to be translated later (let's say, in one or two months). I'm back from L.A., but I'm still jet-lagged. Last night I slept 11h, the previous one I slept 3h, and the previous 13h. I'm totally lost. One thing I'll remember: I really enjoyed the Mustang, but God, it drinks a lot! I'm going to be more involved in GameDev.Net. I'll keep you informed on that later. I'm thinking about, hey, how would it be to become a game publisher? I think I'd like it. I may publish/edit games from either hobbyists or independant game developpers with a limited budget, trying to organize some buzz around them, sell copies in stores, advertise in gaming magazines, and so on. That sounds fun. What do you think about that? .

Emmanuel Deloget

Emmanuel Deloget

 

Ooops. My title was too long.

The good thing about working for a car equipment maker that sells electronic products is that they want to show their talent every year at the CES. Since they do high-end car audio and navigation systems, the best way to present them is to put them in situation - I mean, in a car.

But if you show something at the CES, you'd better try to make something that everyone will remember. So there's not way you'll present your equipment in a normal car.

That why, at Alpine, they decided to tune cars a bit. The result if quite impressive - this is not the average rice boy tuning here.

For exemple, let's begin with a close-up:


clicky's biggar

We see the Alpine logo quite well, and it seems that this "thing" has huge wheels. That's quite true - although it's difficult to know the exact wheel size. Something around 24 or 26 inches.


clicky's biggar too

But changing the wheels doesn't make your car a nice one - you have to change something else also. Why not removing everything which was in the inside to make your own? After all, who needs to get more than one passenger in a car? Also, don't forget the sound system (remember, you're selling them).


clicky's biggar again


yar yar yar, clicky's biggar

Now, you may have forgot what was the car at the beginning - so it's still interesting to not remove some distinctive signs. After all, you have to tell your friends that you are richer than them, and that you can do whatever you want with your BMW SUV...


clicky's biggar. still smallar's than da car

Emmanuel Deloget

Emmanuel Deloget

 

"Somewhat type-safe " enums in C++

I don't know why I want to write this down, since it is a not-that-good solution to a known problem. Anyway, let's begin.

As you all know, C++ enumerations are not type safe. Let's have a look to a simple example:

enum Values
{
value0, value1, value2, value3, value4
};

Values myValue;

// .. later in the code ...
myValue = 100;

your compiler may issue a warning, but this code is still legal from a C++ standard point of view. The problem is that 100 is not a value of the enumeration.

As a consequence, I can't be sure that when I'll use myEnum its value will be one of the values I added to the enumeration - and guess what - this is a Bad Thing.

The solution I use is pretty simple: I encapsulate the enumeration in a class:

class Values
{
public:
enum Enum { value0, value1, value2, value3, value4 };
private:
Enum mInternal;
public:
Values() : mInternal(value0) { }
Values(const Enum& v) : mInternal(v) { }
Values(const Values& v) : mInternal(v.mInternal) { }
Values& operator=(const Values& v) { mInternal = v.mInternal; return *this; }
bool operator==(const Values& v) { return mInternal == v.mInternal; }
bool operator!=(const Values& v) { return mInternal != v.mInternal; }
Enum get() const { return mInternal; }
};

Now, I can write this code

Values v1(1); // -- ERROR !
Values v2(Values::value0); // valid
Values v3;
Values v4;
v3 = 1; // -- ERROR
v4 = Values::value0; // valid
if (v1 == 1) { } // -- ERROR
if (v2 == Values::value1) { } // valid

Of course, I am still able to setup a Values with an int - since there is no automatic cast, I will need an explicit cast

v1 = (Values::Enum)(1); // valid

The get() method is provided because there is no automatic conversion from the class to int - such conversion would break the == and != operators (because the compiler would try to convert my Values to an int when I write "v1 == 1"). Nevertheless, it can be useful to get the real enum value - for example:

switch (v1) // -- ERROR
{
// ...
}

switch (v1.get()) // valid
{
// ...
}


Cons
... Not much? Oh: you have to write the class. It takes more time than just writing the enum :)

Pros
A lot:
type-safetty (even if it is not total) is better than no type safety
this type-safety is hidden, since most of the usage of the variable won't change
since everything is inlined, the use of this class costs nothing
the explicit cast (to throw an int into the value) is more readable and less error prone than an implicit cast
writing Values::value0 is more readable than value0 alone
I can enrich the class to do a lot of things.


There are probably a lot of other advantages to this method - that's why I use it :)

Emmanuel Deloget

Emmanuel Deloget

 

More fun with weapons and armors

The update is going to be short - I don't have much to say :)

Yesterday I added some basic races to the game system; I decided to give some equipement to these races, so I created a bunch of class to inherit InventoryObjectWeapon and InventoryObjectArmor.

The new weapons are: WeaponDagger, WeaponWoodStaff, WeaponShortSword, WeaponLongSword, WeaponWarAxe, WeaponWarHammer and WeaponSpear.

The new armors are: ArmorHelmet, ArmorTorso, ArmorArm, ArmorLeg and ArmorShield.

As you can see, nothing really - except that I only needed less than one hour to add these. This is not really a great achievement since there isn't a lot of code to write.

More interesting, I also added WeaponEnchanted and ArmorEnchanted.

I was facing a small problem: how to say that a weapon is enchanted? After all, if I add one weapon type to the game, I may have to write the same code to handle the enchanted version of the weapon. The other solution is to add an "enchanted" property to the weapon. But then what if I want to create ethereal weapons? Or fire-based weapons? Adding yet another property to the base weapon class is really not a ggood option.

Fortunately, design patters are going to help again - in particular the decorator design pattern.

WeaponEnchanted is a decorator. It modify the behavior of any other kind of weapon, allowing me to create a new kind of weapon on the fly (the same goes fro the ArmorEnchanted class). If I want to add another property to the weapon, I'll simply add another decorator to the class framework and that's it: instant enchanted ethereal weapon :)

See you later ;)

Emmanuel Deloget

Emmanuel Deloget

 

fmx/07 - the insider, day one

Yeah, I'm in the press lounge (let's call it that way) of the fmx/07, writing my first message from inside the show.

I received a few press release to publish, but most of them aren't really related to game development. I will see if I can feature the other on the front page today. I also received the first gifts (from the fmx organization): a nice red bag, two DVDs (selected Stuttgart Media University student work + selected work from the Filmakademie Baden Wuertemberg) - and the conference schedule, a thick book of 280 pages with full details about the talks and the other conference events.

The real thing for me will begin this afternoon - as I told you yesterday (but I'm sure you weren't listening, were you?), I'm attending two reviews. The first one, entitled "Wb nOObs, u roxxor!" discuss World of Warcraft and will try to extrapolate its impact in terms of gaming and internet use. The second one, entitled "Second Life Rocks / Second Life Sucks", discuss the good and bads of Second Life - from the gameplay point of view.

I may also attend another review entitled "Web 2.0 - where the mass meets" which discuss the Web 2.0 phenomenon, what could be its future and its impacts on internet gaming.

After that, it's all pleasure: the SIGGRAPH projection takes place, then the opening gala (which is at the same time the closing gala of the ITFS; that's why it takes place after the end of the first conference day), and the opening party organized by NVidia.

And then, tomorrow will come [smile].

Emmanuel Deloget

Emmanuel Deloget

 

Arf !

It seems that to enter the top 50 rated posters of gamedev, you'll have to be at least... Some kind of 1337 I suppose.

Proof:





Nothing more to say.

Edit: clicky for full size.

Emmanuel Deloget

Emmanuel Deloget

 

FOR I=0 TO 5 STEP 1

ph24r the 1337
I find the so-called elite speaking cool. The r0x0rz. Not because it is cool, but because it is weird. Probably as weird as the pope singing a rap song. Since he did, I guess there is nothing wrong in da leet language. Quite teh funnay.

Where are the comments?
Since you don't read this, you don't comment this. Quite teh logical. But if (for whatever reason) you want to add a comment - not very difficult you know - feel free to do it. Of course, if you feel you should paypal me, that's even better...

Iterative design
Designing a software is an iterative process. One have to think to a solution, design it, then he must improve its design. Once the software is released, he has to work on the next version, which of course needs more design.
The only one who can create a complete software with only one design iteration Gandalf, but AFAIK he does not exists in our world.

And thus...
yesterday we had a talk about the ownage of the graphic driver. We found that both the graphic engine and the system object can own the driver. Today I want to dig further.

There is nothing wrong in having the graphic driver inside the graphic engine. We saw that it only adds implementation-based drawbacks, and those can be defeated by implementation-based techniques. However the creation of the driver will probably need some access to the system.

On Jan-27, I talked about the system component. It seems that this component needs to provide an access to at least two major UI components: the diplay manager and the input manager. We define the system component with:

namespace System
{
class Display; // not defined yet
class Input; // not define yet
};


Because the graphic driver works tightly with the display subsystem, it will probably have to use it (we already saw that the display system can't be implemented in the graphic engine because of the dependance it implies: it is up to the game frawork to implement it, not to the graphic engine). But, from an abstract point of view, the display system - which is aimed at displaying things - and the graphic driver - which is aimed at (guess...) displaying things - are somewhat equivalent.

If you have two display systems in two different modules then there is something weird. We should be able to factorize things a bit more.

Remember then our first solution (the system own the graphic driver). Now, it seems we have two display systems in a single package. This is really bad, and it is probably not what we want.

Since they are basically the same thing, why don't we factorize at this level? The graphic engine is the display, and the display is the graphic driver. It means that we pull the graphic driver away from the graphic engine.


namespace System
{
class DisplayDriver; // this is the graphic driver!
class InputDriver; // renamed, for consistency
};


Yeah! Teh FacOrizOrz!!!11one
Therefore the driver is now in the system package, meaning that our graphic engine is using the DisplayDriver.
We haven't thought about that until now, but why should the system package (which belongs to the game framework) be aware of the graphic engine who is using it? You'd say that we never said something like that. But we just took a graphic driver from the engine and we put it in the system package. How do we know is the driver is aimed at 2D rendering or not? Even if its interface is an abstraction of the real underlying driver, it should not be designed with 2D game in mind, because we won't be able to reuse it. But we don't want our engine to cope with some advanced 3D rendering issue - hell, it is a 2D engine.

Again, as always, we are lucky. The GoF gave us the solution: we have to use the Adapter pattern (check here if you are unfamiliar with this pattern - although the site speak of C#, the pattern description is fairly generic).

The Adapter pattern will allow us to provide a 2D graphic driver - internal to the graphic engine, this time - which will encapsulate the system DisplayDriver.


class AbstractGraphicElement
{
public:
virtual draw(GraphicDriver gdrv);
};

class GraphicEngine
{
GraphicDriver gdrv;

createDriver(system::DisplayDriver drv)
{
gdrv = new GraphicDriver(drv);
}
destroyDriver() { delete gdrv; }

public:
init(system::DisplayDriver drv) { createDriver(drv); }
uninit() { destroyDriver(); }
draw(AbstractGraphicElement gel) { gel.draw(gdrv) }
};


Quite teh c00lz0r, isn"t it?

Hey, you won't stop now!
Hey, I have a life! See you next time :)

Emmanuel Deloget

Emmanuel Deloget

 

I'm the lucky guy!

Deutsche Telekom is suing me for unpaid bills (540EUR). I won't pay the bills. Reason: while it is true that I picked up a phone line some months ago, DT never did their job properly. After two phone calls (the latest has even been ended rather bluntly by the operator who was not able to speak English (maybe he thought this was a joke)), I gave up with them - they never bothered to install a phone plug in my flat, but they never forgot to send me their monthly bills. Consequence: I have never been able to use the phone line for which they want me to pay.

I will face them proudly. I will battle them. Ultimately, I don't expect to win.

Screw them.

Emmanuel Deloget

Emmanuel Deloget

  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!