wickedworx

February 7, 2011

A simple scripting system (as used by EGV)

Filed under: Technical — wickedworx dev clone 1 @ 6:40 pm

Summary

Excruciating Guitar Voyage was a game driven by content, as a lot of adventure games are. A key component in the arsenal of any content/story driven game developer is the ability to script game and world events.

A lot of developers, when talking about scripting, will think of LUA, JavaScript and AngelScript. But it is worth me saying – these may be “scripting languages”, but that doesn’t mean they are necessarily the right tool for the task of scripting events in your game.

For EGV, we wanted a scripting system which was higher level than what a pre-existing scripting language would provide.. a system which gave us direct control of the game. Another key element was that we wanted to be able to switch between a script ‘blocking’ the operation of the main game, and a script running alongside the main game per command… but I’ll get on to that later.

WELCOME TO: “EGV’S MEGA SIMPLE, EXTENDABLE, FLEXIBLE SCRIPTING SYSTEM PART 1”


This example is all in C#. I have implemented this exact same system in C++ on previous occasions…so, uh, write it in whatever language you want – the concept is the same.

Our requirements for this system:

  • High level – to the point it is almost ‘game specific’, and that game specific commands can be added
  • Mixed blocking/passing (similar to ‘mixed asynchronous’) behaviour
  • Simple and quick to use, reasonable to debug
  • Fast to parse (we pre-cache all potential scripts on level load)
  • Not exclusively for cutscenes – player doesn’t have to stop playing while scripts are running
  • Conditions, and branching (“if” support) would be ideal*

OK – so a few assumptions of the game:

  • All important game objects have a “tag” name.
  • There is a “GameResource” which contains a reference to pretty much any game system we may need
  • Game Objects in EGV use a component system. This shifts behaviours in to separate components, making it ideal for this kind of scripting.

* Branching/conditionals to be covered in next post.. we’ve already got it implemented, but I’ve taken it out from this code to avoid too much early bloat in the tutorial. Next post will go about adding it back in!

Implementation

Our scripting system consists of 3 main classes:

  • ScriptManager – holds a list of all currently running scripts, updates as necessary
  • Script – a script. holds a list of all the commands it must run, updates as necessary
  • IScriptItem – class off which script commands can inherit

class ScriptManager    
{        
    GameResource m_gameResource;        
    List<Script> m_runningScripts = new List<Script>();        
    List<Script> m_toAdd = new List<Script>();

Notice I’ve got two lists: “Running Scripts” and “To Add”. The second list is there in case one of our scripts launches another scripts while running – it means we can get around modifying the “Running Scripts” list while it is being iterated through. Each frame, scripts from the “To Add” list are copied to the “Running Scripts” list, and the “To Add” list is subsequently cleared.

See ScriptManager here http://pastebin.com/hg3nBMR0

So, the idea is:

  1. Event happens in game, causes a script to be loaded
  2. ScriptManager creates a new script, using the data from the .xml file
  3. Script reads data from xml file and creates an IScriptItem for each command* within it
  4. The Script now contains a list of IScriptItems and has a “current index” variable which stores which script command it is currently running
  5. Next frame, ScriptManager calls update on the new script
  6. Script calls ‘run’ on the first IScriptItem in the list. If/when this command is finished, it moves on to the next command.

Behaviour

So, there are two different situations which we have to take in to account:

  • Situation A) We want to run a number of script commands all in one frame. e.g. we want to remove the banana object, add an explosion there, play an explosion sound, spawn some particles. This all has to be done in one frame – if this were spread out over four frames (e.g. one frame per command) – it would look ridiculous.
  • Situation B) We want one script command to keep running for a number of frames. e.g. we want to wait 2 seconds between blowing up the bin and a character commenting on the fact the banana has exploded.

This is the interface which I came up with to solve these two situations:

public virtual void run(float dt) {}        
public virtual bool shouldUpdateGame() { return false; }        
public virtual bool isComplete() { return true; }

Note, I decided against a “pure” interface and have opted to add ‘defaults’ to the functions “shouldUpdateGame” and “isComplete”, as the majority of script commands will end immediately and not need a game update.

So here’s the idea in bullet-point go-to logic:

  • point A) Game is updating. Game updates  full frame, then updates ScriptManager.
  • point B) Script Manager runs, updates script, RUN is called on the current ScriptItem (using our index!)
  • shouldUpdateGame() is called and the result stored
  • isComplete() is called. If this returns true then the current ScriptItem index is increased
  • if the result stored from shouldUpdateGame is ‘true’, we’re back to “point A”.. if it is false, don’t update the game – essentially carry on from “point B” without letting another frame pass — UNLESS this script has entirely finished, in which case, we’re done anyway!

