Voice Over Triggers
First, change the level that you load initially to "Assets/Level01.json"
.
EnergyLauncher Cooldown
It turns out that the "cooldown"
property for EnergyLauncher
is not actually set for every EnergyLauncher
. If it’s not set, you want the cooldown for the EnergyLauncher
to be a default of 1.5f
. You can easily do this just in LevelLoader
. When you read in the "cooldown"
property, initialize the float you’re using to 1.5f
instead of 0.0f
.
VO Trigger Basics
The basic idea of a VOTrigger
is that it becomes activated the first time the player collides with it. It then starts playing a sequence of voice over lines while having the HUD
show corresponding subtitles. When the sequence of lines finishes, the VOTrigger
can optionally open a door or load a different level.
For example, here’s one of the triggers in Level01.json:
{
"type":"VOTrigger",
"pos":[0.0, 0.0, -50.0],
"door":"1",
"sounds":[
"Glados-01-01.ogg",
"Glados-01-03.ogg",
"Glados-01-04.ogg"
],
"subtitles":[
"Hello and, again, welcome to the Aperture Science computer-aided enrichment center.",
"Your specimen has been processed and we are now ready to begin the test proper.",
"Before we start, however, keep in mind that although fun and learning are the primary goals of all enrichment center activities, serious injuries may occur."
]
}
In this case, there are three voice over sounds that will play, one after another. When the last sound finishes, the "1"
door should open.
For simplicity, you may assume that the "sounds"
and "subtitles"
arrays will always be the same size.
Create an Actor
subclass called VOTrigger
. It needs:
- A
CollisionComponent
with size(1, 1, 1)
but it is NOT a collider so don’t doAdd
/RemoveCollider
for it - An override of
OnUpdate
- An override of
OnProcessInput
In LevelLoader
add code that creates a VOTrigger
on type "VOTrigger"
. Don’t worry about the additional properties of VOTrigger
yet.
Make sure your code compiles and runs, and you load into Level01:
Adding Additional Properties
Add the following private
member variables to VOTrigger
:
- A
std::string
for the door name - A
std::string
for the next level - A
std::vector<std::string>
for the sounds - A
std::vector<std::string>
for the subtitles
Then in LevelLoader, add code to parse the properties from the JSON and set the corresponding members in VOTrigger
(either with the constructor or with setter functions):
- Door name -
"door"
- UseGetStringFromJSON
- Next level -
"level"
- UseGetStringFromJSON
"sounds"
- UseGetStringArrayFromJSON
"subtitles"
- UseGetStringArrayFromJSON
Use breakpoints to confirm that you’re parsing the properties properly. The first and second VOTrigger
s in the level have a "door"
property while the third one has a "level"
property. Both should have "sounds"
and "subtitles"
.
Basic Playback
The first time the VOTrigger
intersects with the player, the VOTrigger
should activate and play the first sound.
Once a VOTrigger
is activated, on every frame it should check if the sound it’s playing is SoundState::Stopped
, in which case it should start playing the next sound (if there is one). If there are no sounds left to play:
- If the door name member variable is set to something, open that door
- If the next level member variable is set to something, set the next level in
Game
to it, which will force the level to change to the specified level - Set the
VOTrigger
itself toActorState::Destroy
, so it gets removed
Hint: We recommend you make a function to “play next sound” and you can use that both for the initial sound and any subsequent ones, as well as checking in there if there are sounds left.
Once your VOTrigger
is implemented correctly, it should work something like in this video. Notice that when the first set of lines finish, the door opens. And after the “You’re doing very well” line, the game loads into Level02. (Note that I started the video recording during the first VO line, you only hear the second half of the very first introductory line. You should hear the entire line in your game.)
Fast Forward
Particularly for testing purposes, we want to be able to quickly advance through voice over lines. Add code in VOTrigger::OnProcessInput
that, on a leading edge of the F
key, stops the current sound (if the VOTrigger
is playing one).
Confirm that you can press the F
key to skip the current line, and if there are other sounds left in the trigger, those also continue to still play.
Subtitles
To add subtitles, we first need to add a new HUD
component to the player. Create a subclass of UIComponent
called HUD
that has a constructor, destructor, and an override of the Draw
function (from UIComponent
).
In Player
, create a HUD
instance and save it in a member variable, and add a getter for it.
Make sure your HUD
compiles.
Loading the Font
For fonts, we use the SDL_TTF
library. In Game.cpp
:
- Add an
#include <SDL2/SDL_ttf.h>
- In
Game::Initialize
, right before the call toLoadData
, callTTF_Init();
- In
Game::Shutdown
, right before the call toSDL_Quit
, callTTF_Quit();
In HUD
, add a class Font*
member variable. In the constructor, dynamically allocate the Font
and call the Load
function, passing in "Assets/Inconsolata-Regular.ttf"
as the file name.
In the destructor, call Unload
on the font and delete
it.
Creating Subtitle Textures
The way the Font
class works is you call RenderText
on it, passing in the string and a color, and it will return a pointer to a texture that contains the text.
Add two class Texture*
pointers to HUD
– one for the subtitle and one for the subtitle shadow, and initialize them to nullptr
.
Add a new function to HUD
called ShowSubtitle
that takes in a string for the text to show. It should do the following:
- At the start of the function, if the subtitle texture pointer is not null, call
Unload
on both the subtitle and subtitle shadow textures, delete both, and set both pointers to null. - If the text to show isn’t empty:
- Concatenate
"GLaDOS: "
to the start of the text to show - Call
RenderText
on the font once with the text from (a), passing inColor::LightGreen
and saving the texture in the subtitle texture - Call
RenderText
a second time with the same text from (a), this time passing inColor::Black
and saving it in the subtitle shadow texture
- Concatenate
Then back in VOTrigger
, every time you play the next sound, you should also call ShowSubtitle
on the player’s HUD
with the correct text from "subtitles"
. Make sure that when there are no sounds left, you call ShowSubtitle("")
to clear out the texture.
Make sure your code doesn’t crash when playing VO. You won’t see the subtitles yet.
Drawing the Subtitles
You do the drawing in HUD::Draw
. If mSubtitle
is set, you first need to calculate the Vector2
screen position for the subtitles:
- The x-component is 0
- The y-component is
-325.0f + height / 2.0f
, where height is the value you get when you callGetHeight()
on the subtitle texture
To draw the texture, call DrawTexture
which is inherited from the parent, passing in shader
, the texture to draw, and the position to draw at. For now, just draw the subtitle, not worrying about the shadow.
You should now see the subtitles, though they may be difficult to read without the shadow. Confirm that the subtitles change with each VO line, and when the VO ends, the subtitle goes away. The subtitle will look like this:
Adding the Shadow
The trick to add the shadow is just that right before you draw the subtitle texture, first draw the subtitle shadow texture but at the calculated position plus an offset of (2.0f, -2.0f)
.
You should now see a shadow behind the subtitle, which makes it much easier to read:
Once you’ve pushed this code, you’re ready to move on to part 2.