Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Sunday, November 14, 2010

Language features...

So at what point do language features become a hindrance? Well having learnt using BASIC, I know what the features of other languages has allowed me to do. SO lets have a little lookie and see what these features have added.

First up: TYPES.

I remember coding on a ZX Spectrum and being incredibly jealous of the BBC as they had INTEGER variable types. This option allowed your code to run MUCH faster than using the standard floating point number variable. Now, back then ALL floating point code was done through emulation and so was much, much slower. Nowadays, doubles (which is what Game Maker uses) is all done in hardware, so why do we care? Well first, it's not always done in hardware, so we're back to the CPU doing emulation and that's really bad. In fact, even is we're doing this on a machine with hardware for doubles, integer computation is simply faster, what with the multiple execution pipes and single cycle execution on most hardware, if you can do it in integers, you should at least have the option. After all, how many FOR loops really need floating point? Lastly... How much would this really affect folk who don't care? I'd argue not much. For example...
   i = 1.12;
b = 12;

compare that to something like...
   INT  b;

i = 1.12;
b = 12;

I'd say if you don't want to use them, it's not exactly going to bother you, but if you want to declare the variable (with INT, DIM, VAR or whatever we end up using), then you will not only get a speed boost, but it'll help you debug your code because you KNOW it can't be a fraction! That can be a valuable bit of info.
Second: STRUCTS

I'm wondering how I can even begin to say how important this is. Lets give a couple of examples. (this isn't proper code, so don't jump on errors please!)
    BaddieID[i] = id
BaddieGridX[i] = round( id.x/16);
BaddieGridY[i] = round( id.y/16);
BaddieType[i] = enemytype;
BaddieParent[i] = id.parent;

Now, in this little sample, we need multiple arrays to deal with storing all the bits of information I need for processing later. This is pretty common when coding and having multiple, dynamically resizing arrays is a nasty thing. So what can we do to improve this? Welcome to the world of structures.
   struct SBaddie{
id,
gridx,
gridy,
type
parent
};

baddie.id = id
baddie.x = round( id.x/16);
baddie.y = round( id.y/16);
baddie.type = enemytype
baddie.parent = parent;

Baddies[i] = baddie;

