Add example Unity Project

This commit is contained in:
Michał Gdula 2023-04-26 01:55:33 +01:00
parent fda7ff28dd
commit e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions

View file

@ -0,0 +1,140 @@
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// Playable that controls the active state of a GameObject.
/// </summary>
public class ActivationControlPlayable : PlayableBehaviour
{
/// <summary>
/// The state of a GameObject's activeness when a PlayableGraph stops.
/// </summary>
public enum PostPlaybackState
{
/// <summary>
/// Set the GameObject to active when the PlayableGraph stops.
/// </summary>
Active,
/// <summary>
/// Set the GameObject to inactive when the [[PlayableGraph]] stops.
/// </summary>
Inactive,
/// <summary>
/// Revert the GameObject to the active state it was before the [[PlayableGraph]] started.
/// </summary>
Revert
}
enum InitialState
{
Unset,
Active,
Inactive
}
public GameObject gameObject = null;
public PostPlaybackState postPlayback = PostPlaybackState.Revert;
InitialState m_InitialState;
/// <summary>
/// Creates a ScriptPlayable with an ActivationControlPlayable behaviour attached
/// </summary>
/// <param name="graph">PlayableGraph that will own the playable</param>
/// <param name="gameObject">The GameObject that triggered the graph build</param>
/// <param name="postPlaybackState">The state to leave the gameObject after the graph is stopped</param>
/// <returns>Returns a playable that controls activation of a game object</returns>
public static ScriptPlayable<ActivationControlPlayable> Create(PlayableGraph graph, GameObject gameObject, ActivationControlPlayable.PostPlaybackState postPlaybackState)
{
if (gameObject == null)
return ScriptPlayable<ActivationControlPlayable>.Null;
var handle = ScriptPlayable<ActivationControlPlayable>.Create(graph);
var playable = handle.GetBehaviour();
playable.gameObject = gameObject;
playable.postPlayback = postPlaybackState;
return handle;
}
/// <summary>
/// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">The information about this frame</param>
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
if (gameObject == null)
return;
gameObject.SetActive(true);
}
/// <summary>
/// This function is called when the Playable play state is changed to PlayState.Paused.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">The information about this frame</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
// OnBehaviourPause can be called if the graph is stopped for a variety of reasons
// the effectivePlayState will test if the pause is due to the clip being out of bounds
if (gameObject != null && info.effectivePlayState == PlayState.Paused)
{
gameObject.SetActive(false);
}
}
/// <summary>
/// This function is called during the ProcessFrame phase of the PlayableGraph.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
/// <param name="userData">unused</param>
public override void ProcessFrame(Playable playable, FrameData info, object userData)
{
if (gameObject != null)// && !gameObject.activeSelf)
gameObject.SetActive(true);
}
/// <summary>
/// This function is called when the PlayableGraph that owns this PlayableBehaviour starts.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
public override void OnGraphStart(Playable playable)
{
if (gameObject != null)
{
if (m_InitialState == InitialState.Unset)
m_InitialState = gameObject.activeSelf ? InitialState.Active : InitialState.Inactive;
}
}
/// <summary>
/// This function is called when the Playable that owns the PlayableBehaviour is destroyed.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
public override void OnPlayableDestroy(Playable playable)
{
if (gameObject == null || m_InitialState == InitialState.Unset)
return;
switch (postPlayback)
{
case PostPlaybackState.Active:
gameObject.SetActive(true);
break;
case PostPlaybackState.Inactive:
gameObject.SetActive(false);
break;
case PostPlaybackState.Revert:
gameObject.SetActive(m_InitialState == InitialState.Active);
break;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d20e4e177b86a2843805dd3894f41b42
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// This class is deprecated. It is recommended to use Playable Asset and Playable Behaviour derived classes instead.
/// </summary>
[Serializable]
[Obsolete("For best performance use PlayableAsset and PlayableBehaviour.")]
public class BasicPlayableBehaviour : ScriptableObject, IPlayableAsset, IPlayableBehaviour
{
public BasicPlayableBehaviour() {}
/// <summary>
/// The playback duration in seconds of the instantiated Playable.
/// </summary>
public virtual double duration { get { return PlayableBinding.DefaultDuration; } }
/// <summary>
///A description of the outputs of the instantiated Playable.
/// </summary>
public virtual IEnumerable<PlayableBinding> outputs { get { return PlayableBinding.None; } }
/// <summary>
/// <para>This function is called when the PlayableGraph that owns this PlayableBehaviour starts.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
public virtual void OnGraphStart(Playable playable) {}
/// <summary>
/// <para>This function is called when the PlayableGraph that owns this PlayableBehaviour stops.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
public virtual void OnGraphStop(Playable playable) {}
/// <summary>
/// <para>This function is called when the Playable that owns the PlayableBehaviour is created.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
public virtual void OnPlayableCreate(Playable playable) {}
/// <summary>
/// <para>This function is called when the Playable that owns the PlayableBehaviour is destroyed.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
public virtual void OnPlayableDestroy(Playable playable) {}
/// <summary>
/// <para>This function is called when the Playable play state is changed to Playables.PlayState.Playing.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public virtual void OnBehaviourPlay(Playable playable, FrameData info) {}
/// <summary>
/// <para>This function is called when the Playable play state is changed to Playables.PlayState.Paused.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public virtual void OnBehaviourPause(Playable playable, FrameData info) {}
/// <summary>
/// <para>This function is called during the PrepareFrame phase of the PlayableGraph.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public virtual void PrepareFrame(Playable playable, FrameData info) {}
/// <summary>
/// <para>This function is called during the ProcessFrame phase of the PlayableGraph.</para>
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
/// <param name="playerData">The user data of the ScriptPlayableOutput that initiated the process pass.</param>
public virtual void ProcessFrame(Playable playable, FrameData info, object playerData) {}
/// <summary>
/// Implement this method to have your asset inject playables into the given graph.
/// </summary>
/// <param name="graph">The graph to inject playables into.</param>
/// <param name="owner">The game object which initiated the build.</param>
/// <returns>The playable injected into the graph, or the root playable if multiple playables are injected.</returns>
public virtual Playable CreatePlayable(PlayableGraph graph, GameObject owner)
{
return ScriptPlayable<BasicPlayableBehaviour>.Create(graph, this);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fe03a7b0ba57a4d488b6c327ae16c335
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,206 @@
using System;
using UnityEngine;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// Playable Behaviour used to control a PlayableDirector.
/// </summary>
/// <remarks>
/// This playable is used to control other PlayableDirector components from a Timeline sequence.
/// </remarks>
public class DirectorControlPlayable : PlayableBehaviour
{
/// <summary>
/// The PlayableDirector being controlled by this PlayableBehaviour
/// </summary>
public PlayableDirector director;
private bool m_SyncTime = false;
private double m_AssetDuration = double.MaxValue;
/// <summary>
/// Creates a Playable with a DirectorControlPlayable attached
/// </summary>
/// <param name="graph">The graph to inject the playable into</param>
/// <param name="director">The director to control</param>
/// <returns>Returns a Playable with a DirectorControlPlayable attached</returns>
public static ScriptPlayable<DirectorControlPlayable> Create(PlayableGraph graph, PlayableDirector director)
{
if (director == null)
return ScriptPlayable<DirectorControlPlayable>.Null;
var handle = ScriptPlayable<DirectorControlPlayable>.Create(graph);
handle.GetBehaviour().director = director;
#if UNITY_EDITOR
if (!Application.isPlaying && UnityEditor.PrefabUtility.IsPartOfPrefabInstance(director))
UnityEditor.PrefabUtility.prefabInstanceUpdated += handle.GetBehaviour().OnPrefabUpdated;
#endif
return handle;
}
public override void OnPlayableDestroy(Playable playable)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated;
#endif
if (director != null && director.playableAsset != null)
director.Stop();
}
/// <summary>
/// This function is called during the PrepareFrame phase of the PlayableGraph.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void PrepareFrame(Playable playable, FrameData info)
{
if (director == null || !director.isActiveAndEnabled || director.playableAsset == null)
return;
// resync the time on an evaluate or a time jump (caused by loops, or some setTime calls)
m_SyncTime |= (info.evaluationType == FrameData.EvaluationType.Evaluate) ||
DetectDiscontinuity(playable, info);
SyncSpeed(info.effectiveSpeed);
SyncPlayState(playable.GetGraph(), playable.GetTime());
}
/// <summary>
/// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
m_SyncTime = true;
if (director != null && director.playableAsset != null)
m_AssetDuration = director.playableAsset.duration;
}
/// <summary>
/// This function is called when the Playable play state is changed to PlayState.Paused.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
if (director != null && director.playableAsset != null)
{
if (info.effectivePlayState == PlayState.Playing) // graph was paused
director.Pause();
else
director.Stop();
}
}
/// <summary>
/// This function is called during the ProcessFrame phase of the PlayableGraph.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
/// <param name="playerData">unused</param>
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
if (director == null || !director.isActiveAndEnabled || director.playableAsset == null)
return;
if (m_SyncTime || DetectOutOfSync(playable))
{
UpdateTime(playable);
director.Evaluate();
}
m_SyncTime = false;
}
#if UNITY_EDITOR
void OnPrefabUpdated(GameObject go)
{
// When the prefab asset is updated, we rebuild the graph to reflect the changes in editor
if (UnityEditor.PrefabUtility.GetRootGameObject(director) == go)
director.RebuildGraph();
}
#endif
void SyncSpeed(double speed)
{
if (director.playableGraph.IsValid())
{
int roots = director.playableGraph.GetRootPlayableCount();
for (int i = 0; i < roots; i++)
{
var rootPlayable = director.playableGraph.GetRootPlayable(i);
if (rootPlayable.IsValid())
{
rootPlayable.SetSpeed(speed);
}
}
}
}
void SyncPlayState(PlayableGraph graph, double playableTime)
{
bool expectedFinished = (playableTime >= m_AssetDuration) && director.extrapolationMode == DirectorWrapMode.None;
if (graph.IsPlaying() && !expectedFinished)
director.Play();
else
director.Pause();
}
bool DetectDiscontinuity(Playable playable, FrameData info)
{
return Math.Abs(playable.GetTime() - playable.GetPreviousTime() - info.m_DeltaTime * info.m_EffectiveSpeed) > DiscreteTime.tickValue;
}
bool DetectOutOfSync(Playable playable)
{
double expectedTime = playable.GetTime();
if (playable.GetTime() >= m_AssetDuration)
{
if (director.extrapolationMode == DirectorWrapMode.None)
return false;
else if (director.extrapolationMode == DirectorWrapMode.Hold)
expectedTime = m_AssetDuration;
else if (m_AssetDuration > float.Epsilon) // loop
expectedTime = expectedTime % m_AssetDuration;
}
if (!Mathf.Approximately((float)expectedTime, (float)director.time))
{
#if UNITY_EDITOR
double lastDelta = playable.GetTime() - playable.GetPreviousTime();
if (UnityEditor.Unsupported.IsDeveloperBuild())
Debug.LogWarningFormat("Internal Warning - Control track desync detected on {2} ({0:F10} vs {1:F10} with delta {3:F10}). Time will be resynchronized. Known to happen with nested control tracks", playable.GetTime(), director.time, director.name, lastDelta);
#endif
return true;
}
return false;
}
// We need to handle loop modes explicitly since we are setting the time directly
void UpdateTime(Playable playable)
{
double duration = Math.Max(0.1, director.playableAsset.duration);
switch (director.extrapolationMode)
{
case DirectorWrapMode.Hold:
director.time = Math.Min(duration, Math.Max(0, playable.GetTime()));
break;
case DirectorWrapMode.Loop:
director.time = Math.Max(0, playable.GetTime() % duration);
break;
case DirectorWrapMode.None:
director.time = playable.GetTime();
break;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be156cc527d606b4aaac403e9843186e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
namespace UnityEngine.Timeline
{
/// <summary>
/// Interface that can be implemented by MonoBehaviours indicating that they receive time-related control calls from a PlayableGraph.
/// </summary>
/// <remarks>
/// Implementing this interface on MonoBehaviours attached to GameObjects under control by control-tracks will cause them to be notified when associated Timeline clips are active.
/// </remarks>
public interface ITimeControl
{
/// <summary>
/// Called each frame the Timeline clip is active.
/// </summary>
/// <param name="time">The local time of the associated Timeline clip.</param>
void SetTime(double time);
/// <summary>
/// Called when the associated Timeline clip becomes active.
/// </summary>
void OnControlTimeStart();
/// <summary>
/// Called when the associated Timeline clip becomes deactivated.
/// </summary>
void OnControlTimeStop();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5415c904c4fbc3e498253bc2866b37cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System;
namespace UnityEngine.Timeline
{
/// <summary>
/// Use these flags to specify the notification behaviour.
/// </summary>
/// <see cref="UnityEngine.Playables.INotification"/>
[Flags]
[Serializable]
public enum NotificationFlags : short
{
/// <summary>
/// Use this flag to send the notification in Edit Mode.
/// </summary>
/// <remarks>
/// Sent on discontinuous jumps in time.
/// </remarks>
TriggerInEditMode = 1 << 0,
/// <summary>
/// Use this flag to send the notification if playback starts after the notification time.
/// </summary>
Retroactive = 1 << 1,
/// <summary>
/// Use this flag to send the notification only once when looping.
/// </summary>
TriggerOnce = 1 << 2,
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 983c76d87fb6f4f4597a526a4b2b5fd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,177 @@
using System;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// Playable that synchronizes a particle system simulation.
/// </summary>
public class ParticleControlPlayable : PlayableBehaviour
{
const float kUnsetTime = -1;
float m_LastTime = kUnsetTime;
uint m_RandomSeed = 1;
// particleSystem.time can not be relied on for an accurate time. It does not advance until a delta threshold is reached(fixedUpdate) and until the start delay has elapsed.
float m_SystemTime;
/// <summary>
/// Creates a Playable with a ParticleControlPlayable behaviour attached
/// </summary>
/// <param name="graph">The PlayableGraph to inject the Playable into.</param>
/// <param name="component">The particle systtem to control</param>
/// <param name="randomSeed">A random seed to use for particle simulation</param>
/// <returns>Returns the created Playable.</returns>
public static ScriptPlayable<ParticleControlPlayable> Create(PlayableGraph graph, ParticleSystem component, uint randomSeed)
{
if (component == null)
return ScriptPlayable<ParticleControlPlayable>.Null;
var handle = ScriptPlayable<ParticleControlPlayable>.Create(graph);
handle.GetBehaviour().Initialize(component, randomSeed);
return handle;
}
/// <summary>
/// The particle system to control
/// </summary>
public ParticleSystem particleSystem { get; private set; }
/// <summary>
/// Initializes the behaviour with a particle system and random seed.
/// </summary>
/// <param name="ps"></param>
/// <param name="randomSeed"></param>
public void Initialize(ParticleSystem ps, uint randomSeed)
{
m_RandomSeed = Math.Max(1, randomSeed);
particleSystem = ps;
m_SystemTime = 0;
SetRandomSeed();
#if UNITY_EDITOR
if (!Application.isPlaying && UnityEditor.PrefabUtility.IsPartOfPrefabInstance(ps))
UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated;
#endif
}
#if UNITY_EDITOR
/// <summary>
/// This function is called when the Playable that owns the PlayableBehaviour is destroyed.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
public override void OnPlayableDestroy(Playable playable)
{
if (!Application.isPlaying)
UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated;
}
void OnPrefabUpdated(GameObject go)
{
// When the instance is updated from, this will cause the next evaluate to resimulate.
if (UnityEditor.PrefabUtility.GetRootGameObject(particleSystem) == go)
m_LastTime = kUnsetTime;
}
#endif
void SetRandomSeed()
{
particleSystem.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
var systems = particleSystem.gameObject.GetComponentsInChildren<ParticleSystem>();
uint seed = m_RandomSeed;
foreach (var ps in systems)
{
// don't overwrite user set random seeds
if (ps.useAutoRandomSeed)
{
ps.useAutoRandomSeed = false;
ps.randomSeed = seed;
seed++;
}
}
}
/// <summary>
/// This function is called during the PrepareFrame phase of the PlayableGraph.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="data">A FrameData structure that contains information about the current frame context.</param>
public override void PrepareFrame(Playable playable, FrameData data)
{
if (particleSystem == null || !particleSystem.gameObject.activeInHierarchy)
return;
float localTime = (float)playable.GetTime();
bool shouldUpdate = Mathf.Approximately(m_LastTime, kUnsetTime) ||
!Mathf.Approximately(m_LastTime, localTime);
if (shouldUpdate)
{
float epsilon = Time.fixedDeltaTime * 0.5f;
float simTime = localTime;
float expectedDelta = simTime - m_LastTime;
// The first iteration includes the start delay. Evaluate(particleSystem.randomSeed) is how the particle system generates the random value internally.
float startDelay = particleSystem.main.startDelay.Evaluate(particleSystem.randomSeed);
float particleSystemDurationLoop0 = particleSystem.main.duration + startDelay;
// The particle system time does not include the start delay so we need to remove this for our own system time.
float expectedSystemTime = simTime > particleSystemDurationLoop0 ? m_SystemTime : m_SystemTime - startDelay;
// if it's not looping, then the system time won't advance past the end of the duration
if (!particleSystem.main.loop)
expectedSystemTime = Math.Min(expectedSystemTime, particleSystem.main.duration);
// conditions for restart
bool restart = (simTime < m_LastTime) || // time went backwards
(simTime < epsilon) || // time is set to 0
Mathf.Approximately(m_LastTime, kUnsetTime) || // object disabled
(expectedDelta > particleSystem.main.duration) || // large jump (bug workaround)
!(Mathf.Abs(expectedSystemTime - particleSystem.time) < Time.maximumParticleDeltaTime); // particle system isn't where we left it
if (restart)
{
// work around for a bug where simulate(simTime, true, true) doesn't work on loops
particleSystem.Simulate(0, true, true);
particleSystem.Simulate(simTime, true, false);
m_SystemTime = simTime;
}
else
{
// ps.time will wrap, so we need to account for that in computing delta time
float particleSystemDuration = simTime > particleSystemDurationLoop0 ? particleSystem.main.duration : particleSystemDurationLoop0;
float fracTime = simTime % particleSystemDuration;
float deltaTime = fracTime - m_SystemTime;
if (deltaTime < -epsilon) // detect wrapping of ps.time
deltaTime = fracTime + particleSystemDurationLoop0 - m_SystemTime;
particleSystem.Simulate(deltaTime, true, false);
m_SystemTime += deltaTime;
}
m_LastTime = localTime;
}
}
/// <summary>
/// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
m_LastTime = kUnsetTime;
}
/// <summary>
/// This function is called when the Playable play state is changed to PlayState.Paused.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
m_LastTime = kUnsetTime;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f603edd7163537f44927ad2808147a25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,158 @@
using System;
using UnityEngine.Playables;
using UnityEngine.SceneManagement;
namespace UnityEngine.Timeline
{
/// <summary>
/// Playable that controls and instantiates a Prefab.
/// </summary>
public class PrefabControlPlayable : PlayableBehaviour
{
GameObject m_Instance;
#if UNITY_EDITOR
private bool m_IsActiveCached;
#endif
/// <summary>
/// Creates a Playable with a PrefabControlPlayable behaviour attached
/// </summary>
/// <param name="graph">The PlayableGraph to inject the Playable into.</param>
/// <param name="prefabGameObject">The prefab to instantiate from</param>
/// <param name="parentTransform">Transform to parent instance to. Can be null.</param>
/// <returns>Returns a Playabe with PrefabControlPlayable behaviour attached.</returns>
public static ScriptPlayable<PrefabControlPlayable> Create(PlayableGraph graph, GameObject prefabGameObject, Transform parentTransform)
{
if (prefabGameObject == null)
return ScriptPlayable<PrefabControlPlayable>.Null;
var handle = ScriptPlayable<PrefabControlPlayable>.Create(graph);
handle.GetBehaviour().Initialize(prefabGameObject, parentTransform);
return handle;
}
/// <summary>
/// The instance of the prefab created by this behaviour
/// </summary>
public GameObject prefabInstance
{
get { return m_Instance; }
}
/// <summary>
/// Initializes the behaviour with a prefab and parent transform
/// </summary>
/// <param name="prefabGameObject">The prefab to instantiate from</param>
/// <param name="parentTransform">Transform to parent instance to. Can be null.</param>
/// <returns>The created instance</returns>
public GameObject Initialize(GameObject prefabGameObject, Transform parentTransform)
{
if (prefabGameObject == null)
throw new ArgumentNullException("Prefab cannot be null");
if (m_Instance != null)
{
Debug.LogWarningFormat("Prefab Control Playable ({0}) has already been initialized with a Prefab ({1}).", prefabGameObject.name, m_Instance.name);
}
else
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
m_Instance = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(prefabGameObject, parentTransform);
UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated;
}
else
#endif
{
m_Instance = Object.Instantiate(prefabGameObject, parentTransform, false);
}
m_Instance.name = prefabGameObject.name + " [Timeline]";
m_Instance.SetActive(false);
SetHideFlagsRecursive(m_Instance);
}
return m_Instance;
}
/// <summary>
/// This function is called when the Playable that owns the PlayableBehaviour is destroyed.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
public override void OnPlayableDestroy(Playable playable)
{
if (m_Instance)
{
if (Application.isPlaying)
Object.Destroy(m_Instance);
else
Object.DestroyImmediate(m_Instance);
}
#if UNITY_EDITOR
UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated;
#endif
}
/// <summary>
/// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
if (m_Instance == null)
return;
m_Instance.SetActive(true);
#if UNITY_EDITOR
m_IsActiveCached = true;
#endif
}
/// <summary>
/// This function is called when the Playable play state is changed to PlayState.Paused.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
// OnBehaviourPause can be called if the graph is stopped for a variety of reasons
// the effectivePlayState will test if the pause is due to the clip being out of bounds
if (m_Instance != null && info.effectivePlayState == PlayState.Paused)
{
m_Instance.SetActive(false);
#if UNITY_EDITOR
m_IsActiveCached = false;
#endif
}
}
#if UNITY_EDITOR
void OnPrefabUpdated(GameObject go)
{
if (go == m_Instance)
{
SetHideFlagsRecursive(go);
go.SetActive(m_IsActiveCached);
}
}
#endif
static void SetHideFlagsRecursive(GameObject gameObject)
{
if (gameObject == null)
return;
gameObject.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor;
if (!Application.isPlaying)
gameObject.hideFlags |= HideFlags.HideInHierarchy;
foreach (Transform child in gameObject.transform)
{
SetHideFlagsRecursive(child.gameObject);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 439c018cf4619e94d9a92110ce0aa188
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,85 @@
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// A PlayableBehaviour that manages a component that implements the ITimeControl interface
/// </summary>
public class TimeControlPlayable : PlayableBehaviour
{
ITimeControl m_timeControl;
bool m_started;
/// <summary>
/// Creates a Playable with a TimeControlPlayable behaviour attached
/// </summary>
/// <param name="graph">The PlayableGraph to inject the Playable into.</param>
/// <param name="timeControl"></param>
/// <returns></returns>
public static ScriptPlayable<TimeControlPlayable> Create(PlayableGraph graph, ITimeControl timeControl)
{
if (timeControl == null)
return ScriptPlayable<TimeControlPlayable>.Null;
var handle = ScriptPlayable<TimeControlPlayable>.Create(graph);
handle.GetBehaviour().Initialize(timeControl);
return handle;
}
/// <summary>
/// Initializes the behaviour
/// </summary>
/// <param name="timeControl">Component that implements the ITimeControl interface</param>
public void Initialize(ITimeControl timeControl)
{
m_timeControl = timeControl;
}
/// <summary>
/// This function is called during the PrepareFrame phase of the PlayableGraph.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void PrepareFrame(Playable playable, FrameData info)
{
Debug.Assert(m_started, "PrepareFrame has been called without OnControlTimeStart being called first.");
if (m_timeControl != null)
m_timeControl.SetTime(playable.GetTime());
}
/// <summary>
/// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
/// </summary>
/// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPlay(Playable playable, FrameData info)
{
if (m_timeControl == null)
return;
if (!m_started)
{
m_timeControl.OnControlTimeStart();
m_started = true;
}
}
/// <summary>
/// This function is called when the Playable play state is changed to PlayState.Paused.
/// </summary>
/// <param name="playable">The playable this behaviour is attached to.</param>
/// <param name="info">A FrameData structure that contains information about the current frame context.</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
if (m_timeControl == null)
return;
if (m_started)
{
m_timeControl.OnControlTimeStop();
m_started = false;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1db879070d9a45f4c86cdf5e59616df5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using UnityEngine.Playables;
namespace UnityEngine.Timeline
{
/// <summary>
/// Use this PlayableBehaviour to send notifications at a given time.
/// </summary>
/// <seealso cref="UnityEngine.Timeline.NotificationFlags"/>
public class TimeNotificationBehaviour : PlayableBehaviour
{
struct NotificationEntry
{
public double time;
public INotification payload;
public bool notificationFired;
public NotificationFlags flags;
public bool triggerInEditor
{
get { return (flags & NotificationFlags.TriggerInEditMode) != 0; }
}
public bool prewarm
{
get { return (flags & NotificationFlags.Retroactive) != 0; }
}
public bool triggerOnce
{
get { return (flags & NotificationFlags.TriggerOnce) != 0; }
}
}
readonly List<NotificationEntry> m_Notifications = new List<NotificationEntry>();
double m_PreviousTime;
bool m_NeedSortNotifications;
Playable m_TimeSource;
/// <summary>
/// Sets an optional Playable that provides duration and Wrap mode information.
/// </summary>
/// <remarks>
/// timeSource is optional. By default, the duration and Wrap mode will come from the current Playable.
/// </remarks>
public Playable timeSource
{
set { m_TimeSource = value; }
}
/// <summary>
/// Creates and initializes a ScriptPlayable with a TimeNotificationBehaviour.
/// </summary>
/// <param name="graph">The playable graph.</param>
/// <param name="duration">The duration of the playable.</param>
/// <param name="loopMode">The loop mode of the playable.</param>
/// <returns>A new TimeNotificationBehaviour linked to the PlayableGraph.</returns>
public static ScriptPlayable<TimeNotificationBehaviour> Create(PlayableGraph graph, double duration, DirectorWrapMode loopMode)
{
var notificationsPlayable = ScriptPlayable<TimeNotificationBehaviour>.Create(graph);
notificationsPlayable.SetDuration(duration);
notificationsPlayable.SetTimeWrapMode(loopMode);
notificationsPlayable.SetPropagateSetTime(true);
return notificationsPlayable;
}
/// <summary>
/// Adds a notification to be sent with flags, at a specific time.
/// </summary>
/// <param name="time">The time to send the notification.</param>
/// <param name="payload">The notification.</param>
/// <param name="flags">The notification flags that determine the notification behaviour. This parameter is set to Retroactive by default.</param>
/// <seealso cref="UnityEngine.Timeline.NotificationFlags"/>
public void AddNotification(double time, INotification payload, NotificationFlags flags = NotificationFlags.Retroactive)
{
m_Notifications.Add(new NotificationEntry
{
time = time,
payload = payload,
flags = flags
});
m_NeedSortNotifications = true;
}
/// <summary>
/// This method is called when the PlayableGraph that owns this PlayableBehaviour starts.
/// </summary>
/// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param>
public override void OnGraphStart(Playable playable)
{
SortNotifications();
for (var i = 0; i < m_Notifications.Count; i++)
{
var notification = m_Notifications[i];
notification.notificationFired = false;
m_Notifications[i] = notification;
}
m_PreviousTime = playable.GetTime();
}
/// <summary>
/// This method is called when the Playable play state is changed to PlayState.Paused
/// </summary>
/// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param>
/// <param name="info">Playable context information such as weight, evaluationType, and so on.</param>
public override void OnBehaviourPause(Playable playable, FrameData info)
{
if (playable.IsDone())
{
SortNotifications();
for (var i = 0; i < m_Notifications.Count; i++)
{
var e = m_Notifications[i];
if (!e.notificationFired)
{
var duration = playable.GetDuration();
var canTrigger = m_PreviousTime <= e.time && e.time <= duration;
if (canTrigger)
{
Trigger_internal(playable, info.output, ref e);
m_Notifications[i] = e;
}
}
}
}
}
/// <summary>
/// This method is called during the PrepareFrame phase of the PlayableGraph.
/// </summary>
/// <remarks>
/// Called once before processing starts.
/// </remarks>
/// <param name="playable">The reference to the playable associated with this PlayableBehaviour.</param>
/// <param name="info">Playable context information such as weight, evaluationType, and so on.</param>
public override void PrepareFrame(Playable playable, FrameData info)
{
// Never trigger on scrub
if (info.evaluationType == FrameData.EvaluationType.Evaluate)
{
return;
}
SyncDurationWithExternalSource(playable);
SortNotifications();
var currentTime = playable.GetTime();
// Fire notifications from previousTime till the end
if (info.timeLooped)
{
var duration = playable.GetDuration();
TriggerNotificationsInRange(m_PreviousTime, duration, info, playable, true);
var dx = playable.GetDuration() - m_PreviousTime;
var nFullTimelines = (int)((info.deltaTime * info.effectiveSpeed - dx) / playable.GetDuration());
for (var i = 0; i < nFullTimelines; i++)
{
TriggerNotificationsInRange(0, duration, info, playable, false);
}
TriggerNotificationsInRange(0, currentTime, info, playable, false);
}
else
{
var pt = playable.GetTime();
TriggerNotificationsInRange(m_PreviousTime, pt, info,
playable, true);
}
for (var i = 0; i < m_Notifications.Count; ++i)
{
var e = m_Notifications[i];
if (e.notificationFired && CanRestoreNotification(e, info, currentTime, m_PreviousTime))
{
Restore_internal(ref e);
m_Notifications[i] = e;
}
}
m_PreviousTime = playable.GetTime();
}
void SortNotifications()
{
if (m_NeedSortNotifications)
{
m_Notifications.Sort((x, y) => x.time.CompareTo(y.time));
m_NeedSortNotifications = false;
}
}
static bool CanRestoreNotification(NotificationEntry e, FrameData info, double currentTime, double previousTime)
{
if (e.triggerOnce)
return false;
if (info.timeLooped)
return true;
//case 1111595: restore the notification if the time is manually set before it
return previousTime > currentTime && currentTime <= e.time;
}
void TriggerNotificationsInRange(double start, double end, FrameData info, Playable playable, bool checkState)
{
if (start <= end)
{
var playMode = Application.isPlaying;
for (var i = 0; i < m_Notifications.Count; i++)
{
var e = m_Notifications[i];
if (e.notificationFired && (checkState || e.triggerOnce))
continue;
var notificationTime = e.time;
if (e.prewarm && notificationTime < end && (e.triggerInEditor || playMode))
{
Trigger_internal(playable, info.output, ref e);
m_Notifications[i] = e;
}
else
{
if (notificationTime < start || notificationTime > end)
continue;
if (e.triggerInEditor || playMode)
{
Trigger_internal(playable, info.output, ref e);
m_Notifications[i] = e;
}
}
}
}
}
void SyncDurationWithExternalSource(Playable playable)
{
if (m_TimeSource.IsValid())
{
playable.SetDuration(m_TimeSource.GetDuration());
playable.SetTimeWrapMode(m_TimeSource.GetTimeWrapMode());
}
}
static void Trigger_internal(Playable playable, PlayableOutput output, ref NotificationEntry e)
{
output.PushNotification(playable, e.payload);
e.notificationFired = true;
}
static void Restore_internal(ref NotificationEntry e)
{
e.notificationFired = false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afeb55855d7a63b45ba6f8bd97599202
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: