Skip to content

Game.CinematicCamera.CinematicCameraSequence

Assembly: Assembly-CSharp
Namespace: Game.CinematicCamera

Type: class

Base: IJsonWritable, IJsonReadable

Summary: CinematicCameraSequence represents a cinematic camera timeline used by the game's cinematic/photo mode. It stores per-component animation curves for camera position (X/Y/Z) and rotation (X/Y), plus a list of named modifier curves (for driving other photo-mode properties such as exposure). It supports adding/removing keys, sampling transforms at a given time into a camera controller, looping the timeline (with automatic key insertion to close the loop), and JSON (de)serialization. The class also contains logic to "patch" rotation (yaw) keys so rotation interpolation remains continuous across the timeline.


Fields

  • private bool m_Loop
    Controls the backing storage for the loop property. When set through the loop property, additional processing (EnsureLoop / PatchRotations) may run.

Properties

  • public List<CinematicCameraCurveModifier> modifiers { get; set; }
    Collection of named modifier curves. Each modifier is a CinematicCameraCurveModifier containing id, an AnimationCurve, and optional min/max clamps. Default: empty list.

  • public CinematicCameraCurveModifier[] transforms { get; set; }
    Array of 5 CinematicCameraCurveModifier entries used for camera transforms: PositionX, PositionY, PositionZ, RotationX, RotationY (see TransformCurveKey). Each entry holds an AnimationCurve for that transform component. Default array length is 5; Reset() initializes each element with a new AnimationCurve and id matching the TransformCurveKey.

  • public float playbackDuration { get; set; }
    Playback duration of the sequence in seconds. Default is 30f. Used when ensuring loops to add keys at the end timestamp.

  • public bool loop { get; set; }
    Get/set loop behavior. Setting to true will call AfterModifications(rotationsChanged: true) to ensure the sequence loops correctly and rotations are patched. Backed by m_Loop.

  • public float timelineLength { get; }
    Computed length of the timeline based on the last key time in any transform curve or modifier curve. Returns the maximum last key time across transforms and modifiers (or 0 if none).

  • public int transformCount { get; }
    Number of keyframes across the transforms array (maximum length among transform curves). Useful to detect whether transforms exist.

Constructors

  • public CinematicCameraSequence()
    Initializes a new sequence and calls Reset() which clears modifiers and initializes the transforms[] entries (ids and empty AnimationCurves).

Methods

  • public void Reset()
    Clears modifiers and resets transforms to default CinematicCameraCurveModifier instances with ids set to TransformCurveKey names and new empty AnimationCurves.

  • public void RemoveModifier(string id)
    Removes the modifier with the given id from modifiers (if present).

  • public bool SampleTransform(IGameCameraController controller, float t, out Vector3 position, out Vector3 rotation)
    Samples transform curves at time t. If there are no transform keys (transformCount == 0) returns false and sets outputs to Vector3.zero. Otherwise:

  • Starts from controller.position/rotation as baseline,
  • Replaces components for which the corresponding transform curve has keys (PositionX/Y/Z => position.x/y/z; RotationX/RotationY => rotation.x/y),
  • Ensures rotation.z is set to 0f,
  • Returns true and the sampled position/rotation values. This method does not mutate the controller; it uses it only as a baseline.

  • public void RemoveCameraTransform(int curveIndex, int index)
    Removes a key at index from the transform curve at curveIndex. If that curve had only one key, it replaces the whole CinematicCameraCurveModifier at that index with an empty curve (and correct id). For multi-key curves it removes the key and calls AfterModifications(rotationsChanged: curveIndex == 4).

  • public void RemoveModifierKey(string id, int idx)
    Finds modifier by id and removes the key at idx if within range. If the modifier's curve becomes empty, removes the modifier entirely. Calls AfterModifications().

  • public int AddModifierKey(string id, float t, float value, float min, float max)
    Adds a key to an existing modifier with id, or creates a new modifier with an initial key if not found. When creating, sets min/max for the modifier. Calls AfterModifications() and returns the index of the added key (0 if newly created with single key).

  • public int AddModifierKey(string id, float t, float value)
    Same as above but without min/max; creates modifier if needed and calls AfterModifications().

  • public void Refresh(float t, IDictionary<string, PhotoModeProperty> properties, IGameCameraController controller)
    Applies modifier curves and transform sampling at time t:

  • For each modifier, if a matching PhotoModeProperty exists in properties, evaluate the modifier curve at t, clamp using the property's min/max callbacks if present, and set the property's value using its setter.
  • Sample transform with SampleTransform and, if successful, assign controller.rotation and controller.position with the sampled values. This integrates animation curves into live camera/controller state.

  • public int AddCameraTransform(float t, Vector3 position, Vector3 rotation)
    Adds keys to the 5 transform curves (position x/y/z, rotation x/y) at time t. Rotation.x is normalized so large (>90) values are converted into -270..+90 style range (rotation.x > 90 => rotation.x - 360). Calls AfterModifications(rotationsChanged: true). Returns the index returned by adding to PositionX curve (the primary returned key index).

  • public int MoveKeyframe(CinematicCameraCurveModifier modifier, int index, Keyframe keyframe)
    Moves/edits a keyframe in modifier.curve at index to the provided keyframe values:

  • If modifier.curve is null returns -1,
  • If modifier contains min/max (min != max) clamps keyframe.value into [min,max],
  • Sets keyframe.weightedMode = WeightedMode.Both,
  • If the key changed (time/value/tangents/weights), calls curve.MoveKey(index, keyframe) to move it,
  • Calls AfterModifications(modifier.id.StartsWith("Rotation")), returning the (possibly changed) index.

  • public void AfterModifications(bool rotationsChanged = false)
    Called after key edits that may affect looping or rotations. It calls EnsureLoop(); if EnsureLoop returns true or rotationsChanged is true, PatchRotations() is invoked.

  • private void PatchRotations()
    Adjusts yaw continuity for the RotationY transform (transforms[4]). For each key from second to last, computes the signed shortest difference relative to the previous key and moves the key so the values form a continuous path crossing the +/-180 boundary correctly. This prevents large spin jumps during interpolation.

  • private bool EnsureLoop()
    If loop == true, ensures all transform and modifier curves are looped by calling EnsureLoop(AnimationCurve) for each curve. Returns true if any modifications were applied to curves.

  • private bool EnsureLoop(AnimationCurve curve)
    For the provided curve, if not empty:

  • Evaluates start value at time 0,
  • Ensures there's a key at time 0 (if the first key time > 0.1) with that start value,
  • Ensures there's a key at playbackDuration with the same start value (if missing or different),
  • If changes were made returns true. This ensures smooth looping by matching end value to start value and adding endpoints where needed.

  • public void Write(IJsonWriter writer)
    Serializes this CinematicCameraSequence into JSON, writing "modifiers" and "transforms" arrays. Each CinematicCameraCurveModifier is written via its Write method.

  • public void Read(IJsonReader reader)
    Deserializes a CinematicCameraSequence from JSON (expects "modifiers" then "transforms"). Reconstructs modifiers list and transforms array by calling CinematicCameraCurveModifier.Read for each element.

  • private static void SupportValueTypesForAOT()
    Static helper that calls JSON.SupportTypeForAOT() for CinematicCameraSequence and CinematicCameraCurveModifier to ensure AOT runtime serializers are registered. Used to support AOT platforms.