(I'm deliberately avoiding adding any types here... but you can do)
Now... how I'd hook all this up is unclear, but having a single object (much like a standard, but very lightweight Game Maker object) with variables you can access can mean you can contain data in a single packet. This means you no longer have lots of dynamically resizing arrays (which is always a good thing), but if we are to expand what can be passed into functions, it also allows you to pass a lot of data in a single blob, removing lots of parameter stacking. This is all good, not only from a performance standpoint, but simplifies your think about about variables you're dealing with. No longer are you thinking about lots of individual variables, remembering which ones do what, you now have a single variable with all the information you need and this again simplifies your work.

Structures are good. Structures are VERY good.


Third: FUNCTIONS.

Being able to breakup large functions into smaller common parts is nice, not just from a readability standpoint, but for allowing you to reuse code better. It's a very good skill to learn; making code general so you can use the same function over and over again. This not only teaches coding flexibility, but allows you to start to make an API for certain features. Good APIs are a real skill, and one thats vital to learn if you ever want to progress as a coder. All that said, if you don't really care about coding and it's simply a means to an end, then being able to break your functions up does make code just simpler. Having a function that is pages and pages long is horrible to maintain, and will introduce bugs, so this would also help you reduce bugs as each function is smaller, and easer to think about. So again, its another good one.

Fourth: CONSTANTS

Simple one. PROPER constants. This is mainly an internal thing... but allowing you to define them in code would be great.


Now... I'd also say there are features that would just confuse most folk so we should just avoid them. Things like anonymous delegates, the C++ << style operator, operator overloading, templates, and even to some extent #defines; these are all simply not required inside GML. While I do like #defines, I think they can be so badly used, I'd simply avoid them.

EDIT: Oh... and the other thing I'd LOVE to add; argument passing to the instance_create(x,y,obj) function. This would be brilliant. This would allow you to do stuff like this...

    w = instance_create( 10, 10, cBullet, "smallbang.wav", false, sSprite );

I could have used this quite a few times already....

Monday, October 11, 2010

Writing cross platform code...

You know, it occurred to me that some folk may find it interesting to see how we manage to port the Game Maker runner to other platforms, so I thought... Let's write about it. I then thought, well... it's not specific to the runner, Russell and I have been doing this for almost 20 years. I'll use the runner as the example, but the method holds true no matter what you do.

So the first thing you have to get into the habit of doing is abstraction. This doesn't mean layer upon layer of code and calls, but a very thin level that can remove any real platform dependence. Now, since I'm a graphics guy at heart, I'll talk about abstracting graphics code, but the theory is the same be it networking, audio or a basic file system. So here goes...

When we got the C++ runner it was all tangled up with windows specific code, from DirectX to MFC (Windows Foundation Classes), so the first thing we had to do was remove all the specifics, well... as much as we could. We spent months removing MFC and started using a simple WIN32 interface as this is much simpler and gave us control over the main loop which was vital to actually running on other platforms. We also started to add a thin layer between Game Maker, and DirectX. This meant that instead of calling D3D directly, it now called one of our functions, and we called D3D. This was done throughout the code, from creating textures and surfaces, to rendering lines and triangles.

The first thing we did was takeover the screen/canvas creation. This means we're now creating the device and have access to all the normal D3D functions inside our little world, and as far as Game Maker knows, it's simply asked to OpenWindow().

Next, we want to get something drawn, so we change all the rendering calls to a call to our triangle rendering instead so we now have access to all the vertices and can draw flat/coloured triangles where all the sprites would be. Most of these changes are pretty straight forward, and are a simple search/replace. Change DrawPrimitive() into a DrawArray(). Once that's done, we can start to change ALL the primitive types from being D3D specific, into our own custom values. So rather than using D3DPT_TRIANGLELIST, we now use our own ePrimType_TRILIST. So far so good.

Now there's lots of drawing code inside Game Maker, fonts, sprites, tiles and backgrounds and so on, and we really don't want to have to rewrite these every time. However, now that we have something that will render triangles in an abstracted way, we can rewrite them once, into OUR format. So the font rendering will now make vertices into our buffers, and use an ePrimType_TRILIST to draw them. Hay-Presto! The font code no longer needs D3D to render things. But what about the textures it uses?

Well, we created a new CreateTexture() call as well, which returns a simple void* which then allowed the internals of texture management to do whatever it liked. It also has the advantage that the texture system can now resize/resample textures if it liked, as Game Maker will never know; very handy when memory is tight! After all, Game Maker doesn't really care what the call is, as long as it can set the texture, so this works well.

So now we have the ability to render primitives, and create/change textures, so what else? Well there are lots of render states to do, so we'll need to abstract them as well. This means things like the D3D culling renderstate (i.e. D3DCULL_CW) now changes to eCull_CounterClockwise and so on, until every state, every D3D call has been abstracted away into a new interface. But why bother? Well, for a start it means that we don't have to include the D3D headers on other platforms! D3D obviously has windows specific includes internally and that would end up being a nightmare, but my abstracting things a little, the other platforms have become much simpler to port to.

And, although it seems odd... D3D is in effect our first port. Game Maker now runs using YoYo's interface and API, which then has a set of classes which translate things into D3D calls (which has been upgraded to DX9 BTW). Theres lots of other functions which we abstracted, matrix operations, viewports, grabbing screens, surfaces and render targets and the like, but at the end of it, we have a clean, non-D3D interface. And we can now port it easily.

Now, this method is true of any platform specific API you care to mention. We also changed the Audio system on windows to XAudio2 (the latest DirectX audio system), and we did this by simply having calls to Load a sample and return a void* which Game Maker can then use as a handle to the sound,midi or MP3.

Now... the real trick here is to get most of the code to be platform independent. Although things like fonts draw to the screen, they don't have to know anything about the underlying API. All it wants to do is set a texture, some blend modes, and then give you some vertex data. The interface then handles the rest.

Once you have a set of files that ARE platform dependent, you can then port these to any system you like. The real win for this, is that any new feature you add to Game Maker, will usually appear on all the platforms at once. Only the most obscure thing like grabbing screen rects without using the CPU (the PSP needs this) will have to be hand crafted. But other things like optimising font rendering will be the same on all platforms, and only require to be written once.

So, for the record.... we now have a Win32 DirectX 9 render, and PSP render, and an iOS OpenGL ES render. Making a Mac OpenGL render would now be trivial as the OpenGL ES is actually a subset of it, so we can actually ADD features!

As you can see, making a true platform independent bit of code is fairly simple, as long as you think ahead and don't try to be too clever. It also means that all systems benefit from any core changes, and porting to another system can sometimes be achieved rapidly if required.

Now, porting to something like iOS is interesting because your supposed to use Objective C, but in reality... anything that can call C++, can use the whole runner. We use Objective C to create the display, flip the screen and get the touch input, but everything else is done in the normal way using the C++ runner. Any system that allows C++ code, can be done like that making it a trivial port (i.e. simple to do, but takes a little time). If a platform doesn't allow calls to C++ (like XNA), then this means you have to write directly in the provided language, and that then becomes a monster task to rewrite the whole engine. It also means any improvements to the C++ code must then be replicated to the new system, which isn't very nice.

The Game Maker C++ Runner is now very portable thanks to the abstraction it's received, and we also know it's very hardware independent as it runs on Intel, MIPS and ARM cpus. Different CPUs can also give some headaches as byte order can flip, and many systems don't let you access INTs on non-INT boundaries and so on. We've now been through all of that, and now have a very portable engine which is looking good for the future.

So there you go... This is how we ported the runner over, and how other platforms suddenly seem to spring up. All that's really missing from it are some of the features we removed initially to get it all working, and they will come back in time, and one day, the C++ runner will be THE way to run Game Maker games.

Tuesday, July 27, 2010

What makes a profesional programming language?

One thing that I find a lot working at YoYo games is the disrespect GameMaker's built in scripting language (GML) gets. You see it all over the various forums, and in responses to glog entries, that if you really want to make games, you should learn a proper language. This does annoy me, and I'll explain why in a moment... First, I want to explain where I've come from in terms of games, programming and how I got to where I am today.

So, when I was around 13 a friend of mine got a ZX81 and we'd sit and play games, type in programs from magazines and on occasion try to make the computer do what we wanted it to do. Later, I bought the ZX81 from him and would make little games at home in BASIC, and then started to learn Z80 assembler. I then progressed to the ZX Spectrum and wrote a database for a solicitors office using Sinclair BASIC, and the progressed to the Commodore Plus4 where I wrote a few games in a mix of assembler and BASIC. Eventually I progressed to full assembly programs and moved onto the Commodore 64 and my first job at DMA Design. While writing Ballistix and Blood Money I learnt Pascal and wrote several tools on the PC for development, and when I progressed to the PC Engine and its 65c02 assembler, I again wrote many tools in Pascal, including a full (and very powerful) remote debugger. The same goes for the SNES and 65816 assembler, while all it's tools were in Pascal on the PC. While coding on the SNES I started to learn x86 assembler on the PC and did lots of game stuff in Pascal and x86, including the prototype that lead to GTA.
I then moved onto C and used x86 for all rendering stuff, and eventually SIMD and C++. I did lots of JAVA here including mobile phone J2ME and Java Applet code, including some Java Virtual machine assembler (if you can believe that!). I then did PS1 and PS2 C, and learnt some MIPS assembler while also playing with the Gameboy advance and it's ARM assembler. At home I was also doing some Dreamcast stuff and some SH4. These days I hardly ever touch x86 assembler and it's mostly C/C+ or C#.NET, although I'll probably do some Delphi pascal again soon. There you go... a pocket history. I've missed loads out (particularly home projects), but it's pretty close.

So... Why do I bring all this up? Well, you can see that I've progressed through many languages and use whatever is appropriate. On the PC where the machines are fast (these days), I don't need to dip into assembler any more, while on the PSP I'll happily drop down to MIPS assembler again.

So what makes a language? Well, you have variables, loops, branches and function calls. In more object orientated languages, you also have objects you can access, but these certainly aren't a requirement. Old machines like the C64 or spectrum didn't have them, and they managed quite well without all that. You just arrange your data differently, but it's all pretty much the same thing.

GML has all of this. The loops and branches are there, as are variables and function calls. In every important respect, it's a full language. Some might even say it's much nicer to use as you don't have to worry about lots of things and it makes it much simpler to throw things together. There are lots of ways to write games, I used to write in BASIC as did many others, some still do. There's plenty of jobs around for Visual Basic programmers, so this is definitely a proper language, but I would never write a game in Visual Basic. I would use a language better suited to making a game. GameMaker gives you LOTS of tools and support to quickly make games, just like many other game making tools do, but very few pull everything together for you into such an easy to use package.

So to put this in context. There IS no professional language. You use whatever you must in order to make the application or game you want to make. If you make a game or application, and you sell it, then it's a professional language, it's that simple. There have been many games created and sold that were done inside GameMaker, and there will be many more. If you make a Tetris game, can the end user tell that it's written in Game Maker? Would they care? No of course not... If your talking about a game, then the end user just wants to have fun and only cares about if your application delivers that.

Now... if you want to make a game for console, then Game Maker might not be the right tool - yet!! But that may well come, then you'll have a much wider choice as to what the right tool is.

In the old days we used to mock C programmers for using slower languages than assembler. We could do at least twice the amount, but they would develop twice as quickly, and now we hardly use assembler at all! We're getting the same thing with GML. Unless your talking about making Halo or GTA 6, then GameMaker and GML may be just fine. I suspect all 2D games (pretty much) can be done with GameMaker, and a LOT quicker than if you tried with C++ and DirectX too.

So... ignore the requests to learn a proper language. Use what's right for you, and what makes your life easiest.

Sunday, October 4, 2009

Designing a good API

So I've been doing some work porting some code to another platform (which I'll speak about later), and it's showing a severe lack of API design. Now I design APIs for a living I thought I'd quickly go over what I think makes a good API, not just from a users point ov view, but internally as well.

