Entries in componentarchitecture (5)

Wednesday
Jan302013

Performance design patterns and post mortems for component architectures

Latest version of our component architecture research (from September 2012) is now up and available at the 360idev videos site.

From the presentation abstract:

This talk is a follow up to my 2011 360idev talk on component architectures for iOS games. It includes specific in-the-field post mortems and performance analysis from the implementation of a variety of component systems, implemented by myself and others (including other 360idev regulars).

The performance measurement elements are applicable to all architectures and the post mortem information is full of technical goodies about iOS implementation detail and nuance.

Saturday
Jul142012

Component stuff; new blog; The Satanic Games

I started blogging over at garethjenkins.com as well as here.

Recent stuff includes:

 

 

This blog/site will likely be more focussed around 36peas stuff and garethjenkins.com about whatever I feel like writing about. Probably a good bit of crossover on game dev stuff, so I'll continue to cross-link where appropriate.

G.

Friday
Sep302011

Blackboard storage for your game's component architecture

In this article I've outlined a couple of ways this kind of data storage can be used alongside component-based stores for global state and shared data. You can find examples of this implemented in our latest component prototypes.

 

Background and context to date

To date we'd been sharing global data using one of the following methods:

  1. The GameWorld's local store (a dictionary)
  2. Properties on the GameWorld object itself
  3. In-component referencing / lookups

