= JME3 Cinematics
:revnumber: 2.0
:revdate: 2020/07/24


JME3 cinematics (com.jme.cinematic) allow you to remote control nodes and cameras in a 3D game: You can script and and play cinematic scenes. You can use cinematics to create link:http://en.wikipedia.org/wiki/Cutscene[cutscenes] and movies/trailers for your game. Another good use case is efficient "`destruction`" physics: Playing back prerecorded flying pieces of debris for demolitions is much faster than calculating them with live physics.

Internally, Cinematics are implemented as xref:core:app/state/application_states.adoc[AppStates].

Short overview of the cinematic process:

.  Plan the script of your movie. +
Write down a timeline (e.g. on paper) of which character should be at which spot at which time.
.  Attach the scene objects that you want to remote-control to one Node. +
This Node can be the rootNode, or a Node that is attached to the rootNode.
.  Create a Cinematic object for this movie scene. The Cinematic will contain and manage the movie script.
.  For each line in your script (for each keyframe in your timeline), add a CinematicEvent to the Cinematic.


== Sample Code

*  link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java[TestCinematic.java]


== How to Use a Cinematic

A Cinematic is like a movie script for a node.

[source,java]
----
Cinematic cinematic = new Cinematic(sceneNode, duration);
cinematic.addCinematicEvent(starttime1, event1);
cinematic.addCinematicEvent(starttime2, event2);
cinematic.addCinematicEvent(starttime2, event3);
...
stateManager.attach(cinematic);

----

.  Create one Cinematic per scripted scene.
**  `sceneNode` is the node containing the scene (can be the rootNode).
**  `duration` is the duration of the whole scene in seconds.
**  Each Cinematic is a set of CinematicEvents, that are triggered at a given moment on the timeline.

.  Create one CinematicEvent for each line of your movie script.
**  `event` is one motion of a moving object. You can add several events. More details below.
**  `starttime` is the time when this particular cinematic event starts on the timeline. Specify the start time in seconds since the beginning of the cinematic.

.  Attach the Cinematic to the SimpleApplication's stateManager.
.  Play, stop and pause the Cinematic from your code.

[cols="2", options="header"]
|===

a|Method
a|Usage

a|cinematic.play()
a|Starts playing the cinematic from the start, or from where it was paused.

a|cinematic.stop()
a|Stops playing the cinematic and rewinds it.

a|cinematic.pause()
a|Pauses the cinematic.

|===


== Events(CinematicEvents)

Just like a movie script consists of lines with instructions to the actors, each Cinematic consists of a series of events.

Here is the list of available CinematicEvents that you use as events. Each track remote-controls scene objects in a different way:
[cols="20,80", options="header"]
|===

a|Events(CinematicEvents)
a|Description

a|MotionEvent
a|Use a MotionEvent to move a Spatial non-linearly over time. A MotionEvent is based on a list of waypoints in a MotionPath. The curve goes through each waypoint, and you can adjust the tension of the curve to modify the roundedness of the path. This is the motion interpolation you are going to use in most cases.

a|SoundEvent
a|Use a SoundEvent to play a xref:audio/audio.adoc[sound] at a given time for the given duration.

a|GuiEvent
a|Displays a xref:gui/nifty_gui.adoc[Nifty GUI] at a given time for the given duration. Use it to display subtitles or HUD elements. Bind the Nifty +++<abbr title="Graphical User Interface">GUI</abbr>+++ XML to the cinematic using `cinematic.bindUi("path/to/nifty/file.xml");`

a|AnimationEvent
a|Use this to start playing a model xref:animation/animation.adoc[animation] at a given time (a character walking animation for example)

|===

You can add custom events by extending AbstractCinematicEvent.


=== MotionEvent

A MotionEvent moves a Spatial along a complex path.

[source,java]
----
MotionEvent events= new MotionEvent (thingNode, path);
----

Details of the constructor:

*  `thingNode` is the Spatial to be moved.
*  `path` is a complex xref:cinematic/motionpath.adoc[MotionPath].

To create a MotionEvent, do the following:

.  Create a MotinPath.
.  Create a MotionEvent based on the MotionPath.
.  Configure your MotionEvent (see below).
.  Add the MotionEvent to a Cinematic.

[cols="2", options="header"]
|===

a|MotionEvent configuration method
a|Usage

a|event.setLoopMode(LoopMode.Loop)
a|Sets whether the animation along this path should loop (LoopMode.Loop) or play only once (LoopMode.DontLoop).

a|event.setDirectionType(MotionEvent.Direction.None)
a|Sets the direction behavior type of the controlled node. Direction.None deactivates this feature. You can choose from the following options: LookAt, Path, PathAndRotation, Rotation.

a|event.setDirectionType(MotionEvent.Direction.LookAt)
a|The spatial turns (rotates) to keep facing a certain point while moving. Specify the point with the `setLookAt()` method.

a|event.setDirectionType(MotionEvent.Direction.Path)
a|The spatial always faces in the direction of the path while moving.