Simple and Fast. And we're done. Thanks for reading.

Okay... I'll go into a litte more detail. Now, API's are usually there to give a coder simple access to a more complex system. Take DirectX. The underlying hardware and systems are pretty complex these days, what with interrups, DMA chains dynamic memory management and all the rest of it, yet the API is (reasonably) simple. So... here goes.

Simple. An API must be simple, in fact as simple as you can get away with. This isn't to say you should make it basic, no. You should make it do everything a coder will normally have to do, but don't over complicate it by using 1,000,000 calls for each function. Take DirectX texture creation... Now, whenever I do a graphics engine I have a single CreateTexture() function where as DirectX has several. Textures, Surfaces, DepthBuffers and Cube Maps, the list goes on. Now why? Theres really no reason for that kind of split. You could just as easily use flags and paramaters to allow the various types of selection. This means the coder only has a single function to learn, and if you follow this kind of rule for the whole API, then the entire system becomes much smaller. After all, would you rather a 1,000 call API where you have to set the width and height of a texture indavidually, or 10 functions that can do everything! The smaller the API, the more control you can keep over it. If you give access to every single function and variable, then it becomes a nightmare to change or upgrade.

So, keep an API simple. It's less to maintain, and the programmers that use it will thank you for it.

Speed. This is obvious. It has to be fast. If your management code gets in the way, then the API will become too expensive for coders to use properly, and they'll end up writing support functions themselves. This is really bad. You want the programer to have confidence in the API, not only that it'll do what you say it'll do, but that it won't slow him down. Streamline as much as possible, remove as many if's and but's as you can inside performance critical areas.