Here’s the logic for that:

public bool run(float dt)     //   Script.run(float dt)
{            
    bool shouldBreak = false;            
    while ( true )	        
    {		        
        if ( m_itemIndex >= (int)m_items.Count )		        
        {			        
            return false;	// return false - this script is finished	        
        }		        
        if ( shouldBreak )		        
        {			        
            return true;        // return true - let game carry on, but this script has more to do next frame
        }
                               // Continue on - run the next item        
        m_items[m_itemIndex].run(dt);		        
        shouldBreak = m_items[m_itemIndex].shouldUpdateGame();		        
        if ( m_items[m_itemIndex].isComplete() )		        
        {			        
            ++m_itemIndex;		        
        }
    }        
}

Loading

So, now we have the logic to run a linear script – we just need a few implementations of commands and a way to build a script!

The format of one of our scripts is as follows:

<script>
<command_name parameter1=”example parameteranother_parameter=”10” />
</script>

In our framework, across all our systems we have a unified XML load system. This converts XML (like above) to a tree of DataNodes. DataNodes have a name, an optional value and an optional list of child nodes.
In the above example, we’d end up with a DataNode with a name “script”, with a child DataNode (named “command_name”), with two child DataNodes (named “parameter1” and “another_parameter”), which have values of “example parameter” and “10” respectively.

Let’s add a couple more functions to our IScriptItem:

class IScriptItem    
{        
    GameResource m_gameResource;        
    public virtual void run(float dt) {}        
    public virtual bool shouldUpdateGame() { return false; }        
    public virtual bool isComplete() { return true; }        
    protected GameResource getGameResource()        // protected, access to GameResource
    {            
        return m_gameResource;        
    }        
    public void baseSetup(GameResource gr)        // set up base (don't rely on ScriptItem implementations to remember this)
    {            
        m_gameResource = gr;        
    }        
    public virtual void setup(DataNode node)        // set up function for overriding - passed a DataNode
    {
    }    
}

I decided to keep the name “IScriptItem” despite it not being a pure interface.

A Script is constructed from an xml file, which is converted to a DataNode. It then constructs all its script commands using the list of children in it’s DataNode. Each command in the script is build from a DataNode (and its children)

class Script    
{        
    List<IScriptItem> m_items = new List<IScriptItem>();        
    GameResource m_gameResource;        
    int m_itemIndex;        
    String m_filename;        
    bool m_shouldDelete = false;

