Cutscenes using cinematics

The previous recipe explored the possibilities of using MotionPaths to move objects around. One step up from that and a way to organize many events in a sequence is cinematics. It can be used both to create in-game scripted events and advanced cutscenes. The power of a well-scripted in-game event should not be underestimated but neither should the time it takes to get one right.

In this recipe, we'll explore the possibilities of the Cinematics system by creating a cutscene using content that we have created before.

Getting ready

Some basic knowledge of MotionPaths and MotionEvents is required. Checking out the Performing complex movements with MotionPath recipe should provide enough information to get started. A new class that is introduced is the Cinematic class. This works as a sequencer or manager of many events firing them at set times. The events don't just have to be MotionEvents but could be AnimationEvents dealing with skeleton-based animations, SoundEvents, and even GuiEvents. It can also manage several cameras and switch between them.

Before starting the actual implementation of a cinematic scene, it's good to have some kind of script describing what is going to happen. This will help organize the cinematic event and will save time in the end.

This recipe will use the TestScene from Chapter 1, SDK Game Development Hub. We can also use the animated sky box from earlier in this chapter. It will display Jaime walking from his initial position to stand by the edge of the water looking into the horizon. While he's walking, several switches between panning cameras will occur.

Getting ready

Using an empty node as a waypoint

Finding out good waypoints for characters and cameras can be difficult enough and it's not any easier if you have to do it all in code. A trick is to use the SceneComposer to create markers much like real movie makers use tape to designate where actors should move. Right-clicking on the scene node and selecting Add Spatial.../New Node will give us an invisible marker. Give this a recognizable name and drag it into place by using the Move function.

How to do it...

So now that we have a scene prepared with some waypoints we can get to work with implementing the cutscene itself by performing the following steps:

  1. We start by loading a scene where the Cinematic will be played out. The scene reference will be used in several places, so it is a good idea to store it in a field.
  2. We'll also create a Spatial field called Jaime, the main actor and either load or extract him from the scene (depending on the setup).
  3. Now, we can create a MotionPath instance called jaimePath for Jaime. Since we created Nodes for each waypoint in SceneComposer, we can get their location from the scene by using:
    jaimePath.addWayPoint(scene.getChild("WayPoint1").getWorldTranslation());
  4. We go on and create a MotionEvent called jaimeMotionEvent using jaimePath and initialDuration of 25 seconds:
    jaimeMotionEvent = new MotionEvent(jaime, jaimePath, 25f);
  5. It's also preferable if Jaime faces in the direction of the path he travels along, so we also set directionType to MotionEvent.Direction.Path.

Before we get too far, we want to check out that the path Jaime follows is alright. Therefore, we should go ahead and create a Cinematic instance at this point. To do this, perform the following steps:

  1. Doing this is as simple as supplying it with a scene, which will affect rootNode together with the total duration of the cinematic:
    cinematic = new Cinematic(scene, 60f);
  2. After that, we add MotionEvent with the following line; 0 being the time it should start at:
    cinematic.addCinematicEvent(0, jaimeMotionEvent);
  3. We also need to add cinematic to the stateManager of the application with stateManager.attach(cinematic).
  4. Calling cinematic.play() at this point should display Jaime sliding along the path.

Once we're happy with it, we can go on and do the camera work as follows:

  1. The Cinematic instance will create a CameraNode for us if we call cinematic.bindCamera("cam1", cam), so let's do that for our first camera. The string is the reference that Cinematic will know the camera by.
  2. It will be a camera that pans so we create a MotionPath instance and a MotionEvent instance for it. Again, we can get the waypoints of the camera path from the scene. Since the Node we added in SceneComposer by default snaps to the ground, we need to add between 1.5f and 2.0f to the y axis to get it to a suitable height.
  3. The camera should look at a fixed point as it pans, so we set directionType of camera's MotionEvent to LookAt and then also set the direction it should look at with cam1Event.setLookAt where the first Vector3f is the location to look at and the second is Vector3f, which is up in the world:
    cam1Event = new MotionEvent(camNode, camPath1, 5f);     cam1Event.setDirectionType(MotionEvent.Direction.LookAt);
    cam1Event.setLookAt(Vector3f.UNIT_X.mult(100), Vector3f.UNIT_Y);
  4. With that done, we can test the first camera pan. We do that by calling the following code:
    cinematic.activateCamera(0, "cam1");
  5. The next camera will get its own MotionPath and MotionEvent instances and can similarly get its own CameraNode. It's perfectly fine to use the same physical camera for both of the CameraNodes.

Now, we can start doing something about the lack of animation in the scene.

  1. The first thing Jaime does in the scene is walk towards the beach. We can create a new AnimationEvent instance that uses the Walk animation:
    AnimationEventwalkEvent = new AnimationEvent(jaime, "Walk", LoopMode.Loop);
  2. We then add it to cinematic at 0 seconds:
    cinematic.addCinematicEvent(0, walkEvent);
  3. Jaime should stop when he reaches the last waypoint, which is also when the jaimeMotionEvent ends. So we create another AnimationEvent with the idle animation and add it at the end of the duration of the jaimeMotionEvent.

Tip

At the time of writing this, it seems the cinematic doesn't end the animation when it starts a new one, so we have to do something to stop it ourselves. Using a MotionPathListener, we can check when the final waypoint is reached and manually stop the walking animation:

jaimePath.addListener(new MotionPathListener() {
  public void onWayPointReach(MotionEventmotionControl, intwayPointIndex) {
    if(wayPointIndex == 2){
      walkEvent.stop();
    }
  }
});

How it works...

The Cinematic acts as a sequencer for all the different events and apart from firing events at the defined intervals, we can instead use cinematic.enqueueCinematicEvent. Doing so will start the supplied event just after the previous one is done. This can be useful if we want to trigger a series of animations right after each other. Cinematics can also be set to loop or cycle just like MotionEvents and you don't need to start them at time 0.

In conclusion, using cinematics is not particularly technical. It's just difficult to get all the positions, angles, and timings right, especially since there's no intelligence or collision involved in the script. Once you get it right, however, the result will be extremely rewarding.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.77.208