Now, a quick word about abstraction. It's important to abstract certain types of API so that it's clean and portable. Now theres a few of reasons for that. First you expect the API to change under you. If your using some open source interface, you never know when the latest buzz word is gonna take hold and your whole API is gonna change, so a simple layer of abstraction will protect you. Next, it will allow you to add value to an underlying API. Take DirectX texture management, if you add a simple layer to your API, you could then now keep track of all your textures, and it'll allow your to manage things like device resets (when you resize the window etc.) as since you own all the texture pointers you can free/reallocate things like render targets automatically. Lastly... Portability. Now most hobby projects don't care about this, but you never know when your project might take off and someone else might want to port it for you to a Mac, or a ZX81...or something.

The program I'm currently working on was hard coded for windows, using not only MFC, but DirectX calls and enums directly. So, being someone that designs APIs, I know all about abstraction. Being someone who's worked on lots of multi-platform games, I know all about how to make an API platform independent. So, thats what I'm doing. All calls to DirectX and being put through a layer of abstraction, each ENUM is being changed to be platform agnostic. This means the API can translate (very quickly) to whatever rendering API I want, without the main application knowing, or caring. The classic case is DirectX and OpenGL. Doing this would allow this program to run on the Mac, without the main program changing. I'd only have to port the (now pretty small) API, and not the whole program.

If you've done your job correctly, you'll have to port a handful of API's. Sound, Graphics, Networking and simples file systems and memory management. All games can be based on these simple API's, and then be made to run on other platforms (reasonably) easily.

Theres obviously lots of ideas for each API. Designing graphics APIs is an art in itself. I've used the same API for the last 10 years. The underlying graphics systems have been PS2, XBox 1, DirectX 8&9 and I know it would work just as well on a PS1, XBox360 (never seen a Ps3 SDK), and DX10/11. A well designed API will work on any platform and be a pleasure to use.

So there you go... Keep an API simple - don't get sucked into the latest fad. Keep if as fast as you possibly can. And abstract to reasonably protect yourself from underlying systems and to allow simple porting.