Scripted Gameplay Flow Control

Miscellaneous Forums/General Discussion/Scripted Gameplay Flow Control

I'm pretty new to making a game scriptable and I'd like some advice from people who are more experienced than me. I understand the technical side of scripting quite well, and I've opened a significant part of my game engine up to the VM now.

Beyond that, I've identified a need for three types of scripts.

The first type of script is what I'm calling a behaviour script. Object which have a behaviour script attached to them call this script once per update loop and run the script in it's entirety. The script could do absolutely anything, so it's basically a substitude for hardcoded game object behaviour. I understand this.

The second type of script is what I'm callig event-driven behaviour. This includes the on- events you might get in something like flash. For example, On-Die. If an object has an on-die script attached to it, this script is run in its entirety when the object dies. I understand this too.

The third type of script is more ambiguous, and hard for me to describe, but it's one which changes program flow. It's probably best described with an example.

Let's say I have a palace with a throne room and a King sitting on the throne. As I approach the King, I want a scripted scene to begin, where the King speaks to my character, my character approaches, then changes animation sequence, replies to the king and finally the king stands up, gives me an object and says something else. Now I am back in control again.

Now I can trigger this sequence with one of my event-driven scripts, I suppose. I could have an on-trigger event which is triggered when my character stands anywhere near the throne in the throne room. At this point, my cut-scene, sequence, call it what you will begins. But I don't quite understand how program flow works at this point. I want to be able to script this scene really. I could have a separate script-type language to describe cut scenes, but I would prefer not to. I've implemented a LuaVM and it works very nicely. Why recreate the wheel? Lua is powerful and readable.

What I don't understand is quite how program flow works with a scripted cut scene like this. Some events are queued, ie they cannot happen until other events have happened. Some events happen in time with other events, ie: as the king reaches out to hand me an object, my character needs to reach out to grab it at the same time.

I'm also wondering about interactions with scripts. For example, let's say I have a tutorial world. I'm in a dungeon and the jailer is my guide. He tells me that I need to look under the bed for a key. Control returns to me. When I find the key, back to the jailer who tells me the next thing to do and how to do it. Is this a whole long sequence of triggers and new scripts? Or is there a better way of sequencing these interactions? I guess it seems to me at the moment that you could very easily end up with hundreds of very little bits of scripts and that doesn't seem very practical. So it's probably the wrong way to go about it.

I've never had to "think script" before so I guess you could consider me a bit of a noob in this regard.

Interesting questions. This is pretty new to me too and I'm trying to figure out the same kind of things at the moment. I'm using my own script `language` and `virtual cpu` rather than LUA but I guess it's the same problems.

For me, each script is multitaskable ie I give tasks timeslices and priorities and all that, so you could say that a character says something to the player and then puts itself into a `waiting for state` condition, and senses the players activities like you would sense things like collision detection, and when the right conditions are met then the guard's script would just kick in with the next part of its sequence. I guess it depends how flexible you want things to be, whether you want the story to proceed along a variety of known lines which all end up at known destinations, or wether you want it so open that anything can happen.

I think of each object as an infinite loop running all the time sensing messages and other conditions and then occasionally doing something to react to it. Each object is fully autonomous and has its own script code. That means that as your player interacts with the guard there are messages being sent back and forth.

Another approach is to think of events as objects. An event, even in real life, is really just a collection of objects arranging themselves in a particular way at the same time, to create the overall impression of significance or meaning. There really are no events, there are only arrangements of objects. So to detect when it's time to do something you basically are detecting what state the objects are in and when it matches up to something that `triggers` an activity then that kicks in some new set of behaviors - which really are just sequences of changes to objects.

You could have a `game object`, a single object which senses game state and is looking for when its the right time to trigger the cut scene based on sequence or pattern matches. Then it can send the appropriate signal/message to the player object to put it into a passive mode and let `control` switch to the king object which then orchestrates what the player object does.

There's probably other approaches I guess. Good luck.

I don't believe in the object-attached script idea. It's cool from a coding perspective, but it will always be more efficient to write an entire program in script instead of just exposing certain points of the program loop.

I have a hazy memory of trying to tackle scripting in an adventure game - I think it went something like this:

I stored scripts in an Excel spreadsheet, but a data file or Access database would have been equally sufficient.

Each script line had a
- Script ID
- Script step
- Actor ID (linked to an actor / object table)
- Action statement consisting of a bunch of actions and variables (e.g. "WALK 10, 40" or "GET OBJ 104" or "ANIM 40, 109")
- Next Scripts ID (multiple allowed for concurrent processing, e.g. 29, 203) which would be
- Active flag (0, 1)

Scripts could only execute if they were active, so you could control story flow by turning scripts on and off.

I think there was more to it than that, but I can't remember... I do remember realising that it would be difficult to maintain the scripts without some sort of visual editor / script editor though, trying to get track of all the linked object and script IDs.

I like the Action Statements as suggested by Caff.

LOCK PLAYER
WALK PLAYER 10,40
IDLE
ANIM PLAYER 40,104
IDLE
GET OBJ 104
IDLE
WALK PLAYER 32,12
IDLE
ANIM 40, 109
SPEAK KING king1_hello.wav
OFFER OBJ 109
UNLOCK PLAYER


