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.

13 comments:

Ultrahead said...

"... and if there's enough interest I could write a few more posts that break out the details."

I'm interested ...

Unknown said...

I'd love to see more detail on this

Karl Fredberg said...
This comment has been removed by the author.
Karl Fredberg said...

Nice blog you got here!

I get an error when I try to run Shooter Zero,
"Input string was not in a correct format"

at this line:
"float debounce = float.Parse( function.GetAttribute( "Debounce" ) );"
I'm a newbie and don't know how to fix it...

And I as well like to see more details on this topic.

I also like to see more details on the design structure post. I use Cornflower Blue's method in my attempts at creating games. It works for me but after reading your post I would like to try your method as well, but I don't know how to implement it. If you could post a sample on that too I would be most grateful.

Karl Fredberg said...

ps. I think it might be a culture error, I'm from Sweden in Europe. ds.

Gamey Little Hacker said...

It does sound rather likely that it's a globalisation thing. Doh! How embarrassing...

I have to admit, despite having done a ton of localisation work for games (I wonder why we name it the other way around?), I've yet to do so for a C# app, so I beg ignorance here.

The way I see it, we have two options: either make the scripts use the Invariant culture (as per most programming languages I wager?), or allow globalisation in the scripts. Which choice would be right would depend on the project's deployment pattern and intentions.

For us, I reckon invariant is the way to go, with the assumption that we want our target games to work with any player.

If you'd be so kind to test the fix for me (to make sure that really is the problem), I'll roll it into the zip file.

Please search out any float.Parse functions (there are about half a dozen) and add the invariant culture specification:

float.Parse( string );

becomes

float.Parse( string, System.Globalization.CultureInfo.InvariantCulture );

There's also one float.TryParse in there that needs changing from

float.TryParse( string, out float );

to

float.TryParse( string, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float )

Karl Fredberg said...

That seem to be the trick! Works fine now.

Gamey Little Hacker said...

Thanks Karl.

I've uploaded a replacement zip file with the appropriate changes made.

Unknown said...

First of all, thanks Mr Gamey Little Hacker, this shooterZero framework is a real educational surprise !

For a while I was tweaking my code to achieve what you call "data driven" way of doing (I'm a demo coder not a game coder so I reinvent the wheel each time) and when I looked at your shooter code, I said 'Whaaaaah, amazing, exactly what I want to achieve'

So I have played with it and I started to modify it to add something not available, mapping an objectinstance to an existing DrawableGameComponent.

For example, when moving, sendign a message to the "Background object" which is linked to the Background DrawableGameComponent to change its scroll direction.

So I was wondering if it is the good way to do, I mean adding a reference attribute to the obejct definition which is going to reference the Background object instance, to transalte the Message received into Background scrollUnit modification.

I planned to use this ref feature for any external complex DrawableComponent (tile engine, UI boxes,...)

Am I wrong ?

Gamey Little Hacker said...

No, that sounds perfectly reasonable shazz. There are plenty of ways to do this sort of thing, and your proposal, if I understand it correctly is definitely a nice modular one.

To see if I understand this, you propose to:
* Add a way to make ObjectInstances with a reference to another C# object.
* Implement a message handler on that other object that can receive messages from the scripting language.

Sounds good to me. What mechanism will you be using specifically to implement the message despatching?

Unknown said...

Thanks for the feedback !

In fact I'm adding a lot of stuff into the framework (I'll be pleased to send it to you for review ;-)) like
- new movements : Jump
- AnimatedVisual (for multi frames sprites)
- a Tile Engine
- idle control (when to key is pressed => set Stand state)
- ...

so for the Reference stuff for the obejct definition I did it like that :

-> added a Reference tag for object tag
< Object TypeName="Background" >
< Reference >
< Ref typeName="Background" Value="Ocean"/ >
< /Reference >
...

-> added message sent from Player to Background ObjetInstance

...
< Control >
< Left >
< Set Parameter="Direction" Value="-256,0"/ >
< Set Parameter="Rotation" Value="-25"/>
< SendMessage To="TileMap" Message="Left"/ >
< /Left >
...

-> added ref to the Background object in the Level object
< ObjectInstance TypeName="Player" Position="64,32" / >
< ObjectInstance TypeName="Background"/ >
< ObjectInstance TypeName="Level" >
< Object Name="back" Value="Background"/ >
< /ObjectInstance >

the only little stuff I add to take care is that the message will control the background scroll but as no message is sent when nothing is pushed the backgroudn will continue to scroll... So I had to tweak it.

so it looks at that you thougt no ?

If you'd like to look at the code just give me an email !

thx again, I love your stuff :D

Unknown said...

and for the message dispatching, I have added in the ObjectInstance Draw() :

public void Draw()
{
if ( IsDead )
return;

// Draw the kids first
foreach ( ParameterInstance p in parameters.Values )
{
if ( p.Value is List< ObjectInstance > )
{
List< ObjectInstance > objectInstances = (List< ObjectInstance >) p.Value;
foreach ( ObjectInstance i in objectInstances )
i.Draw();
}
}

foreach (KeyValuePair< string, NamedDrawableGameComponent > pair in definition.Reference)
{
NamedDrawableGameComponent dgc = pair.Value;
object o = this.GetParameter("Position");
definition.Draw((Vector2)GetParameter("Position"), (Vector2)GetParameter("Direction"), dgc);
SetParameter("Direction", Vector2.Zero);
}

as the drawableComponents references are stored into a dictionary

Anonymous said...

Thank you for the post and the sample code in a game project. I'm having trouble finding good examples of data driven code and may look at your program later if I get stuck.