Note: This is an old page that has been updated/formatted, you can view the original here.
MotS-Style Cutscenes Tutorial
What they are, how they work and how to create them
By Antony Espindola
One of the best features of the Jedi Knight series of games is the fact that they contain "cutscenes" when you finish either a level or certain objectives. These are like mini-movies which help with the flow of the story being told by the game. The cutscenes in Jedi Knight were pre-recorded video which is then saved to the CD in a special format and then played back.
With the Mysteries of the Sith add-on pack, the cutscenes changed from video to being "acted" out by the graphics engine of the game itself. These cutscenes were also recorded to the CD (as .san files) but were created using the game engine and COGs. This tutorial shows you how to achieve the same effects by writing your own COGs to control Actors and the camera(s). You'll need some COG experience, which you can get from other tutorials, but I'll explain everything as best I can so hopefully you won't need too much experience to understand it!
Cutscenes make a nice addition to any SP level. They give a professional feel and are easy to write. At least you don't have to hire (and pay) actors! The best way to implement any cutscenes is to have a story. Use the cutscenes to enforce the story and give your player a break from the action. It's nice to see a quick intro to a level (How did Kyle/Mara get there? Why are they there?) and also to wrap up the ending to the story.
Now you have some idea about what you want to see in your cutscenes, we'll discuss how you go about implementing your ideas. We all know from using Security Cameras in levels that you can view your level from a different object - this is the basis for our "camera". A camera is only a "thing" which can have frames and can therefore be moved around. Now we have a panning, tilting and moving camera!
Our actors are just that - Actor Things. We can create things that use any template and that includes Kyle, Mara, StormTroopers, Darth Vader and so on. Each Actor has commands which can be used to get it to do things - move to positions, look in certain places and even fire their weapons or die. In fact, anything in the game can be done with the right COG commands.
One thing that we cannot do however, is control the Player object. We must therefore be a bit sneaky and replace the Player object with an exact copy which is really an Actor object instead. We then hide the Player object by making it invisible! That way, we control the Actor, don't worry about the player and you just see the cutscene!
Setting the Stage
The first thing that we need to do for our cutscene is to create a stage for our actors to work with. This is simply part of the level where we can create our Actors, move them around, play out the cutscene and then pass control back to the game and the player. This area doesn't have to be part of the actual level; you can create it seperately from the main level in a bit of space that you have spare. This works best for levels where the action takes place inside something, such as a Bulk Cruiser, but we want the cutscene to show some action outside, such Pirate Fighters attacking it. Just create a large cube in the corner of your level somewhere, dress the inside with the ships and stars and viola! One make-shift studio!
Implementing our COGs
I assume that you know how and when COGs can be called, such as entering sectors, touching surfaces, activating objects and so on. We can also use the Master COG to produce a small intro cutscenes when our level starts. Maybe Kyle has just landed and discovers the hidden entrance to an Imperial Base in a mountain. You get the idea! Assuming that we have an area in our level for our Actors to "work" in, we can create our Startup COG to start playing our cutscene. You can edit the startup COG directly or call another COG from within. I'll leave that up to you as it depends on your COGging ability!
Camera Implementation
We know that viewing the cutscene as Kyle/Mara isn't going to very exciting, so we need to create one or more external cameras. We simply place "ghost" objects, as these don't appear in the game and are invisible, into our level. Change the Roll, Pitch and Yaw of the camera to get it pointing in the right direction. This is a bit hit-and-miss, so you'll have to give it a try and keep testing until you get it right. Just a few attempts will give you the feel for it.
Using frames in our camera things allows us to setup positions where we want the camera to move between. We then simply use our COG to move the camera around between the frames. Before we do that, we first want to switch the view to our camera object. This is done with this little bit of code:
// switch to camera view
SetCameraFocus(0, CameraThing);
The first parameter, a 0
in this case is always used as a zero. This
defines "internal" view, just like the internal view for Kyle. As other
objects don't have external views, always make sure this parameter is
zero!
The CameraThing
object has been defined as a Thing and assigned to our
Ghost object we want to use as a camera. When this piece of code is now
called, we see the view from that Object.
Use this code to switch between any number of cameras you have defined in your COG.
We also need to know how to set the view back to the player. This is done using the code:
// switch to player view
SetCameraFocus(0, jkGetLocalPlayer());
This just switches the camera back to the internal view for the player. Simple!
Introducing the Grip
Ever wondered what the "Grip" is in a film's credits? Apparently it's the poor person who has to pull the cart with the camera attached when they film a moving shot. This is what we are now going to look at - moving our camera.
Moving any object can be done with frames. A frame is a definition of an object in a certain position. Most objects don't have frames because they don't need to move. If you've ever created a door in your level, you'll know about how frames work. You define a series of frames for an object, a bit like a "dot-to-dot" puzzle. When you use the COG to move the object to the next frame, or "dot", the game engine fills in between the frames, moving it along the "line". This allows us to create complicated paths for our camera which it can move along.
Getting the camera to move between frames is a doddle:
// move camera between frames
MoveToFrame(CameraThing, 1, speed);
MoveToFrame(CameraThing, 2, speed);
MoveToFrame(CameraThing, 3, speed);
No! That's too easy! Yes, it really is that easy to move a camera around. You don't need to implement any sort of delays in between moving the frames as the game engine will wait until the camera has finished moving before setting it off to the next frame.
In case you didn't guess it, CameraThing
is our Ghost object we're using
as a camera. The number, 1
, 2
, 3
etc is the number of the frame you want
the object to move to. The last number (a float) is Speed
which is just
that - the speed the object moves.
Because our ghost "camera" object is just another object, we can use this technique to move any object around, including the models of ships.
Emotions Dahling, Emotions...
Now we need to look at how we create, move and animate our Actors. Remember I said earlier about cheating with the Player by replacing it with an Actor? This is how you do it:
thing player local
thing kyle local
template gunless=KyleBryarActor local
[...]
// setup first
player = GetLocalPlayerThing();
jkBeginCutscene();
// switch the player for an Actor
SetActorFlags(player, 0xa00000);
StopThing(player);
Kyle = CreateThing(gunless, player);
StopThing(Kyle);
SetThingCurGeoMode(player, 0);
You'll notice I've introduced a few extras in there. The first bit gets
the handle for our player. The next line, jkBeginCutscene();
tells the
game engine that we are acting out a cutscene and that the user must
sit and watch it. It stops them pressing "Escape" to get out to the
setup screens.
The SetActorFlags(player, 0xa00000);
makes the player object (that we
setup earlier) unable to respond to the player's interactions. This
stops them running around, shooting or whatever when we're trying to do
our cutscene!
We also use StopThing(player);
as this stops the player dead in their
tracks. If you don't have this line, it's possible to move the mouse and
when the SetActorFlags()
line is executed, the player can spin on the
spot! Looks kinda funny!
The next lines are where we do our swapping. We setup an object (called
Kyle
) to be a new object that we create using the template defined for
gunless
, at the position of player
. This will create an exact replica of
Kyle where the player is standing. At this point, we haven't yet
switched to our camera view so you can't see it.
We also want to make sure that our new Kyle actor isn't doing anything,
so we use the StopThing()
command again.
Finally, we make our player "invisible" by using the
SetThingCurGeoMode()
to set their Geo Mode to zero, which is "not
drawn".
We are now free to use our camera code to switch to an external view and watch the action!
Using the same code to create Actors at points, using different templates such as the StormTrooper or Darth Vader, we can create more Actors in our scene. If the scene is in an area the player cannot get to, the Actors can always be predefined, ready and waiting for the words "Action!".
What's my motivation for this scene...?
Now we have all our actors, scenery and the like ready and waiting, we just need to animate the Actors. There are many commands you can use to do this. A good reference is at the JK Specs pages under the Actors section in "COGs".
Another good place to check on how LucasArts did their cutscenes is by looking in the CutScenes.zip file which is on your MotS CD. This has all the COGs used to create all the cutscenes from the game. It's interesting to look through these and see some of the names they gave the StormTroopers. I would have thought they'd have had more macho names than Kevin or Brian!
Anyway, here are a few commands you can use to create some effects:
AISetMoveSpeed(Kyle, 1.0);
AISetLookPos(Kyle, GetThingPos(BombGhost));
AISetMoveThing(Kyle, BombGhost);
This shouldn't take too much explaining as it's pretty obvious. This
code comes from one of my levels where Kyle looks towards an object
(BombGhost
) and then runs towards it. Don't forget to set the
AISetMoveSpeed
to make it look right.
Play it again Sam
You can also play Keys for an actor to make it look like they are doing things. Define the key you want to play by looking in that Actor's .PUP file as this will tell you which keys do what action. You then "play" the key and the Actor does the action:
keyframe crouch=kyusef0.key local
int keynum local
[...]
keynum=PlayKey(Kyle, crouch, 1, 0x4);
// now do some other stuff
StopKey(Kyle, keynum, 0);
This code plays the animation key for kyle to crouch down and look like
he is either picking something up, putting it down or activating it. The
KeyFrame will play continuously until you use the StopKey()
command to
stop playing it. Use a Sleep()
command to get the COG to wait until you
have finished what you are doing.
That's a Wrap!
Now you've done your acting, you need to tidy-up after yourself at the end of the code. This includes things like switching back to the Player's viewpoint, turning off the CutScene command, making the player visible again and so on. Here's the code:
// stop cutscene
jkEndCutscene();
// switch back to player focus
SetCameraFocus(0, Player);
// unfreeze Kyle
ClearActorFlags(Player, 0xa00000);
...and you're off again!
I haven't gone too deeply into creating cutscenes- just shown you how to create them and what you need to do to get the right effects. As always, experimentation is the best way of getting the knowledge, so get out there, stoke up your copy of JED and make yourself some tasty COGs which are good enough for an Oscar!
Happy Hunting!