Friday, February 23, 2007

Baby lisp


So the rpg has come on a little bit. I really ought to come up with a name at some point. For now, it stays RPG Zero.

New since the last update:
  • Tried to refine the look of the thing. I'm going primary colours, sharp contrasts, simple forms, abstract surfaces and everything easy to put together quickly. Also leaning a lot on vertex colouring for the low frequency shifts.
  • Our protagonist can now be hit by badguys, and keeps track of his health.
  • First blush at a HUD, and UI framework to back it up.
  • A chest! What's an RPG without a treasure chest, eh?
  • Refactored the player control to remove some annoying kinks, like sliding while idling.
  • Wrote a *secret* story outline!
I mentioned last time that the scripting language in this thing was a bit... odd. Well, here's a look at what a typical dev session is like.


Seem oddly familiar? It's not really lisp, but it does share some characteristics with it. Chiefly: code is data. I can pass it around, chop and change it, store it and call it later. The scripting language is interpreted, which means it inherently has the ability to inject novel code into the currently running session. Godsend that, when working on a game. No compile waits, you're right there, in the game, where you can see what you're doing immediately.

It's called Stutter.

It's implemented as a runtime interpreter that is plugged into a reflection based dispatcher. I have a class that contains all the intrinsic functions. I then have a few intrinsics for dealing with .Net types directly, cnew (new a C# object), cset and cget (set/get properties or fields) and call (invoke a C# function). These are for... well cheating. My character entity for instance, is a C# based PhysicalObject, that gets initialised and bandied about by Stutter, but lives in the Game.Components update loop.

Why not real lisp? Well, I couldn't find a handy implementation, and I've still a lot to learn about building and implementing the support for an embedded language, so it seemed like the right time to learn. And the right time to experiment. I've taken the things I like about lisp, and screwed around with the formula to give me the things I've always wanted for games. Will it work? Will it be fast enough? Will it be expressive enough? Who knows? That's the whole point of trying.

I did mention this whole thing is one big bout of self indulgence, didn't I?

P.S. Today's vid is higher res than the last one, so you might want to click on over to the bigger Google Video player to see it. Today's music is Billy Bragg and Natalie Merchant doing Way Over Yonder in the Minor Key. Totally love that tune.

Tuesday, February 20, 2007

RPG Zero: Another Start



So Ultrahead, over at Do as I say, not as I do... in a comment conversation reminded me that I've always wanted to make an RPG. Abandoning all the other toys (I have a habit of doing that), I've decided to embark on the adventure.

When I started writing about game programming here last month, I really did want to practice a little altruism and share some stuff with the community. I'm afraid the vanity levels on this project however, make it wholly unsuitable to share as good practice. The sheer self indulgence in the code should warn people away. I really can't in good conscience recommend anyone learn anything from this... the scripting language is an own rolled, interpreted, prefix notated, reflection driven nightmare for pete's sake!

I would like to share though. So if anyone is interested in watching, I'll post the goings on in my RPG land.

I thank you for your patronage,
GLH.

Wednesday, January 31, 2007

Shooter Zero: A data driven sample

So, in the last programming post I tried to dump what I know about data driven architecture onto a rather dense page. Here I'd like to share with you a sample application that might shed some light on what I was trying to say.

The ShooterZero.zip file over on my google pages page contains an XNA project that is a 2D shooter game framework.

The code describes a series of classes that together provide services that a shooter will need:
  • Resource management and a game loop with debugging functions (even a simple console!)
  • An object model, where all elements of the game are Object Instances that are created from Object Definitions
  • A scripting language to define the behaviour of Objects
  • A set of services that the Object Instances can employ, e.g. intersection, rendering, input, etc.
  • A parser that can create Object Definitions and Object Instances
The project comes with 3 XML file definitions for 3 different kinds of shooters.

The implementation here is admittedly very slapdash for lack of time invested, but I believe the principal features of the kind of data driven system I'm trying to demonstrate are evident:
  • Game is setup with parameters read from a data file - one engine supports a variety of expressions by implementing common primitives.
  • Objects in the game are defined using data driven templates, both properties and behaviors - data is parsed into logical collections of primitives.
  • Object instances are created from data - separate structure from content.
  • All the Data is reloadable at runtime - faster iteration time when it comes to implementing content.
So, if you wish, download and have a play with it. Questions are welcome, and if there's enough interest I could write a few more posts that break out the details.

A Dark Day For Gamedevs

An article over at gamesindustry.biz is painting a horrible picture of what's just gone down at Sports Interactive. In summary: a leaked pirate copy of Eastside Hockey Manager has killed legit sales so badly that they've had to can the game. Not any pokey little game either, we're talking a critically acclaimed game. They didn't make their money back and they can't afford to pay the hockey team licences in the game.