a|event.setDirectionType(MotionEvent.Direction.PathAndRotation)
a|The spatial faces the direction of the path, plus an added rotation. Use together with the `setRotation()` method.

a|event.setDirectionType(MotionEvent.Direction.Rotation)
a|The spatial spins (rotates) while moving. You describe the spin by a custom quaternion. Use together with the `setRotation()` method.

a|event.setLookAt(teapot.getWorldTranslation(), Vector3f.UNIT_Y)
a|The spatial always faces towards this location. Use together with `MotionEvent.Direction.LookAt`.

a|event.setRotation(quaternion)
a|Sets the rotation. Use together with `MotionEvent.Direction.Rotation` or `MotionEvent.Direction.PathAndRotation`.

|===

[TIP]
====
Most likely you remote-control more than one object in your scene. Give the events and paths useful names such as `dragonEvent`, `dragonPath`, `heroEvent`, `heroPath`, etc.
====


=== SoundEvent

A SoundEventplays a sound as part of the cinematic.

[source,java]
----
SoundEvent( audioPath, isStream, duration, loopMode )
----

Details of the constructor:

*  `audioPath` is the path to an audio file as String, e.g. "`Sounds/mySound.wav`".
*  `isStream` toggles between streaming and buffering. Set to true to stream long audio file, set to false to play short buffered sounds.
*  `duration` is the time that it should take to play.
*  `loopMode` can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.


=== GuiEvent

A GuiEventshows or hide a NiftyGUI as part of a cinematic.

[source,java]
----
GuiEvent( screen, duration, loopMode )
----

You must use this together with bindUI() to specify the Nifty +++<abbr title="Graphical User Interface">GUI</abbr>+++ XML file that you want to load:

[source,java]
----
cinematic.bindUi("Interface/subtitle.xml");
----

Details of the constructor:

*  `screen` is the name of the Nifty +++<abbr title="Graphical User Interface">GUI</abbr>+++ screen to load, as String.
*  `duration` is the time that it should take to play.
*  `loopMode` can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.


=== AnimationEvent

An AnimationEvent triggers an animation as part of a cinematic.

[source,java]
----
AnimationEvent( thingNode, animationName, duration, loopMode )
----

Details of the constructor:

*  `thingNode` is the Spatial whose animation you want to play.
*  `animationName` the name of the animation stored in the animated model that you want to trigger, as a String.
*  `duration` is the time that it should take to play.
*  `loopMode` can be LoopMode.Loop, LoopMode.DontLoop, LoopMode.Cycle.


=== Camera Management

There is a built in system for camera switching in Cinematics. It based on CameraNode, and the cinematic just enable the given CameraNode control at a given time.

First you have to bind a camera to the cinematic with a unique name. You'll be provided with a CameraNode

[source,java]
----

 CameraNode camNode = cinematic.bindCamera("topView", cam);

----

then you can do whatever you want with this camera node : place it so that you have a the camera angle you'd like, attach it to a motion event to have some camera scrolling, attach control of your own that give it whatever behavior you'd like.
In the above example, I want it to be a top view of the scene looking at the world origin.

[source,java]
----

 //set its position
 camNode.setLocalTranslation(new Vector3f(0, 50, 0));
 // set it to look at the world origin
 camNode.lookAt(Vector3F.ZERO, Vector3f.UNIT_Y);

----

Then i just have to schedule its activation in the cinematic. I want it to get activated 3 seconds after the start of the cinematic so I just have to do

[source,java]
----

 cinematic.activateCamera(3,”topView”);

----


=== Customizations

You can extend individual CinematicEvents. The link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/animation/SubtitleTrack.java[SubtitleTrack.java example] shows how to extend a GuiTrack to script subtitles. See how the subtitles are used in the link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java[TestCinematic.java example].

You can also create new CinematicEvent by extending link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-core/src/main/java/com/jme3/cinematic/events/AbstractCinematicEvent.java[AbstractCinematicEvent]. An AbstractCinematicEvent implements the CinematicEvent interface and provides duration, time, speed, etc… management. Look at the link:https://github.com/jMonkeyEngine/jmonkeyengine/blob/master/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java[TestCinematic.java example] is to use this for a custom fadeIn/fadeOut effect in combination with a com.jme3.post.filters.FadeFilter.


== Interacting with Cinematics


=== CinematicEventListener

[source,java]
----
CinematicEventListener cel = new CinematicEventListener() {
  public void onPlay(CinematicEvent cinematic) {
    chaseCam.setEnabled(false);
    System.out.println("play");
  }

  public void onPause(CinematicEvent cinematic) {
    chaseCam.setEnabled(true);
    System.out.println("pause");
  }

  public void onStop(CinematicEvent cinematic) {
    chaseCam.setEnabled(true);
    System.out.println("stop");
  }
}
cinematic.addListener(cel);
----


=== Physics Interaction

Upcoming.
