Cutscene system

Yesterday and today I spent the time making a cutscene system. I’m really proud of the result and thought other might also benefit from it. You can find the content here and a demo place to take a look for yourself here.

Below is a big overview of how exactly the cutscene system works (no tl;dr available). If you have any questions, find any bugs or if you have any suggestions let me know. I might not add the features or fix the bugs if I don’t think it’s important enough, but that of course doesn’t mean that I won’t allow you to do it yourself! And finally, some of the code in the cutscene system might not be perfectly clean or perfectly follow the standards of programming in Lua but oh well, I tried. ¯\_(ツ) _/¯


Overview

Parenting

The cutscene system exists out of a LocalScript with a small API bound to it and a ScreenGui. You don't actually have to do anything with the ScreenGui and the bindables inside of it. Just put it in the StarterGui and it'll work. The LocalScript on the other hand is the important part. It should be put into the PlayerScripts folder (but you can put it anywhere probably) and it looks like this:

Creating a cutscene

If you want to create a Cutscene you'll first have to require() the Scene module. It will return a table with 2 methods:

Scene.NewScene(string Name)
Scene.NewComponent()

You will want to create a new Scene object with Scene.NewScene() which will then return a Scene object. You can then create new Components to add to the Scene. Scene.NewComponent() will return a new object with a lot of properties (see the ModuleScript for specifics). You can add those components to the Scene object with Scene:AddComponent(component object). Components are added in order, so the first component you add with Scene:AddComponent() will be seen by the cutscene system as ‘Component 1’.

If you want to start a cutscene all you have to do is fire the Cutscene.API.RunScene BindableEvent with a Scene object as the first argument and an optional second argument which is an integer that indicates which Component should be displayed first.

Events

The cutscene system also has a CutsceneEnded and CutsceneStarted BindableEvent which are both fired automatically. CutsceneEnded is fired when a cutscene is finished playing. It will return the name of the Scene object and the number of the last component which was shown. CutsceneStarted is fired right when a cutscene starts and return the name of the Scene object and the number of the component that will be shown first.

Features

I won’t give any example of how you can use every single feature as that would take too long and you can take a look at the place I linked at the start for it. I’ll instead just describe each feature and what you can do with it.

Dialog

The cutscene system allows you to display dialog. You will be able to set the dialog which is written at a speed of 1 character per frame. You can also give the dialog a title - for example the name of the NPC who is speaking - and you can choose to align it to the left or right of the screen. You can choose to start displaying the dialog right as the component is being displayed (OnEnter) or only after the camera has stopped moving (OnFocus).

One of the most exciting features of the dialog - in my opinion at least - is the response system. You can choose to display just dialog for X seconds before transitioning to the next component or you can add responses to dialog. The system allows up to 4 answer options. You can pick per answer which component will be shown after the player chooses that answer. Additionally, you can set a timeout on the responses. If you player doesn’t pick an option within the time the cutscene system will then jump to a component you picked beforehand.

Transitions

When a component is being displayed the cutscene system will 'transition' to a certain camera angle. You can choose how long the interpolation takes and the CFrame the camera should move towards. Additionally, you can choose what kind of transition you want: "Interpolate" which uses Camera:Interpolate(), "Tween" which manually lerps the camera or "Jump" to instantly cut to the given CFrame.

Actions

You can add code to a component to be run. Each component can run code at two moments: When the component is being shown (OnEnter) and/or when the component has finished its transition animation (OnFocus). The way it works is you basically define Component.Action.OnEnter or Component.Action.OnFocus as a function and the cutscene system will then automatically run those functions at the right moment. Keep in mind that the code is of course local, so it won't replicate if FilteringEnabled is true.

Notes

1. The cutscene system doesn't prevent the player from walking or jumping. You'll have to listen to Cutscene.API.Events.CutsceneStarted to manually remove the controls and Cutscene.API.Events.CutsceneStopped to give back the controls.
  1. The cutscene system will disable the ‘Dead’ HumanoidStateType to prevent players from resetting during a cutscene. If they reset anyways it will only take effect after the cutscene has ended.

  2. The cutscene system works on PC and XBox and probably also on mobile devices (although I haven’t tested it for those). The system will also respect (un)plugging an XBox controller and automatically adjust the UI for it.


Known issues

So far there's only one issue I'm aware of:

The default ControlScript uses ContextActionService to bind the ButtonA from the XBox controller to jumping. This cutscene system however tries to bind ButtonA to dialog options, so the ControlScript ends up overriding this 99% of the time. If you want to use this system it’s recommended to edit the ControlScript so you can easily unbind and rebind the ButtonA KeyCode.

36 Likes