These have all got problems -- the first requires proper management of lookups, is slow for common lookups (each lookup is a key search) and is clunky for access outside of the game world (i.e. by controller interfaces; the second requires modification of the GameWorld class -- this feels very wrong and makes the GameWorld code completely unportable, and; the third leads down the road of interdependency and presents similar problems to traditional OO hierarchies (inflexibility, obfuscation and bottlenecks).

No perfect solution

A component-oriented design really should be as component centric as possible, but there are cases where there is no clear "component" solutions, for example:

  • GameWorld <> Application <> OS communication
  • Controller interfaces and attachment
  • Game state
  • Caching

The blackboard architecture helps with all of the above cases.

It also helps with more debatable components -- things like map managers -- where there's a strong case for externalising the functionality of a component but also a desire to have belong in the game world (especially for the purposes of messaging, updates etc). Other components can acccess said map managers directly as if they were singletons or external systems, but they can also participate in the gameworld's messaging system or update loops as required.

How to implement a component-friendly blackboard data store

We're simply creating a blackboard class and hosting an instance of it on the GameWorld object itself. This means that:

  • Specific data stores are exposed through properties
  • As a result data access is type safe
  • The blackboard can house it's own LoadData function to populate from persistant storage if required
  • The blackboard class itself is project-specific but it's hosting in the GameWorld is generic (keeping your game world, objects and components generic utilities)
  • Access is direct for all components (e.g. component -> object -> world -> blackboard)

Smarter game components and behaviours

Abstracting away shared storage has a couple of other benefits at the component level as well.

A great example of this is, in the iOS/Cocos2d space, sharing sprite batches between instances of the same component. Our simple BCharSprite is instanced in a number a different types of game objects and hence is difficult to build factory and shared methods for -- it's a true component in it's isolated behaviour. However, we want to be able to re-use sprite batches when the sprite sheets themselves have already been loaded by another component. This is accomplished very easily using a blackboard using a single NSMutableDictionary accessed as follows:

 

Should I use a blackboard for storing data in my game's component architecture?

As with anything component-oriented (and really any software design) this does require some forethought and consideration -- you need to understand why you are implementing this kind of storage, how your comopnents are independent and (as they inevitably will be in some way) interdependent.

There is no perfect solution, but there are lots of great ones. This seems to be working for us so far.

More information:

Thursday
Sep152011

Lightweight component architectures and object composition -- including some "sample" code

Summary of the latest component stuff published at 360idev yesterday...

We use a pretty lightweight component architecture for our iOS projects. It looks exactly like the structure above.

  • Game World -- container for game objects; entry point for broadcast messaging, update loops, cleanup messaging and object removal
  • Game Object -- container for components (or behaviours as we actually call them in code); parse for loading objects from templates; proxy for messages, loops and cleanup
  • Component -- a specific piece of encapsulated game logic or functionality

The component is the critical thing -- this is where you put your stuff. Game objects are a composition of components. I'm not going to try and explain that here -- I suggest you check out the links below, or wait until John gets the session recording up from 360idev in which I talk about this quite a bit.

It's critical not to get hung up on trying to implement or decide upon a perfect architecture. There isn't one.

Remember:

  • Object-orientation isn't bad -- but composition is very different, so they end up competing
  • Though most composite systems utilise OO structure and design in their implementation
  • Messaging and data storage/access need to have solid design (there are several options -- see links above) before you start coding

In implementation that we use, a component (behaviour): 

  • Gets used more-or-less generically
  • Receives messages and acts on them
  • Sends messages to it’s game object or the game world
  • Reads and writes data from the game object’s store
  • Handles messages on the update loop (and draw loop potentially)

Additionally:

  • The components contained in an object represent the only formal definition of that object -- don't add or remove components to an object after its initialization
  • You need to be watching performance -- certain tasks (like key lookups on a shared store) can be expensive if done frequently (design around these or embed other systems within your component architecture)

360idev Game Jam example and code

The Game Jam's theme was "opposite" and the entry I put together was titled "Fungal Adventure." Summary of the game is pretty much:

  • 2-player local iPad game (vs -- with players on opposite sides of the device)
  • Move your dude left and right
  • Objective is to get to top of screen to win, or to force other player to die
  • Eating a mushroom makes you go faster for a little while
  • All mushrooms have some effect on you -- which must be countered by that mushroom's counterpart (edible<>toxic; psycho<>medicinal).
  • If you don't eat the right counterpart you die (and lose)
  • Eating any mushroom also swaps all other mushrooms of that pair (i.e. all edible become toxic etc)

It looks like this:

Aside from the obvious game wrapper stuff (menus, scores, tallies etc) I'll be following this up with some additional features and changes before releasing it, including:

  • Swap out one set of mushrooms for something else to make it a bit clearer how the pairs work
  • Add an AI for single player use
  • Add some other obstacles and game-state-changing elements

If you'd like to take a look at the code itself (which I know at least a few of you do judging by the numerous requests and feedback I got after the session), you can download it below.

However, please read the following disclaimer:

  • It was written in a 12-hour game jam on zero sleep

Which means that:

  • It's incredibly sloppy
  • There's very little memory management
  • There are huge performance problems if you replay the game a few times
  • Sometimes you can't win
  • Occasionally mushrooms spawn on top of a player and end the game immediately for no apparent cause 

However, on the upside:

  • It's an excellent demonstration of what you can do with components in a short space of time
  • The component architecture itself (the DW* classes) are not sloppy -- I've re-used them
  • The game code itself is so incredibly shoddy you can see that it's not important how to build a component system, just that you do so in the first place 

Download it here -- Fungal Adventure (36peas 360idev 2011 Game Jam project).

Notes on use: 

  • Do what you like with the code
  • Name and all artwork are copyright 36peas
  • I've had to remove the background music as I'd borrowed it from another project

Enjoy -- give me a shout if you've any questions on the above, would like to see the same material at a different conference or just want to chat. You can get me on twitter @36peas or by email gareth@36peas.com.

--

Updated 21/09/11: Not quite component-architecture specific, but this post re static blackboardson AIGameDev is useful if you choose to implement data storage in that way.

Wednesday
Nov242010

An introduction to our component architecture - what we started with

Update (Sep '11): I've posted about this more recently here -- Lightweight component architectures and object composition
 
This week's #idevblogaday post serves two purposes:
  1. It saves me some time (this is the first week where I'm really struggling to get sufficient time to write a whole post)
  2. It's given me the kick up the back side required to move this research (the component architecture) forward

What I'm sharing below is the first draft of a description/outline of the component architecture we've developed. It's a work/piece of research in progress, so bear in mind that:

  • It is not complete
  • It is not contemporary (the version we're now using makes changes)
  • I will post more up to date information about this archictecture in the coming weeks and months

So, without further adue...

"Dead West" Component Architecture Version 0.1

Context - http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/, and in Bjarne Rene: Component Based Object Management. Game Programming Gems 5, 2005, page 25. More links and OO-comparison video here - http://www.learn-cocos2d.com/2010/06/prefer-composition-inheritance/.

The Dead West (36peas - it’s deliberately generic) component architecture:
  • Composes game objects from behaviours and data - not from an object hierarchy
  • Uses an object hierarchy only for Game World > Game Object > Behaviour
  • Broadcasts (and narrowcasts) messages throughout the game world
  • Encourages messaging over explicit references (game objects should not normally keep references to one-another in the long-term) - even where references are kept, they should only be kept to narrowcast messages
  • Templates allow for behaviours to be grouped into logical, abstract concepts - e.g. there is a template for a Hero
  • Templates can implement other templates  - e.g. the Farmer template can implement the Hero template
Notable stores and methods noted below - does not include obvious glue methods and debug/logging methods.

DWGameWorld (GW)

Manages the game by virtue of owning the set of gameObjects.
  • localStore contains world-level data.
  • doUpdate: is the entry point for delta-based update methods (passed from here to objects and ultimately behaviours)
  • handleMessage: broadcasts messages to the entire game world
  • attaches to the rendered world by virtue of initWithRenderLayer:
  • addGameObjectWithTemplate creates new game objects in the world based on the provided template

DWGameObject (GO)

Represents a single object in the game - a hero, enemy or non-rendered item such as a trigger, spawn point etc. The DWGameObject object itself provides little more than a container for its behaviours and a store for shared data.
  • localStore (NSMutableDictionary) contains all data for that object
  • addBehaviour: withData: add the specified behaviour to the object, if not already in the object’s behaviour list. Will always write the data passed, including overwrites of existing data.
  • loadData: loads (and overwrites) data passed - pass a single-key dictionary to load just one key/value pair
  • doUpdate: calls doUpdate on all behaviours
  • handleMessage: passes message to all behaviours - can be used as messaging entry point for narrowcast messages

DWBehaviour

Base class for behaviours.
  • localStore (NSMutableDictionary) contains all data for this instance of this behaviour - shared data should only be stored in the host DWGameObject
  • handleMessage: does nothing in base class definition - see DWBTest.m for sample implementation
  • doUpdate: does nothing in base class abstract definition - see DWBTest.m for sample implementation
See DWBTest.m for a sample implementer of DWBehaviour.

DWMessage

Data object for passing messages through the game world.
  • messageType type of message (as defined in DWModelConstants.h)
  • payload the message detail/payload
For sending messages:
  • game world: [gw handleMessage:m];
  • to specific GO: [go handleMessage:m];
  • from behaviour, to host GO: [myGo handleMessage:m];

Templates

See templatedefs-general.plist - specifically the Dictionary keyed DWTModel for the data definition for templates.

Constants

DWModelConstants.h defines:
  • model load/save key constants
  • DWMessageType enum
  • (doesn’t, but implied that it will) behaviour names (keys - same as object names)

Additional notes:

  • Does not accommodate UI external to the game world - messages in and queries out should facilitate this
  • A template can implement other templates that specify the same behaviour. Loading order of implemented templates is not guaranteed, but behaviours specified directly by the host template will be loaded last, meaning data specified there will overwrite that written by any implemented templates.
  • GO data defined in template is written last (it will overwrite any class-level, implemented-template-behaviour-level and behaviour-level data)
  • Behaviour implementation classes can (do and should) write their own data defaults in initWithGameObject - see DWBTest.m for an example.

Outstanding issues/undecided:

  • Do behaviours need a processing order - in which doUpdate would be passed to behaviours based on their priority/classification. If so - easiest method would likely be storing processing priority (+/- integer with 0 as default) on behaviour definition, then creating a sorted list of behaviours on GO creation - assuming GOs don’t get behaviours added later