    public Script(String filename, GameResource resource)
    {            
        m_filename = filename;            
        m_gameResource = resource;            
        DataNode rootNode = DataNodeResource.getDataNode(@"Content\scripts\" + filename);            
        m_itemIndex = 0;            
        addFromNode(rootNode);            
    }
    void addFromNode(DataNode node)        
    {            
        int nodeCount = node.getNodeCount();            
        for (int nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex)            
        {                
            DataNode thisNode = node.getNode(nodeIndex);                
            String itemType = thisNode.getName();
...

So, here we are iterating through command nodes, and getting a string from each – “itemType”. Using this itemType we can determine which class (inheriting from IScriptItem) to create an instance of. In our current code, we use Generics and a Registered Builder pattern to avoid a huge series of “if-else”.. but for this example, it’s probably easier to go down the “if-else” route! (There’s enough examples of using Generics to build objects from Strings in C# on the internet already!)

...
            IScriptItem itemToAdd = null;
            if ( itemType == "wait" ) // replace these else-ifs with your favourite String -> Object building logic
            {
                itemToAdd = new ScriptItems.Wait();
            }
            else if ( itemType == "play_cue" )
            {
                itemToAdd = new ScriptItems.PlayCue();
            }

            if (itemToAdd != null)
            {
                itemToAdd.baseSetup(m_gameResource);   // set up base
                itemToAdd.setup(thisNode); // pass data to item set up
                m_items.Add(itemToAdd);
            }
            else 
            {
// throw an exception or do some error thing here - you've tried to use a command in your script which doesn't exist!
            }

Full Script class here http://pastebin.com/DbX9SGUz

Command Logic

The great thing is – every time we want to add a new command, we just add a new class inheriting from
IScriptItem. EGV has about 30 commands, some of them very specific (“walk_to” and “speak”), and some of them more generic (“spawn_object”, “remove_object”, “set_animation”).
Adding new commands doesn’t seem to happen very often, and when it does – it’s usually a fairly quick addition…most of our commands involve finding a GameObject in the world, and calling a function on them.**

So, that should deal with adding two types of item – a “wait” and a “play_cue”. How do these look?

Wait

Class

class Wait        :        
IScriptItem    
{        
    float m_delay;        
    public override void setup(DataNode node)        
    {  
        // read in the delay required         
        m_delay = node.getNode("time").getValueF();        
    }        
    public override bool isComplete()        
    {            
        // this command isn't finished until the delay is over
        if (m_delay < 0)            
        {                
            return true;            
        }            
        return false;        
    }        
    public override bool shouldUpdateGame()        
    {            
        // we only want the *script* to wait, the rest of the game should carry on in the mean time
        return true;        
    }        
    public override void run(float dt)        
    {            
        m_delay -= dt;        
    }    
}

In Script

<wait time="5" />

This will cause a pause of 5 seconds in the script, while the game continues. Very useful in a script!

Play cue

Class

class PlayCue        
:        
IScriptItem    
{        
String m_cue;        
    public override void setup(DataNode n)        
    {            
        m_cue = n.getNode("cue").getValue();        
    }        
    public override void run(float dt)        
    {            
        getGameResource().getAudioManager().playAndDispose(m_cue);        
    }    
}

In Script

<play_cue cue="fx_explode" />

End of side 1

Well, there you have it – a simple linear scripting system – great for cutscenes. It’s a 5 minute job to add a new IScriptItem – so if you want a command to kick off an animation, set the camera’s target position, tell a character to walk to a new location, wait for a character to finish walking to their target location.

The majority of ‘acting’ logic still must be in your game objects (e.g. your script may command your character to walk to “location X”, but it is the character’s update which is responsible for actually walking him there). Using our component system, we have a component “ai_walk” which is responsible for this behaviour. If you command an object to walk, which doesn’t contain an “ai_walk” component – it simply won’t go anywhere…

Here are a few example script commands we use:

  • Spawn Object
  • Remove Object
  • Walk To (character walking)
  • Add Focal Point (camera)
  • Remove Focal Point (camera)
  • Add Objective (for player’s objective list)
  • Remove Objective
  • Speak (character talking)
  • Wait for talk (wait for a character to stop talking)

So, even this small set of commands, it is possible to see how a cutscene with a couple of characters talking / walking to a new location can be easily created.. (and object being added or removed, the player’s objectives being updated etc..)

In the next blog on this subject, I’ll write about how we implemented branching (IFs, etc..), variables and conditions.. transform this linear system can be transformed in to a full script language that can be used for more than just cutscenes.

**While on the train today, I had some thoughts about how this system could be made more “OO”, and more cohesive with our existing GameComponent/Message system… so maybe I’ll write a post about that (and why I don’t think we’ll be switching to it immediately) some time!


December 17, 2010

a look at WickedWorx tools, December 2010 edition

Filed under: Technical — wickedworx dev clone 1 @ 12:35 pm

COMPONENTIATOR
the “plus” symbols you see everywhere show positions of objects (whether they are just nodes for camera work, guiding walk paths in scripts, spawning objects at a position later etc.., or actual physical world objects.. sprites, decals, characters, etc.. some are lights!)
they’re all at funny angles so that if two are in the same place – you can see both!! 😉

the top right box shows the list of objects in the scene.. the 2nd box down shows the components the current object is made of, and the 3rd box down lets you edit the parameters sent to that component on create.

there’s also a slider for zoom which i shoved in the middle ‘coz it didn’t seem to fit anywhere else.

the initial version of this editor was probably put together in around 3-4 hours… it’s had a few improvements since (one which Kevin is particularly proud of is the “clone” button, which we didn’t have during EGV1).. – still, it’s probably saved us 100s of hours, as previously we would be hand editing .scene files (me and Jon C did this during Whatever Games SEU1 – it was definitely stressful!).. so, in terms of an investment for time saving – pretty effective..
Componentiator is cross-project, too… SEU2 (Whatever Games), EGV1+2, Super Janitoroid… a few others are using it for their own games (Goatbuoy is working on a pinball game using our stuff, and has picked up Componentiator pretty quickly!…possibly finding us a few bugs to fix on the way…)

oh – and here’s a picture of our animation tool:
ANIMHATOR
characters in EGV are made up of floating parts which we move about to create nice animations.
for EGV1 we didn’t have an animation tool… we just hand edited all the .anim (xml) files.. this meant that some animations were more simplistic than if we could’ve had a visual editing tool… animations in EGV2 are more slick and detailed!
Should mention that Animhator isn’t quite as cross-project as Componentiator, as it really only animates in the style of EGV… although it could be used for sprite/frame based animation – there would probably be better ways of doing this.
Animhator still has a lot of bugs and quirks, but we’re gradually working them out… it also has a special area for rushed features. Great!

August 17, 2010

EGV Scripting

Filed under: Technical — wickedworx dev clone 1 @ 3:50 pm

Quite a lot of the work we’re doing currently is writing scripts for Excruciating Guitar Voyage.

EGV uses its own custom scripting system. I was trying to explain to someone how this worked the other day, so here’s a blog about it.

EGV script comes from simple XML. You have a command and a set of parameters.

Commands can be synchronous or asynchronous with the game (e.g. “wait” is asynchronous, as the game will continue.. but many other commands – e.g. spawn object, delete object, set value) will be run synchronously with no game update between them (this means if you want to do a bunch of commands in one frame.. e.g. spawn 4-5 objects in one go, you don’t have to worry).

More than one script can be running at any one time – but, the same applies – depending on the commands they are running, they will either block other scripts or run asynchronously alongside them.

This is set per command type – so you don’t have to worry about this while writing scripts (just when adding new command types).

We’ve also got very simple branching/if statements/and “goto” style things… as well as asynchronous wait command for animations to finish, player to make decisions, characters to walk / talk / – or even for just a fixed time etc..

So far we haven’t had much difficulty writing scripts for the game… and we’ve documented our many commands on our private dev wiki.

Currently we’ve got around 40 different commands… A large number of these are to do with camera movement/zoom/focus, and also object spawning/behaviour…
One of the coolest features we have is the ability to spawn a custom component-based object  in script… So I can just say “spawn this object, with these components (using this data)”… e.g. in one of our cutscenes, I want to create a “Molly” at the helicopter’s current location:

  <spawn_object_data node="helicopterf" x="-2" y="0">
    <object name="molly">
      <component type="color" r="200" g="200" b="200" />
      <component type="position" x="0" y="0" z="0" rx="0" ry="0" rz="0" />
      <component type="humanoid_physics" w="1.5" h="3" bottom="1.5"/>
      <component type="animate" filename="molly.anim" layer="3"/>
      <component type="talkable" name="Molly" x="0.5" y="-2" />
      <component type="ai_walk" />
    </object>
  </spawn_object_data>

Here is an example showing how the mixed asynchronous/synchronous scripting works:

  <cutscene_start />
  <start_music cue="intro_amb" /> <!-- first two commands are run in one go --!>
  <wait time="2" /> <!-- game continues running for 2 seconds, script waits here --!>
  <speak target="narrator" key="narrator_intro_1" />
  <speak target="narrator" key="narrator_intro_2" />
  <speak target="narrator" key="narrator_intro_3" />
  <speak target="narrator" key="narrator_intro_4" />
  <speak target="narrator" key="narrator_intro_5" />
  <speak target="narrator" key="narrator_intro_6" />
  <speak target="narrator" key="narrator_intro_7" />
  <speak target="narrator" key="narrator_intro_8" />
  <speak target="narrator" key="narrator_intro_9" /> <!-- all speak commands run in one go, narrator character queues up his dialogue --!>
  <wait_for_main_slot /> <!-- game continues running while dialogue is spoken, script waits here until it finishes --!>
  <fade target="1" />
  <set_music_effect target="100" />

  <camera_speed speed="5" linear="true"/>
  <camera_zoom zoom="40" set="true"/>
  <add_focal target="pan_1" jump="true" />
  <add_focal target="pan_2" />
  <remove_focal target="pan_1" /> <!-- script sets all these camera options ready to go, in one update. all of these commands are just changing the state of the camera. game does not update --!>

  <wait time="2" /> <!-- game continues updating for 2 seconds, script waits here --!>

anyway – hope someone finds this interesting. we’ve got loads of work to do, so i’m gonna get back to it!

June 24, 2010

WickedWorx and Components

Filed under: Technical — wickedworx dev clone 1 @ 11:19 am
(this is a post I wrote on tigsource forums… I know a lot of this has been covered here before)
I thought I’d write a little about how the ‘dynamic aggregation’ game object model works in terms of EGV…
this horrible diagram probably doesn’t show very well what’s happening, so I’ll explain further below
so, the game runs on two ‘main’ threads..
  • render / state – the main game’s render loop, and also state machine (out of game update)
  • update – update, physics (in game update)
there is also a network thread for the minor bits of networking the game does…
ignoring the state machine, it’s best just to see this as render/update.
when an object is spawned, it can either be spawned from
a) a .object file (looks like this)
<block>
    <component type="color" r="200" g="200" b="200" />
    <component type="position" x="0" y="0" z="0" rx="0" ry="0" rz="0" />
    <component type="physics_body" points="bridge.poly" density="0.1" static="true" renderable="false"/>
    <component type="outline" points="bridge.poly" />
    <component type="texture" points="bridge.poly" texture="machine.png" sx="5" sy="5" ox="0" oy="0" layer="3"/>
</block>
b) a .scene file referencing multiple object files, e.g. here’s one _block.object being overriden
<object name="Object_block.object">
    <override override="component" unique="type" name="color" index="0">
        <data r="0" g="100" b="0" />
    </override>
    <override override="component" unique="type" name="physics_body" index="0">
        <data points="G_crashsite_ground.poly" />
    </override>
    <override override="component" unique="type" name="outline" index="0">
        <data points="G_crashsite_ground.poly" />
    </override>
    <override override="component" unique="type" name="texture" index="0">
        <data points="G_crashsite_ground.poly" texture="rock.png" sx="0.07" sy="0.07" />
    </override>
</object>
(the .scene file is created by componentiator, see original post for screenshot)
c) an inline object definition in script
<wait time="2" />
<spawn_object_data>
    <object>
        <component type="color" r="255" g="255" b="255" />
        <component type="alpha" alpha="1" />
        <component type="position" x="0.5" y="0.4" z="0" rx="0" ry="0" rz="0" />
        <component type="sprite" texture="credits_jon.png" sx="0.4" sy="0.1" layer="6" use_color="true"/>
        <component type="fade_in_out" />
    </object>
</spawn_object_data>
The data is merged as required (e.g. using the overrides supplied in the .scene) and then sent to the GameObjectManager.
From here, each component definition is separated and sent to the ComponentBuilder. Each component is built (the correct component type is built depending on the type=””, using a templated factory thingy.. i know templated isn’t the right word in C#, but it’s the nearest equivalent C# does to C++ templating and i forget what C# calls it…) and sent its data.. e.g., here is how a “alpha” component builds:
public override void componentInitialise(string name, DataNode node, GameResource gameResource, GameObjectManager objectManager, GameObject gameObject)
{
m_alpha = node.getNode(“alpha”).getValueF();
}
Once all the components are built and added to the object, a ‘linkup’ stage is performed to allow Components to register with render, physics and update (or any other systems) as appropriate. This is usually not done in the componentInitialise as the whole object has not been created by this point.
e.g. a “sprite” component will register with render, but the “fade_in_out” component will register with just update.
when an object is destroyed, it calls an ‘unregister’ function on all of its components, allowing them to unregister with systems they have registered with.
Components can query the root object for other components, like this:
getRootObject().getComponent(“position”);
so, for example – the “sprite” component will get the “position” component, and use this to determine where to render itself.
if you have more than one “position” component in an object, it can be accessed as such:
getRootObject().getComponent(“position”, 0);
getRootObject().getComponent(“position”, 1);
etc..
there’s quite a bit of thread safety involved, especially with management of registered lists (it may be registering or deregistering with ‘render’ from the ‘update’ thread, for example).. to avoid any stalling, an intermediate ‘waiting list’ is used, and then flushed in to the main list on the next local update.. this means there is no holding on to mutexes during actual render/update, only during the flushing of the lists immediately before render/update.
destroying an object is never instant, it happens at the end of the current update frame.
well, there you have it – that’s how the whole of our game works. every time we need a new behaviour or new rendering method – it’s just a case of writing a new simple component… it’s quite neat!
watch the trailer and you can see where one of the objects detailed above is spawned in script.. 😉
next time i think i’ll write a bit about the scripting system, which works in a ‘mixed mode’ synchronous/asynchronous per-instruction timeline against the game updates.. it’s a custom scripting system (all in xml), but it allows us to make it very powerful.

Blog at WordPress.com.