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!
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!
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.
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!".
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.
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.
// 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!