As part of my current game project, we wanted to capture that retro aesthetic when setting up the game world. To do that, we created a simple text-based cutscene system with a skip function. Here’s an example of it working in-game.

Soul Taker Example Of Cutscene

As you can see it goes back to the old days when games would start out with a simple text to start the story. Now that’s cool and all, but how do you do it yourself? I’ll show you; let’s get started.

Setting Up The State

The first step is to create the state. In Flixel each scene in your game could be considered a FlxState. Now, by creating a state we have to set up the class properties. The first thing we’ll need is a skip threshold property. The next thing we need is to create a FlxTypedText. The skip threshold(Float number) allows us to create a time before skipping the cutscene. The FlxTypedText allows us to create that old school text effect where the text streams in one character at a time. These are the two main ones to be considered, but there are other roles as well. Here’s the code we’re going to use below.

package;
//Imports for the FlxState
import flixel.addons.text.FlxTypeText;
import flixel.text.FlxText;
import flixel.FlxState;
// Type Information to create a delay that you want for each individual text that appears on screen
typedef SceneText = {
public var text:String;
/**
* Delay in seconds
*/
public var delay:Int;
}
class CutsceneState extends FlxState {
public var sceneText:FlxTypeText;
public var textIndex:Int; //Text index to determine which text to display
public var textList:Array<SceneText>; //Text list for al lthe text in the cutscene
public var nextState:FlxState; //State that we want to transition to after the cutscene
public var skipBar:FlxBar; //Bar displayed on screen for when trying to skip
public var skipText:FlxText; //Text for skipping
public var skipThreshold:Float; //Skip Delay aka threshold for when skip is is complete
public var skipPerc:Int; //Percentage using Integers
/** * Delay before transitioning to the next text in seconds.
*/
public var textDelay:Float; //Delay before switching to another text option
//Static variables used for setting up some basic types
public static inline var TEXT_WIDTH:Int = 400;
public static inline var INIT_TEXT_DELAY:Int = 3;
public static inline var SKIP_THRESHOLD:Float = 2.5;
public function new(newState:FlxState, textInfo:Array<SceneText>) {
textIndex = 1;
skipThreshold = 0;
skipPerc = 0;
textList = textInfo;
nextState = newState;
textDelay = 0;
super();
}
}

As you can see this is the initial setup for the state, which will initialize some of the important elements for us. As mentioned above, Flixel provides some out of the box elements to help us create the effect with the FlxTypedText. But, do note that the FlxTypedText requires you have Flixel-addons unchecked in your project.xml. The next thing we have to do is start creating the on-screen elements.

Creating The Properties

Here in the create function we assign the properties with new instances of their respective class. Now, you would put all these in their own create function personally. You can see the implementation below.

class CutsceneState extends FlxState {
//Additional code from before here
public static inline var TEXT_WIDTH:Int = 400
override public function create() {
createSkip();
createSceneText();
transitionText();
super.create();
}
public function createSkip() {
var margin = 48;
var barWidth = 100;
var x = FlxG.width (barWidth + margin);
var y = margin;
var textSize = 12;
skipBar = new FlxBar(x, y, LEFT_TO_RIGHT, barWidth, 20, this,
"skipPerc", 0, 100, true); //Create the skip Bar
skipBar.createFilledBar(FlxColor.BLACK, FlxColor.RED, true,
FlxColor.WHITE); //Create the aesthetic of the bar
skipText = new FlxText(skipBar.x + (skipBar.width / 2), (skipBar.y),
50, 'Skip', textSize); //Setting up the text
skipText.y += 2;
skipText.x = 12;
skipText.color = FlxColor.WHITE;
//Making the bar and text invisible until we start processing a skip
skipBar.visible = false;
skipText.visible = false;
//Most important adding them to the scene
add(skipBar);
add(skipText);
}
//Creates the large text we see on the scene
public function createSceneText() {
var textSize = 16;
sceneText = new FlxTypeText(0, 0, TEXT_WIDTH, '', textSize);
sceneText.screenCenter();
add(sceneText);
}
//Moves the text forward and displays it on screen. We started our index at -1, so this moves it to the first
//text.
public function transitionText() {
textIndex++;
//Grabs the text from the list and resets the text. Starts displaying the text and resets the delay.
var currentText = textList[textIndex % textList.length];
sceneText.resetText(currentText.text);
sceneText.start(0.05);
textDelay = 0;
}
}

The above sets up the major on-screen elements in relation to one another on-screen. See how the skip bar is placed in the top right corner of the screen. The text you saw in the video is position over the bar using the above. Also, notice that we add the text after the bar in order to make sure the skip text is displayed on top of the bar in-game. Now, we need to setup the logic for the state.

Setting Up The Update Logic

Now, we need to focus on the logic. The logic here is that we need to advance the text after a certain amount of delay. For example, we need to check for when the time of the delay has finally hit the time set in the current text shown on screen in that array we declared earlier. Once the delay is hit, we get the next as shown in the transition text function. Now, here’s what that update code looks like.

/**
* Update Code – HaxeFlixel
* Kino Rose
*/
class CutsceneState extends FlxState {
//Code from Before
override public function update(elapsed:Float) {
super.update(elapsed);
updateSkip(elapsed);
updateText(elapsed);
}
public function updateSkip(elapsed:Float) {
// Update Percentage that's shown on screen and also controls the visibility of the skip text and bar.
skipPerc = Math.ceil((skipThreshold / SKIP_THRESHOLD) * 100);
if (skipPerc > 0) {
skipBar.visible = true;
skipText.visible = true;
} else {
skipBar.visible = false;
skipText.visible = false;
}
//If the Z key is pressed we start the skip, which prompts the skip bar to show
//Otherwise the bar drains slowly over time
if (FlxG.keys.anyPressed([Z])) {
skipThreshold += elapsed;
} else if (skipPerc < 100 && skipPerc > 0) {
skipThreshold = elapsed;
}
if (skipThreshold >= SKIP_THRESHOLD) {
transitionToScene();
}
}
//Handles updating the text or switching scenes on the delay is hit
public function updateText(elapsed:Float) {
var currentText = textList[textIndex % textList.length];
if (textIndex < textList.length 1) {
if (textDelay > currentText.delay) {
transitionText();
}
} else if (textDelay > currentText.delay) {
transitionToScene();
}
textDelay += elapsed;
}
public function transitionText() {
textIndex++;
var currentText = textList[textIndex % textList.length];
sceneText.resetText(currentText.text);
sceneText.start(0.05);
textDelay = 0;
}
//Transitions to a new scene using a simple fade to black
public function transitionToScene() {
FlxG.camera.fade(FlxColor.BLACK, 1, false, () -> {
FlxG.camera.fade(FlxColor.BLACK, 1, true);
FlxG.switchState(nextState);
});
}
}

This code allows the cutscene to advance on its own. This is the important logic necessary to get everything working. Once all of this is set up, the only thing you need to do is pass in SceneText and the state you want to move to when you use the Cutscene state.

With that said, that’s how you setup the cutscene. In that regard, I hope it helps you. Good luck game making!

%d bloggers like this: