Quote:
This particular loading utility has to be the hardest thing I've had to program on this project so far. I redesigned the system at least 5 times before I came up with what you see there.
I thought this game tutorial would be done as simple as possible.. It seems, it is getting harder and harder ... :) Probably I might have more questions to ask after this ... :)
Quote: I thought this game tutorial would be done as simple as possible.. It seems, it is getting harder and harder ... :)
No worries...the reason it was hard for me was because of the windows API and its percularities :) Conceptually, the design of the preloader is quite simple. Anyway, I thought for the next tutorial, we acutally look at how chess is programmed...so without further delay I introduce Beowulf:
http://www.chessbrain.net/beowulf/
I'll basically be wraping this chess engine for our next tutorail.
I've been rather busy the past few weeks. Currently, I'm a consultant on a project and we are currently having a hard time pinning down the design of our system. Our problems are primarily caused by the logistics of the interface. So I decided to give a bit of a blab on why interfaces are so important.
Interfaces are the windows to the soul of your implementation. Even the best code design can be screwed up by a lousy interface, and similarly a well thought out interface can save you a lot of time and headache. For those of you who have used OpenGL and DirectX, you already know what I mean.
So what exactly is an interface? In c++, it basically means whatever you put in your header, however I think we can extend the definition a bit. An interface is basically the way we use code. OpenGL's interface for example is gl.h. You guys were also exposed to Win32's interface in our latest tutorial on preloading. In fact our tutorial has so far been, in part, a demonstration on different types of interfaces. All of our low level systems are built on top of some other system and are therefore forced to use it's interface to do it's job. You'll note that some interfaces make things easy to do while others make them harder.
There are quite a few interesting things going on here, so let’s start to decompose :) Here's a list of questions we're going to tackle: - What makes a good interface? - If we have a bad interface, how can we change it? - When can't we change our interface? - How do we tackle hard to design interfaces?
What makes a good interface?
Here's a hint; you've been using a system with a good interface for a while now. Give up? It’s OpenGL. IMHO, OGL has one of the best interfaces I've used in programming. In fact its interface is so good that it's spawned a bunch of other libraries (OpenAL, OpenIL, OpenML), which use its interface design philosophy. So what makes this interface so good?
Well, one of the first things you'll notice is that it's very clean (open up gl.h if you don’t believe me). There are a few things that contribute to this clean effect and I think the first thing that pops out at you is the naming consistency. Not only is the naming consistent, but it is predictable; you rarely have to look at documentation for syntax reference. For example, if you wanted to use integers instead of floats you would just switch the last character of the function from f to i. It is also very easy to distinguish gl code form the rest of your own code, which as a result makes your own code clean and easy to read.
After using GL for a while you'll also notice that it's easy to use. There are approximately three different types of workflow architecture that exist in OpenGL: - glBegin/glEnd (or a queue) - glPush/glPop (or a stack) - gl... (Basically any other state setting function you call) Almost everything you do with OpenGL will fall under one of these categories making the workflow both consistent and predictable. As a result, learning new techniques requires little work. Perhaps the nicest thing about the interface is that it is not intrusive. All you have to do is call functions. What is an intrusive interface? It’s an interface that locks you into using its methodology. Win32 is a good example of an intrusive interface. You are always forced to use a callback routine and you must use Win32 messages ect. If you want a good example of solid UI design check out the MEL scripting language (comes with Alias products).
If we have a bad interface, how can we change it?
The solution is conceptually simple; warp it :). Wrapping something basically means you're changing its interface. Think of Pandora's box (http://www.pantheon.org/articles/p/pandora.html). The box may be full of many nasty things but on the outside it’s still just a box. So where exactly are these mythic boxes found? Check out our res_ subsystem. Many different and nasty things exist within the .cpp files but when you take a look at the .h file, things look a lot more calm. There are typically things that let you initialize and de-initialize. Things that let you load and do other neat things.
However, if you open the boxes, some very bad things will happen. Eventually, your once peaceful program will be reduced to a pile of broken function interfaces and publicly exposed class members. How do we open the box? There are a few bad things we can do; making class members public is one. Perhaps the worse thing is to expose an intrusive interface because it will then naturally affect everything else that uses it. Check out this site for pointers on what not to do:
http://mindprod.com/unmain.html
When can't we change our interface?
Unfortunately there comes a time when you can’t change what is in your .h file. This time is roughly proportional to how often you have used your interface. For example, if you have only used your interface in one spot, then changing it is quite easy provided you change where you've used it as well. However, if your interface is used in your entire program, changing it becomes almost impossible (short of rewriting the entire program). It’s always best to have your interface reviewed by someone before you use it (and your implementation if they have the time).
How do we tackle hard to design interfaces?
Decomposition to the rescue! Like many problems in programming, the easiest way to tackle interface design is to break it apart into smaller and smaller problems until they can be solved. Remember when trying to solve interface problems, you must also make considerations for future expansion. After a while you will not be able to make significant changes so your interface should accommodate everything you want to do now and in the future. When dealing with harder problems, you'll find that after you complete your decomposition you've reduced the interface problem to a set of logical problems, which cannot all be ideally solved. You will have to make sacrifices in order to get things working (this is the engineering part of software engineering).
Let’s try to tackle one of these difficult problems. Let’s say you were modeling a city full of things (i.e. people, cars, mailboxes, ect.). These things can then perform certain actions on each other. For example, the car might be able to change the location of the person, while the person might be able to change the direction of the car. To build our city, we start to define class after class. But we run into a snag...a mailbox can hold a lot of things. It can hold letters (a long string of chars), it can hold a ball (x,y,z), ect. The mailbox needs to know how to accept the data it’s getting from the object and how to deal with it. In fact, this occurs everywhere; an oven for example needs to treat a bit of dough differently from a rubber ball. Let’s start to decompose. We have several elements already defined: - we have a bunch of things - they can perform actions that are predefined - they have attributes that are predefined The question is, how do we connect the attributes and actions? This can then be broken down into two sub problems. - How do we transfer data that can potentially be any type and quantity (i.e. chars, floats) to the action? - How do we deal with that data once it’s in the action? Now our decomposition is finished. We are capable of identifying the major parts of our problem and can now implement a solution for it. I however, am not going to implement the solution; I'm interested to see what you guys come up with :)
For the next tutorial, I will be wrapping SDL's input library as well as taking care of a few things. Again, sry for the delay.