1

I wonder if you can help me.

I'm writing a game (2d) which allows players to take multiple routes, some of which branch/merge - perhaps even loop. Each section of the game will decide which section is loaded next.

I'm calling each section an IStoryElement - And I'm wondering how best to link these elements up in a way that is easily changed/configured and at the same time, graphable

I'm going to have an engine/factory assembly which will load the appropriate StoryElement(s) based on various config options.

I initially planned to give each StoryElement a NextElement() As IStoryElement property and a Completed() event. When the vent fires, the engine reads the NextElement property to find the next StoryElement.

The downside to this is that if I ever wanted to graph all the routes through the game, I would be unable to - I couldn't determine all possible targets for each StoryElement.

I considered a couple of other solutions but they all feel a little clunky - eg Do I need an additional layer of abstraction? ie StoryElementPlayers or similar - Each one would be responsible for stringing together multiple StoryElement perhaps a Series and a ChoicePlayer with each responsible for graphing its own StoryElement - But this will just move the problem up a layer.

In short, I need some way of emulating a simple but dynamic workflow (but I'd rather not actually use WWF). Is there a pattern for something this simple? All the ones I've managed to find relate to more advanced control flow (parallel processing, etc.)

Basic
  • 26,321
  • 24
  • 115
  • 201

2 Answers2

5

It might help to think about the StoryElement objects as nodes in a directed graph. The edges of the graph could be represented by StoryElementTransition objects (or some similar name). A StoryElementTransition could contain references to the state you want to transition out of, the state you want to transition into, and maybe the event that triggers the transition.

By setting up your story as a graph, you open up the possibility of running a directed graph traversal algorithm to determine all the possible routes through the game. For example, you can run a Depth First Search algorithm on the Event graph, pushing each state and transition on a stack, and printing the entire stack once you reach an end state.

tedw4rd
  • 363
  • 1
  • 8
  • That's a much cleaner way of looking at the problem - Thank you – Basic Jun 14 '10 at 01:44
  • the completed() method shuld return a status, and each transition be based on a status (ie if completed() return status 1, use transition to StoryElement B, id status equals 2, go to SoryElement C... You could even add probabilistic transition (allowing several transition from a StoryElement, based on the same status but with a % of activation...) – PATRY Guillaume Jun 16 '10 at 12:28
  • That's a very elegant suggestion - Thank you – Basic Jun 18 '10 at 01:42
1

I know this question has an accepted answer and probably you've settled anyway, but I must say I've been thinking about that lately ("how should we model a storyline-centric game?") and the conclusions I've come to as of now are:

  • Stories are data, they shouldn't be defined anywhere in code.

(The same applies for gameplay elements, for example you shouldn't have a Goblin Class, instead you could load an Enemy.Creep called "Goblin" from some database of enemies.)

  • Most designs you can come up with will be limited or hard to extend.

This is a golden rule of design to me: make everything that may be extended, extensible. So make a good hierarchy of simple classes and interfaces that don't do more than they should and do it well. Because in some point you may want an event to be triggered by killing 50 goblins in the forest and if your design isn't good you'd have to adapt a story or re-factor a framework, both bad things.

  • Most professional games rely on scripting and an engine.

There's a reason to this. First, the code that renders the game can be easily optimized if it's small, and all the story elements can be defined in either a common (like Lua) scripting language or even some custom notation. So you could have your characters defined in YAML or whatever you like.

  • You shouldn't have to touch your engine after a point, and focus on the story.

This means that you'd be editing story files (databases? markups? scripts?) and seeing how they play together, while leaving your compiler alone.

Camilo Martin
  • 37,236
  • 20
  • 111
  • 154
  • That's an interesting response - and I fully agree re: extensibility! What I've got in my head is something like a neural net - with connecting dots allowing me to follow from one to another and examine possible outcomes. I suppose you're right that a scripting approach may be more robust. I've also run into a 2nd problem re: entities to represent elements. Hierarchies do NOT work well in EF4 so having a HumanoidCreep which inherits Creep causes huge performance issues. Time to look at nHibernate I think – Basic Nov 08 '10 at 16:08
  • I never touched nH or EF, but I guess if such inheritance causes so many issues it would be good to look at nH - I've heard it's more tweakable than EF. BUT, you wouldn't inherit Creep into HumanoidCreep because an Humanoid Creep would be a Creep with diferent stats/name/abilities/sprites/etc. That's my point on data outside of the code - it's something that I've learned the hard way, keep changeable data changeABLE. – Camilo Martin Nov 09 '10 at 03:28
  • Hmm interesting - Can you explain why you'd avoid thi inheritance? I can understand not having a Goblin which inherits creep - it' too specific but HumanoidCreeps will all have -say- Armor, social group affiliation, etc. All creeps have hitpoints, etc. - Would you perhaps use just creep and have a Type to indicate animal/humanoid/etc.? Won't you lose some OO benefits like having type-specific methods (Humanoid.Kill -> relationship with Social Group-- before calling Creep.Kill)? – Basic Nov 09 '10 at 09:38
  • I wouldn't specify these characters with classes because that's putting data into code, and that's hard to deal with in case of changes - for instance, let's say you need a custom action each time a Humanoid Creep is killed. Instead of defining that into code, it would be in the `HumanoidCreep.script` file, or maybe even a `Humanoid.script` "included" by the former. Think about it like CSS: if I kill either a human or a humanoid, I lose reputation points. This can be easily defined by an "action" (in an `Actions` namespace maybe) to be called for every Creep script including `Humanoid.script` – Camilo Martin Nov 09 '10 at 11:27
  • My reference to CSS is that metadata to style the character (such as it being able or not to carry one or another type of equippable item) should be defined in some place that's easy to edit, easy to extend, and that is specific for this purpose. I believe OOP is good to make your engine, but not good to define how your game works. You wouldn't define a song or a drawing with OOP, right? But maybe you could script them, with MIDI or SVG. In the same sense, you can define a character on a script and "render" it with the engine. So yes, every creep would just load its information from a file. – Camilo Martin Nov 09 '10 at 11:34
  • And an action like `Creep.Kill` would just trigger every `Engine.Action` defined in the `Creep.Actions("OnKill")` taken from the creep's script, something like `Action.OnKill{Reputation: -1;}`. – Camilo Martin Nov 09 '10 at 11:40