#if USE_TIMELINE
#if UNITY_2017_1_OR_NEWER
// Copyright (c) Pixel Crushers. All rights reserved.
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using System.Collections;
namespace PixelCrushers.DialogueSystem.SequencerCommands
{
///
/// Sequencer command Timeline(mode, timeline, [nowait], [nostop], [#:binding]...)
/// or Timeline(speed, timeline, value)
///
/// Controls a Timeline (PlayableDirector).
///
/// - mode: Can be play, pause, resume, stop, or speed. If speed, the third parameter should be the new speed.
/// - timeline: Name of a GameObject with a PlayableDirector, or a Timeline asset in a Resources folder or asset bundle. Default: speaker.
/// - nowait: If specified, doesn't wait for the director to finish.
/// - nostop: If specified, doesn't force the director to stop at the end of the sequencer command.
/// - #:binding: If specified, the number indicates the track number, starting from zero. The binding is the name of a GameObject to bind to that track.
///
public class SequencerCommandTimeline : SequencerCommand
{
private PlayableDirector playableDirector = null;
private TimelineAsset timelineAsset = null;
private string mode;
private Transform subject;
private bool nostop = false;
private bool nowait = false;
private bool mustDestroyPlayableDirector = false;
private bool mustDestroyPlayableAsset = false;
public IEnumerator Start()
{
if (parameters == null || parameters.Length == 0)
{
Stop();
yield break;
}
mode = GetParameter(0).ToLower();
subject = GetSubject(1, Sequencer.Speaker);
nowait = string.Equals(GetParameter(2), "nowait", System.StringComparison.OrdinalIgnoreCase) ||
string.Equals(GetParameter(3), "nowait", System.StringComparison.OrdinalIgnoreCase);
nostop = string.Equals(GetParameter(2), "nostop", System.StringComparison.OrdinalIgnoreCase) ||
string.Equals(GetParameter(3), "nostop", System.StringComparison.OrdinalIgnoreCase);
playableDirector = (subject != null) ? subject.GetComponent() : null;
// If no suitable PlayableDirector was found, look for a Timeline asset in Resources:
if (playableDirector == null || playableDirector.playableAsset == null)
{
DialogueManager.LoadAsset(GetParameter(1), typeof(TimelineAsset),
(asset) =>
{
timelineAsset = asset as TimelineAsset;
if (timelineAsset != null)
{
if (playableDirector == null)
{
playableDirector = new GameObject(GetParameter(1), typeof(PlayableDirector)).GetComponent();
mustDestroyPlayableDirector = true;
}
playableDirector.playableAsset = timelineAsset;
mustDestroyPlayableAsset = true;
}
StartCoroutine(Proceed());
});
}
else
{
yield return Proceed();
}
}
private IEnumerator Proceed()
{
if (playableDirector == null)
{
if (DialogueDebug.LogWarnings) Debug.LogWarning("Dialogue System: Sequencer: Timeline(" + GetParameters() +
"): Can't find playable director '" + GetParameter(1) + "'.");
}
else if (playableDirector.playableAsset == null)
{
if (DialogueDebug.LogWarnings) Debug.LogWarning("Dialogue System: Sequencer: Timeline(" + GetParameters() +
"): No timeline asset is assigned to the Playable Director.", playableDirector);
}
else
{
var isModeValid = (mode == "play" || mode == "pause" || mode == "resume" || mode == "stop" || mode == "speed");
if (!isModeValid)
{
if (DialogueDebug.LogWarnings) Debug.LogWarning("Dialogue System: Sequencer: Timeline(" + GetParameters() +
"): Invalid mode '" + mode + "'. Expected 'play', 'pause', 'resume', or 'stop'.");
}
else
{
if (DialogueDebug.LogInfo) Debug.Log("Dialogue System: Sequencer: Timeline(" + GetParameters() + ")");
// Check for rebindings:
timelineAsset = playableDirector.playableAsset as TimelineAsset;
if (timelineAsset != null)
{
for (int i = 2; i < Parameters.Length; i++)
{
var s = GetParameter(i);
if (s.Contains(":"))
{
var colonPos = s.IndexOf(":");
var trackIndex = Tools.StringToInt(s.Substring(0, colonPos));
var bindName = s.Substring(colonPos + 1);
var track = timelineAsset.GetOutputTrack(trackIndex);
if (track != null)
{
playableDirector.SetGenericBinding(track, Tools.GameObjectHardFind(bindName));
}
}
}
}
switch (mode)
{
case "play":
playableDirector.Play();
var endTime = nowait ? 0 : DialogueTime.time + playableDirector.playableAsset.duration;
while (DialogueTime.time < endTime || playableDirector.extrapolationMode == DirectorWrapMode.Loop)
{
yield return null;
}
break;
case "pause":
playableDirector.Pause();
nostop = true;
break;
case "resume":
playableDirector.Resume();
var resumedEndTime = nowait ? 0 : DialogueTime.time + playableDirector.playableAsset.duration - playableDirector.time;
while (DialogueTime.time < resumedEndTime || playableDirector.extrapolationMode == DirectorWrapMode.Loop)
{
yield return null;
}
break;
case "stop":
playableDirector.Stop();
nostop = false;
break;
case "speed":
playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(GetParameterAsFloat(2));
nostop = true;
break;
default:
isModeValid = false;
break;
}
}
}
Stop();
}
public void OnDestroy()
{
if (playableDirector != null && !nostop)
{
playableDirector.Stop();
if (mustDestroyPlayableAsset)
{
DialogueManager.UnloadAsset(playableDirector.playableAsset);
playableDirector.playableAsset = null;
}
if (mustDestroyPlayableDirector) Destroy(playableDirector.gameObject);
}
}
}
}
#endif
#endif