You get the idea. Not sure if you have anything like this already in place but the concept is brilliant. BTW, the idle command means wait until the previous command is finished until proceeding in the script. That way more than one action can happen simultaneously.

Thanks for all the opinions. My first instinct leads me towards ImaginaryHuman's "trigger" approach. I like that. The cutscene script is run in its entirety when the cutscene begins and each command creates an event in the event list ( a stack would make sense.) Then I have a cutscene manager object which monitors the event list looking for events whose time has come. When an event's time comes, the manager pops it off the stack, triggers the event and continues waiting for the next one. This would enable me to sequence events quite naturally, and seems like it would be pretty flexible.

My Lua VM already has access to all the game objects ( actors, entities, etc ) so I can still use those, but I'll need to add new functions for all the cutscene commands as they'll all need to take a trigger time as their first parameter. Or maybe I'll just rewrite the existing commands to take a trigger time which defaults to 0 ( immediately. )

That seems pretty logical to me, and I think I could get used to writing scripts for that, so I'll code that up this afternoon and see how it looks and feels.

Thanks again everyone.

I thought most games, commercially, had cut-scenes that were basically just animations, with a sound file running along side, that syncs with the animation, or small scripts that activate sounds files when that animation reaches certain frames.

An approach I used and worked really good was to copy the ai coding approach from fps creator. They had a scripting approach worked as followed.

:state=0:turnLeft,state=1;
:state=1,enemySighted:moveFore;
:state=1,enemyNotSighted:state=0;

You would attach this script to an agent. It's default state value is 0. The first part of the first statement inbetween the ":" is the condition, the second is the action. So in line one, the condition is, "does state = 0". If true then execute the action afterword. In this case turnLeft, and then set the state value to 1. Then the next line checks to see if the state value is 1, and if the enemy is in sight. If so it executes the moveFore function. The script is executed once a cycle and the state value static between cycles. State would be field value attached to the entity.

It's a pretty simple AI scripting system, but it work quite good.

Yep I like the idea of an event stack too - particularly if you can control it via a timeline. But this relies on your game / animation speed running smoothly enough, and things like speech or dialogue being displayed for long enough. I just don't trust timing in games - I'd rather see actions completed fully and sequentially.

Another big decision is whether you want to take control away from the player, which is often seen as a 'gameplay turnoff'. I think it's a necessity in some situations, but it's worth having a think about.

I found my approach became complex, but whatever route your take, you'll need a way to easily manage / edit / debug your scripts. I think the easiest way is a simple database, with every item / object / actor / sound fx / level / inventory position having a unique ID, just like Blitz3D's entity system. Then you can write scripts that refer to these unique IDs.

I'd be really interested to see what you come up with because it's something that did my head in!

That looks like DarkBASIC Chroma!!!

Well I coded this up last night, and wrote some functions to actually make use of it, and I'm pretty happy with it.

It's pretty much as I said earlier, based on ImaginaryHuman's trigger suggestion. Here's a cutscene script I wrote for testing :

Cutscene:SetCameraToActor(0.0,"Actor3")
Cutscene:MoveCameraToEntity(1.0,"Entity7",4.0,9)
Cutscene:EndCutscene(6.0)

The first parameter in every case is a floating point value representing the delay before the command is executed, measured in seconds.

The script is executed in its entirety when your cutscene begins. All the Cutscene: functions create a cutscene event on the event stack. It actually doesn't matter what order you put the functions in the script, of course, so you can group your commands by object or by time. For example, you could have all the camera commands together, then all the King commands, then all the player character commands. Or mix them all in and order them on time instead.

The Cutscene Event Stack manager pops events off the stack as their time comes and then executes the function the event relates to. In the example, SetToActor() places the camera on an actor, so that happens immediately. MoveToEntity() moves the camera from it's current position to the position of an entity. The LuaVM has the ability to get objects by name and then pass them as objects, but in this limited instance, it's faster to get the object as you need it, because the same one is never used twice. In a longer script, you would get the object by name and then pass the object itself each time. I prefer retrieving objects by name.

This example cutscene is set to execute at the beginning of a level. So as soon as the level opens, the camera is positioned on the actor. After a 1 second pause, the camera smoothly moves over to look at an entity. Then a further second passes and the cutscene ends, giving control back to the player. Everything remains as it was at the end of the cutscene. Physics controlled objects continue to behave according to their normal behaviours during cutscenes, so you can even script events which trigger a physics response. Eg: you could set a rock to drop in script by breaking it's joint with the rope that holds it, and then the rock takes on it's own physics behaviour and rolls/crashes around as it would in the game itself.

It works nicely, and feels very natural to code for me, so I think I'm going to stick with this. Now all I need is lots of additional functionality for the cutscene class. Actor behaviours like animating, moving, talking, etc mainly.

As for the respective merits of timeline versus sequential, I'm greedy, I want the best of both worlds. With a good timing system ( I hope I've got one! ) it should be trivial to pause time completely. So anything which needs to be sequential or involve user input ( for example, letting a player press fire or click to indicate that they've read a dialogue ) can just be set to pause time when it is displayed and resume time when it is closed. That way no time passes while the player reads, and so nothing gets out of sync.