Cutscenes scripting

Started by
5 comments, last by Mekamani 6 years, 10 months ago

This is instance of a broader topic, but I concentrate on a narrow example.

Imagine a jRPG (like Final Fantasy 6 or Tales of Phantasia). A game like this generall has a lot of cutscenes.

Logic of cutscenes lends itself nicely to a simple sequentual description:


hero.walk(0,-100);
npc.walk(0,100);
say("Hero","I will need to travel north. Do you know how I may go about it?");
say("NPC","There is a bridge to the north. I can open it for a 1000 coins.");
if(money<1000)
{
   say("Hero","Drat!");
}
else
{
    if(choice("How about it","1. Yes","2. Not now"))
    {
        say("NPC","Opening right away!");
        money-=1000;
        tile(10,10).passable=true;
    }
    else
    {
        say("NPC","See you later!")
    }
}

I am combining quests and cutscenes here, but if we are using scripting language, it feels natural thing to do.

If we are writing a text-based game (text quest), this is actually how we can implement it.

However if we are writing a video-game, things are different. We usually have an update()/render() loop, and need to draw things on screen 60 times a second.

So there are two subsystems: cutscenes, which are nice and sequental, and game engine, which is nice and wants to run update()/render() 60 times per second.

Is there a way to make them communicate cleanly?

Our options are:

1. Cutscenes system is in control. walk(), etc. actually call render() inside, and so on. Strange, and raises some questions: how do we go back to normal gameplay? Or "normal gameplay" is also a kind of a cutscene? Etc.

2. Use actual separate thread. This feels like a wrong tool for the job. The interaction between engine and cutscene subsystem better be deterministic/synchronized, probably.

3. Use some form of coroutines. This feels like the right thing, but how to go about it, exactly? What walk() should look inside? If we are using e. g. Lua (having your own scripting language with full control might simplify this). And how engine runs cutscene?

4. Ditch scripting and write cutscenes in C++. Saves the need to write communication layer, and probably can be made not to blow up compilation time. But does not answer the main question: how to sequental cutscene.

5. Use state machines. Workable, but not nice. More code, less natural flow, more demand for coding experience for scripter.

There are few open-source jRPGs. Valyria Tear seems to use state machine kind of approach. Which feels like a waste, considering Lua has coroutine support.

So, I guess, the question is:

1. Do you know a clean way to do this?

2. How did people go about this? There are a lot of jRPGs. Cutscenes have been done for years. How does this look in games? Examples of scripts from actual games are welcome (assuming it is legal to post them). Not necessary jRPG. Baldur's Gate? WoW?

Broader topic (from the beginning of the question): this interaction seems common and kind of a pain (I'm guessing that for typical Match-3 the code to implement actual logic (sufficient for ASCII console Match-3) is dwarfed by the code to get gameplay animated). And intuition is, that it does not need to be.

Advertisement

Well, the somewhat popular Rpg-Maker Series, tailored towards beginners & jRPGs uses a pseudo-graphical "event" system, where you don't directly code, but put together a series of commands, ie:

http://img04.deviantart.net/365b/i/2012/140/1/8/rpg_maker_vx_ace___event_by_novadragon1000-d50i65w.jpg

Since there's no actual coding involved, it internally creates a command-object which is being processed once per frame until it says its done, in which case it will processed.
So thats one way of approaching it - creating a system with a (graphical) user interface, that internally generates code to make it seem to run seemlessly.

On a similar side, Unreal Engine 4's blueprint-visual scripting (https://docs.unrealengine.com/latest/INT/Engine/Blueprints/) actually supports similar functionality, where you can put ie. a "delay" node that will resume execution after a certain amount of time, or a "DoMove"-command that resumes after movement has ended, just like you put it; though this time around its in a proper, fully functional scripting language (only that its graphical, and I'm not sure whether such functionality can easily be recreated in any regular scripting language without touching the core compiler - since what Unreal is doing for those special nodes it pretty much generate an internal callback that will resume execution after the node once its called - which pretty much has to happen on a compilation level, I assume).

So far for whats available on the market - obviously building such a system like the Rpg-Makers events would probably be overkill if you wanted to make just a game, in case of the Rpg-Maker its obviously being reused many times so the effort is actually worth it. I cannot comment on Lua or coroutines since I have no experience with that, but I hope this gives you some ideas about whats "on the market".

I know you stated you feel state machines require more involved coding, but have you considered behavior trees ?

http://www.gamasutra.com/blogs/ChrisSimpson/20140717/221339/Behavior_trees_for_AI_How_they_work.php

Engines like Unity and Unreal have visual scripting modules for behavior trees, which greatly simplifies their creation. I've been using behavior bricks https://www.assetstore.unity3d.com/en/#!/content/74816 and it works great but there are many options

I personally favor one of two approaches: either coroutines, or data-driven content. If you're interested in coroutines there are some cool things you can do with having code express all of the content. The opposite extreme is to power everything with data instead of code.

Another way to look at it is that if you use coroutines, you can write code that runs at the pace of the gameplay. If you use data, you "execute" at the game's framerate/simulation tick rate, and only update your state when the data indicates you should do so. It's kind of a state machine like system, but controlled through something that's easy to edit instead of trying to explicitly model all of the game's states and transitions in code.


Coroutines have pitfalls but they're pretty darn cool if you're building a game as a solo programmer. Data driving is powerful and scalable to large teams, but requires more infrastructure to do well.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Thanks!

Couple more questions:

1. Can anyone recommend a textbook/resource on (using) coroutines? There should be wealth of well-known techniques and conceptual models to using them to fullest, and it seems not ideal to rediscover everything through experience.

2. Are there any open-source jRPGs other than Valyria Tear and Hero of Allacrost and Fall of Imiryn?

Google finds this: http://lua-users.org/wiki/CoroutinesTutorial

I'm using coroutines in my Lua scripts as well. Hint: Let coroutine.resume return the elapsed time and you can build Wait( 1000 ) methods easily.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

I have written xml-cutscenes, and then parsed them in c++. It required me to write some cutscene-system, then something to control the parts of cutscenes. Like you will most likely have to have different way to declare which scripts are running in parallel, and which are sequentical. For example something along the line:


<cutsceneStart>
    <choise type="play cutscene" name="final attack"/>
    <choise type="play cutscene" name="run away"/>
<cutsceneStart>

<cutscene name="final attack">
    <say char="paul" text="I am not going down!" face="beated up"/>
    <play effect="theMostAwesomePowerupShaderEffect"/>
    <parallel>
        <walk char="paul" x="1" y="2"/>
        <character char="paul" useAnimation="swing"/>
        <play sound="swingSound"/>
    </parallel>
</cutscene>
<cutscene name="run away">
....
</cutscene>

Then you can parse each of the xml-element as a corresponding class, which can be derived from cutsceneBase. After the element has done it's task, tell the cutsceneEngine to play next thing. You could also make conditions which text to be shown, which could also be used to create branches for conversation.

The good thing is, that you can actually write a system to create cutscene-xmls, like cutscene editor and all the cutscenes would have specified format on them. The bad thing is that creating the xml-format might take some time.

This topic is closed to new replies.

Advertisement