Scenario.js
From Spheriki
Bruce's Scenario.js (properly called simply "Scenario") lets you coordinate RPG cutscenes in Sphere. If you've ever wanted two persons to walk while showing a textbox or wait for a person to reach a point before continuing, this is the script for you.
Contents |
Download
The current version of Scenario as of Tuesday, December 8, 2009 is 2.0.
- Scenario.js (17.7 KB JavaScript file)
Usage
Getting Started
Scenario takes instructions for a cutscene, called actions, and runs them in the map engine. The script comes with a handful of predefined actions, but you can also make your own.
To make a cutscene, first download and put Scenario.js in your scripts/ folder.
Include the script:
RequireScript("Scenario.js");
Make a map and add some persons to it if you haven't already.
Scenario needs to run while the map engine is running, which means cutscenes can only run in person scripts, map scripts or triggers. In any of those, write your cutscene as follows:
// Cutscenes are called "scenarios" var Scene = new Scenario(); // Build your scene. with (Scene) { walkPerson("Jill", "n", 50); // Make Jill walk north 50 pixels, focusOnPerson("Jack",1000); // then pan to Jack over a period of 1000ms. // Do some other stuff in the cutscene } // Run the cutscene. Scene.execute();
There! For simple cutscenes, that's all you should need.
Forking the Timeline
If you want to, say, have two people walk at the same time, you'll want to get friendly with some other Scenario methods:
Scenario.beginFork(); Scenario.endFork(); Scenario.synchronize();
Normally, a scenario only has one timeline, where things happen one after the other. To make two or more things happen at once, the timeline must be forked:
No fork -------------- (timeline)
With fork -----o-------- (timeline)
\
o------ (forked timeline)
As you can see, a fork runs actions at the same time as its containing timeline does. In this way, more than one thing can happen at a time.
To create a fork, wrap the concurrent instructions in calls to beginFork and endFork.
var Scene = new Scenario(); with (Scene) { walkPerson("Alice", "s", 10); beginFork(); walkPerson("Bob", "s", 30); endFork(); walkPerson("Alice", "s", 20); } Scene.execute();
In the above script:
- Alice will walk 10 pixels south.
- Bob will begin to walk 30 pixels south.
- While Bob walks, Alice walks another 20 pixels south.
Note that the instructions after the fork don't wait for those inside the fork: In this example, Alice doesn't wait for Bob to finish walking his 30 pixels south before continuing. However, Bob does wait for Alice to walk her first 10 pixels.
Multiple forks can run at once, for example:
var Scene = new Scenario(); with (Scene) { beginFork(); walkPerson("Carol", "s", 60); endFork(); beginFork(); walkPerson("Bob", "s", 60); endFork(); walkPerson("Alice", "s", 60); } Scene.execute();
Here, everybody starts walking at the same time.
Note also that forks can be nested: That is, forks can be created inside other forks.
var Scene = new Scenario(); with (Scene) { beginFork(); beginFork(); walkPerson("Carol", "s", 60); endFork(); walkPerson("Bob", "s", 60); endFork(); walk("Alice", "s", 60); } Scene.execute();
The above code effectively does the same thing as the one before, just in a rather more roundabout fashion.
Coordinating Complex Scenes
Using beginFork and endFork let you start forks, but what if you want to wait for those forks to finish?
The synchronize action does just that: It waits until all of the forks in the containing timeline in have finished before continuing, effectively resynchronizing the timeline.
Fork, no sync ---o---------------- (main timeline)
\
o--------x (fork)
Fork with sync ---o---(sync) o--- (main timeline now waits for fork to finish)
\ /
o--------o (fork)
synchronize will wait on all forks that the timeline it's on spawned:
o------------o (fork #2)
/ \
---------o---o-(sync) o o------ (main timeline waits for all its forks when sync'ing)
\ /
o--------o (fork #1)
As an example, we can take the last script and make Alice wait for Carol and Bob to finish before walking herself.
var Scene = new Scenario(); with (Scene) { beginFork(); walkPerson("Carol", "s", 60); endFork(); beginFork(); walkPerson("Bob", "s", 60); endFork(); synchronize(); // Wait for Carol and Bob. walkPerson("Alice", "s", 60); } Scene.execute();
And there you have it! That's everything you need to make sophistiecated coordinated cutscenes, from sequencing actions, to making them run side-by-side in forks, to synchronizing with those forks.
Defining Your Own Actions
Actions are the instructions that you can put in your cutscenes to do things like walk, move the camera, play sounds, display effects, or fade the screen. Scenario comes with some default actions, but for full control, you'll want to make and use your own too.
To create an action, use the function Scenario.defineAction and pass it a name, render priority and a set of callback functions:
Scenario.defineAction("twirl",0, { start: function(host,state,Person,Time) { state.Directions = [ "south","west","north","east" ]; state.Person = Person; state.Direction = 0; state.EndTime = Time + GetTime(); SetPersonDirection(Person,state.Directions[state.Direction]); return true; }, update: function(host,state) { if (GetTime() < state.EndTime) { state.Direction = (state.Direction + 1) % state.Directions.length; SetPersonDirection(state.Person,state.Directions[state.Direction]); return true; } return false; } };
Here, our new action will be called twirl and will take two arguments: the name of the person to twirl, and how long in milliseconds.
start is called once when the action is executed and should initialize the action's state. If the action doesn't include an update callback, Scenario will immediately move on to the next, otherwise it will proceed to call the action's update callback once per frame until it returns false.
Actions can additionally include a render callback, which is also called once per frame and should be used to draw anything related to the action. This can be used, for example, to draw text boxes. The second argument to defineAction the render priority, determines the draw order: Higher priority actions are rendered above those with lower priority.
So there it is, our custom twirl action! It can be used just like any of the built-in actions:
var Scene = new Scenario(); with (Scene) { walk("Alice", "s", 10); twirl("Alice", 1000); } Scene.execute();
Now Alice will walk 10 pixels south, and then twirl around really fast for 1 second.
Handling User Input
There is one more callback you can add to an action: handleInput. It's used for text boxes and other input-taking actions. How it works is that for all running actions that have it, the latest one still active will have it called, and the others miss out. When the action with input focus finishes, the privilege passes on to the next one down.
var Scene = new Scenario(); with (Scene) { textbox("No other text boxes yet."); beginFork(); textbox("I close 3rd."); endFork(); beginFork(); beginFork(); textbox("I close 1st."); endFork(); textbox("I close 2nd."); endFork(); textbox("I close 4th."); textbox("No other text boxes remaining."); } Scene.execute();
The phony textbox action above implements the handleInput method to wait for a key and close. To get the input order, look for the last fork that's still running, and then read the beginFork calls upwards.
The whole thing looks a bit like this as the player presses keys to dismiss the text boxes:
No other text boxes yet.
I close 1st. I close 2nd. I close 3rd. I close 4th.
I close 2nd. I close 3rd. I close 4th.
I close 3rd. I close 4th.
I close 4th.
No other text boxes remaining.
Reference
Built-in Actions
TODO: Test and document the built-in actions.
fadeTo(Color, Duration) facePerson(Person, Direction) focusOnPerson(Person, Duration) followPerson(Person) hidePerson(Person) killPerson(Person) panTo(X, Y, Duration) pause(Duration) playSound(File) showPerson(Person) walkPerson(Person, Direction, Distance, Speed, FaceFirst)
Frequently Asked Questions
How do I make Scenario.js work with persist.js?
TODO: Transfer knowledge from here http://www.spheredev.org/smforums/index.php/topic,2994.msg39497.html#msg39497 to here.
Feedback
Visit the Spherical forum thread for the latest version, to report bugs or request features.