Managing and Customizing Scenes
by Gamefic on June 1, 2015
The concept of scenes has always existed in Gamefic. The simplest kind of scene was simply a block of code that got called by the cue method. There were also specialized kinds of scenes like pause, prompt, and yes_or_no. The system seemed solid in my initial tests, but when I started building a larger game that heavily relied on different types of scenes, I noticed a few shortcomings. As a result, I redesigned scenes so the shortcuts for creating common scene types were still available in plots, but under the hood there's a system that provides a lot more capability. The new system is based on three classes: SceneManager, SceneData, and the Scene itself.
The previous system's biggest flaw was due to a naive assumption. Every scene included a single Proc that would get executed before the prompt. In practice, however, I often needed two Procs; one to execute before the prompt, and one to execute afterwards. Take a Paused scene, for example. A Paused scene typically displays a message and then waits for the user to respond before the game continues (e.g., "Press enter to continue"). With a single Proc assigned to every scene, this means that a pause had to be composed of two scenes: one to display the message and the prompt, and one to change the user state back to an Active scene, which is the normal state that accepts commands. (Yes, even the command prompt is implemented as a scene.)
The new system resolves this problem by allowing a scene to have two procs called start and finish. In an Active scene, the process looks like this:
- Start by handling any processes that need to occur before the player acts. (In the default implementation, the start proc does nothing; but this could be overriden, for example, to increment a turn counter.)
- Wait for the user to enter a command.
- Finish by performing the command and update the game world.
In a Paused scene, the process looks like this:
- Start by processing the scene (typically a long message to the user).
- Wait for the user to press enter.
- Finish by cueing the next scene. By default, a pause will return the user to an Active scene.
A SceneManager defines the properties of a scene. Here's an example that's similar to a Paused Scene. It displays a message before and after the prompt, then sends the user back to an Active scene.
echo = SceneManager.new do |manager| manager.start do |actor, data| actor.tell "Type something." end manager.finish do |actor, data| actor.tell "You typed #{data.input}." cue actor, :active end end
Add the scene to the plot:
scene_managers[:echo] = echo
Cue the scene in the introduction:
introduction do |actor| cue actor, :echo end
As with previous versions, plots provide methods that let you bypass a lot of the boilerplate for typical types of scenes. To create a pause, you only need to provide a symbolic name for the scene and a proc to execute before the prompt:
pause :meet_joe do |actor| actor.tell "Joe is here. He's happy to meet you." end introduction do |actor| cue actor, :meet_joe end
The new version also lets you yield a SceneData object in the proc. Among other things, you can use it to customize the scene's prompt and the next scene to cue.
pause :meet_joe do |actor, data| actor.tell "Joe is here. He's happy to meet you." data.prompt = "He'll start talking to you after you press enter." data.next_cue = :joe_talks end pause :joe_talks do |actor| actor.tell "He starts chatting with you." end
This system seemed like the best compromise between ease of use (the scene methods in plots) and breadth of features (extending and customizing SceneManagers). The current code is available for review in the repo. More advanced examples of custom scenes are forthcoming.