Real devs just lost real work. This isn't a theoretical discussion about piracy, it's real actual people who work to make games who have had their livelihood directly affected. No sales means no recoupment, means their investment down the drain. Thank goodness the company can survive the blow and the guys haven't been laid off.

To the pirate to hacked this one: hope you're proud.

Sunday, January 28, 2007

Data Driven Game Architecture

You hear the term "data driven" bandied about reasonably often in gaming tech circles. Why is this? Here I'll present a summary of my experiences in the matter.









Goals:

  • Laziness. We want to write the least amount of code to deal with each instance of a feature of our game. Why on earth write a whole class for a new bad guy if we can get away with a bit of data instead?
  • Laziness part 2, or maintainability. As a consequence of laziness, we want to end up with the most compact form and the least possibility for errors in our instances.
  • Quick iteration. Reloading a bit of data in a running game is much quicker than compiling and restarting. We want to spend time on the game, not the structure.
  • Encapsulation. Games change during development, usually quite a lot. Data is easy to add and remove from projects whereas code can all to easily get entrenched.
  • Devolution of responsibility. The power to actually carry out game implementation begins in the hands of the programmer, but not all aspects of implementations belong there. Data, and tools to edit said data, let you shunt that power to whomever requires it.

Bonuses:

  • Better modularity. We end up partitioning functionality in our code more naturally when we're thinking data driven. As a result, you're also more likely to get recyclable code for next time.
  • Easier serialisation. Writing your save game module can be made easier by a nicely data driven framework: the data to save is usually easily found and easy to mark out.

How to go about it?

  • For a given feature in your game, collect a definition that encompasses all instances of the feature. For example, if you're making an RPG you'll need NPCs. Each NPC might want to be able to engage in a conversation, query information from the world state, be able to retain and be queried on its state, etc.
  • Figure out what portions of your definition will be the variables between your instances, e.g. each NPC will have a different name, a different description, a different storyline, etc.
  • Figure out what sort of data will best facilitate describing those variables.
  • Start out right! If you already know you'll need this, invest in the time to get it right. Sure you can hack in something hard coded to start with, but you'll later pay the price in lost momentum when you least want to.

There are two broad divisions on methods of data driving a game.

  • On the one hand, we have the simple property bag. This is passive data that simply sets up a few variables for game objects. Simple RPG weapons, for instance, where each definition is a name, a description, damage information and some art resource references. My preference for this sort of data is a nice XML file. XML is robust and easy to parse, there are plenty of decent raw XML tools to leverage (especially if you write a schema to go with it), and there are plenty of mature XML code libraries to work with. That last part is particularly important as it makes a painless transition to a binary format, should it prove necessary, trivial. XML isn't perfect, but it's easy and it's there.
  • On the other hand we have the more complex behavior bag. This is an extension of the property bag where we also add some scripted behavior specialisations to each of our instances. Enemies in a shooter for instance: where each enemy is defined with some art resource references, and callbacks that contain code fragments to describe their AI. There are a lot of ways to implement this, a virtual cornucopia of choices. You have all manner of embeddable scripting languages like Python or Lua that are quite mature now, and plenty of experimental smaller ones if that's your bag. Depending on your exact requirements, rolling your own might even be an option. There's a lot to say for domain specific languages.

Things to watch out for.

  • Don't get carried away. Do your homework first, and make sure that you really do need to data drive a feature. There is some overhead to pay in terms of implementation time, and it's not a cost worth paying if all you'll do with it is define the one instance that never changes!
  • Remember performance. You should absolutely be willing to pay some performance decrease in exchange for data driving, as obviously you're trading that for a quality upgrade from the faster design iteration. Be careful not to be disastrously generous with this trade though; as with all things be pragmatic about the end result. Above all do not fall into the "we'll fix it later" performance trap. The industry is littered with horror stories where teams have leaned on a scripting language only to spend half a year backpedaling out of it to get the frame rate back to 30, or get the memory footprint back down to the release configuration.
  • Beware the lure of generic! When building your data driven system, it is intuitively tempting to continue to generalise the implementation. In fact, as you continue to work, you'll keep getting struck with "Hey, this is just a slight specialisation on that other thing!" Be careful here! All problems in your game are implementable in an ultimately generic structure: the programming language you're writing the game in. Hold fast to the idea that what you're doing is in fact specifying groups of solutions to remove the redundancy of implementing their variations generically! In other words: if you end up embedding a scripting language into your C++ game and then writing nothing else but script for every feature of the game... all you've done is swapped programming languages. This is not necessarily bad (and possibly a topic for another time), but it's not an action towards the ideal we're discussing here.
  • Continuously evaluate where you are. The whole point here is that you're optimising how you write the game. If you end up haemorrhaging more time than you otherwise would have, you've made the wrong choice. Often it's too late to back out, but learn all you can from the experience.