Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm currently working on a game and using the naive component based architecture thingie (Entities are a bag of components, entity.Update() calls Update on each updateable component), while the addition of new features is really simple, it makes a few things really difficult: a) multithreading / currency b) networking c) unit testing.

  • Multithreading / Concurrency is difficult because I basically have to do poor mans concurrency (running the entity updates in separate threads while locking only stuff that crashes (like lists) and ignoring the staleness of read state (some states are already updated, others aren't))

  • Networking: There are no explicit state changes that I could efficiently push over the net.

  • Unit testing: All updates may or may not conflict, so automated testing is at least awkward.

I was thinking about these issues a bit and would like your input on these changes / idea:

  1. Switch from the naive cba to a cba with sub systems that work on lists of components
  2. Make all state changes explicit
  3. Combine 1 and 2 :p

Example world update:

statePostProcessing.Wait() // ensure that post processing has finished
Apply(postProcessedState)

state = new StateBag()

Concurrently(
 () => LifeCycleSubSystem.Update(state), // populates the state bag
 () => MovementSubSystem.Update(state), // populates the state bag
  ....
})

statePostProcessing = Future(() => PostProcess(state))
statePostProcessing.Start()
// Tick is finished, the post processing happens in the background

So basically the changes are (consistently) based on the data for the last tick; the post processing can a) generate network packages and b) fix conflicts / remove useless changes (example: entity has been destroyed -> ignore movement etc.).

EDIT: To clarify the granularity of the state changes: If I save these post processed state bags and apply them to an empty world, I see exactly what has happened in the game these state bags originated from -> "Free" replay capability.

EDIT2: I guess I should have used the term Event instead of State Change and point out that I kind of want to use the Event Sourcing pattern

share|improve this question

closed as not a real question by Noctrine Nov 29 '11 at 17:34

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.If this question can be reworded to fit the rules in the help center, please edit the question.

3 Answers 3

I would suggest using your first option (sub-system) with state change requests from components to the sub-system.

Regarding the order of update, I think this is more of a data structure problem. You should first define the dependencies between objects (or sub-systems) and group independent objects together (that can be easily updated in parallel). This concept is very well explained in this article http://slidesha.re/u6xR10 (long but totally worth it!)

Also have a look at this selection of presentation http://bit.ly/tw04LI especially the Data Oriented Design ones.

I hope that helps :)

Cheers

share|improve this answer
    
There are edge cases though that complicate everything but you can isolate those (e.g. Cameras may depend on controllers at the same time controllers depend on camera updates), you can always have "multiple passes" in some systems or split them in two –  DiogoNeves Nov 29 '11 at 15:36

May this stackoverflow question helps you: http://stackoverflow.com/questions/1901251/component-based-game-engine-design

It contains a lot of interesting literature.

To your question about networking: One approach could be that you introduce a message/event system instead of/in addition to calling the update method.

You can easily transmit the messages/events over the network. The receiving client/server can interpret the message and do what it should do (depending on the type of message). In most cases this is just receiving the message and forwarding it to an actual component which knows what to do when it receives the message.

About sending the state changes: I prefer sending the new position instead of the changes. In a 2D world this means in both cases you are sending an int/double for x and y but with the benefit that loosing one message in the network does not mean that the position isn't correct any more. And you don't have to calculate the new position on each client.

share|improve this answer
    
Event/message system: I already have one and while it makes implementing new features even easier, it is affected by and somewhat causes the mentioned problems. To clarify the state changes thing: I want to record all state changes (examples: Movement(x,y, facing...), Spawn(projectile)), the state changes are not what gets pushed over the net, it's just that the packages are built from the state changes (as is the real "server-state"). Also: the changes should more or less yield replay capabilities "for free". –  Maxem Nov 29 '11 at 15:06

Multithreading / Concurrency is difficult because I basically have to do poor mans concurrency (running the entity updates in separate threads while locking only stuff that crashes (like lists) and ignoring the staleness of read state (some states are already updated, others aren't))

If that is 'poor man's concurrency', what do you consider 'rich man's concurrency' to be?

Thread safety is about more than stopping things from crashing, anyway. I expect you're doing something unsafe here.

Networking: There are no explicit state changes that I could efficiently push over the net.

What kind of state change are you interested in? I don't see how the components make any difference to what you need to send over the network. If your problem is that you don't have 1 standard place to send updates from, then make a standard place, eg. a Networking component, and push updates into it.

Unit testing: All updates may or may not conflict, so automated testing is at least awkward.

Again, I don't see what the problem is. Test the components in isolation, and if there are issues with interaction, test those too.

share|improve this answer
1  
rich mans concurrency: Thread safety :) - The poor mans version may or may not have undefined behavior (without crashing), if it crashes it's just wrong :p –  Maxem Nov 29 '11 at 15:15
    
Networking: I either have to create the packages / package parts while updating a component or I have to deduce what has happened from the current state ("after the fact"). The latter is a non-issue for things like position packages, as these only need information from the current state. The former is basically what I want to do, although not only for the networking thing but for the current server state as well. A reason I didn't mention in my question is that this might result in "free" replay-capabilities. I say free because - due to networking - I have to record the state changes anyways. –  Maxem Nov 29 '11 at 15:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.