Nested types: - public enum TransformCurveKey { PositionX, PositionY, PositionZ, RotationX, RotationY, Count }
Enumerates transform curve indices. transforms array index corresponds to these values.

  • public struct CinematicCameraCurveModifier : IJsonWritable, IJsonReadable
  • Properties:
    • public string id { get; set; } — identifier used to match to PhotoMode properties or transform ids.
    • public AnimationCurve curve { get; set; } — animation curve for the modifier/transform.
    • public float min { get; set; } and public float max { get; set; } — optional clamping range used by MoveKeyframe and when creating the modifier with min/max.
  • Methods:
    • public void Write(IJsonWriter writer) — writes id, curve, min, max.
    • public void Read(IJsonReader reader) — reads those fields back.
    • public int AddKey(float t, float value) — lazily creates an AnimationCurve if curve == null, then adds the key and returns the index.

Notes and behavior details: - The transforms array contains 5 entries in fixed order. Many operations assume that ordering (0..4). - Rotation handling is special: an explicit PatchRotations step is applied so yaw (RotationY) keys remain continuous and avoid sudden 360-degree transitions. AfterModifications(rotationsChanged: true) will run PatchRotations. - Looping adds keys at time 0 and playbackDuration to match start value; EnsureLoop enforces that if loop == true. - Serialization supports reading/writing modifiers and transforms arrays; CinematicCameraCurveModifier handles curve (de)serialization. - The Refresh method integrates modifier curves with PhotoModeProperty instances (assumed type has .min/.max callbacks and setValue) and applies the sampled transform directly to the IGameCameraController.

Usage Example

// Example usage of CinematicCameraSequence (pseudocode for IGameCameraController and PhotoModeProperty)

CinematicCameraSequence seq = new CinematicCameraSequence();
seq.playbackDuration = 10f;

// Add two camera transform keyframes (time, position, rotation)
// Rotation.x is pitch, Rotation.y is yaw (rotation.z is forced to 0)
seq.AddCameraTransform(0f, new Vector3(0f, 5f, -10f), new Vector3(10f, 0f, 0f));
seq.AddCameraTransform(5f, new Vector3(0f, 10f, -20f), new Vector3(15f, 90f, 0f));

// Add a modifier curve for "Exposure" (example property id)
seq.AddModifierKey("Exposure", 0f, 1f);
seq.AddModifierKey("Exposure", 5f, 2f);

// Enable looping to automatically duplicate endpoints and patch rotations
seq.loop = true;

// Sampling at runtime: assume controller implements IGameCameraController with position/rotation properties
IGameCameraController controller = GetActiveCameraController(); // game-provided
float t = 3.2f; // time in seconds

// Apply modifiers and transforms (Refresh will set controller.position and controller.rotation)
var properties = new Dictionary<string, PhotoModeProperty>(); // populate with your photo-mode properties
seq.Refresh(t, properties, controller);

// Or sample only the transform
if (seq.SampleTransform(controller, t, out Vector3 sampledPos, out Vector3 sampledRot))
{
    controller.position = sampledPos;
    controller.rotation = sampledRot;
}

Notes: - When constructing modifiers for real PhotoMode properties, ensure the property id matches the modifier.id so Refresh can find and set values. - Be mindful that RotationX normalization when adding camera transforms maps >90 pitch values into expected signed range to avoid wrapping issues at edit time.