//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using System.Runtime.InteropServices; namespace BansheeEngine { /** @addtogroup Animation * @{ */ /// /// Determines how an animation clip behaves when it reaches the end. /// public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode { /// /// Loop around to the beginning/end when the last/first frame is reached. /// Loop, /// /// Clamp to end/beginning, keeping the last/first frame active. /// Clamp } /// /// Determines what happens to other animation clips when a new clip starts playing. /// public enum AnimPlayMode // Note: Must match C++ enum AnimPlayMode { /// /// All other animation clips are stopped. /// StopAll, /// /// Only the clips within the current layer are stopped. /// StopLayer } /// /// Contains information about a currently playing animation clip. /// [StructLayout(LayoutKind.Sequential), SerializeObject] public struct AnimationClipState // Note: Must match C++ struct AnimationClipState { /// /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers. /// public int layer; /// /// Current time the animation is playing from. /// public float time; /// /// Speed at which the animation is playing. /// public float speed; /// /// Determines how much of an influence does the clip have on the final pose. /// public float weight; /// /// Determines what happens to other animation clips when a new clip starts playing. /// public AnimWrapMode wrapMode; /// /// Initializes the state with default values. /// public void InitDefault() { speed = 1.0f; weight = 1.0f; wrapMode = AnimWrapMode.Loop; } } /// /// Handles animation playback. Takes one or multiple animation clips as input and evaluates them every animation update /// tick depending on set properties.The evaluated data is used by the core thread for skeletal animation, by the sim /// thread for updating attached scene objects and bones (if skeleton is attached), or the data is made available for /// manual queries in the case of generic animation. /// public class Animation : Component { private NativeAnimation _native; [SerializeField] private SerializableData serializableData = new SerializableData(); /// /// Returns the non-component version of Animation that is wrapped by this component. /// internal NativeAnimation Native { get { return _native; } } /// /// Determines the default clip to play as soon as the component is enabled. If more control over playing clips is /// needed use the , and methods to queue clips for /// playback manually, and method for modify their states individually. /// public AnimationClip DefaultClip { get { return serializableData.defaultClip; } set { serializableData.defaultClip = value; if (value != null && _native != null) _native.Play(value, 0, AnimPlayMode.StopLayer); } } /// /// Determines the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the /// first or last frame. /// /// public AnimWrapMode WrapMode { get { return serializableData.wrapMode; } set { serializableData.wrapMode = value; if (_native != null) _native.WrapMode = value; } } /// /// Determines the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse. /// public float Speed { get { return serializableData.speed; } set { serializableData.speed = value; if (_native != null) _native.Speed = value; } } /// /// Checks if any animation clips are currently playing. /// public bool IsPlaying { get { if (_native != null) return _native.IsPlaying(); return false; } } /// /// Plays the specified animation clip at the specified layer. /// /// Clip to play. /// Layer to play the clip in. Multiple clips can be playing at once in separate layers. /// Determines how are other playing animations handled when this animation starts. public void Play(AnimationClip clip, int layer = 0, AnimPlayMode playMode = AnimPlayMode.StopLayer) { if(_native != null) _native.Play(clip, layer, playMode); } /// /// Blends the specified animation clip with other animation clips playing in the specified layer. /// /// Clip to blend. /// Determines how much of an effect will the blended animation have on the final output. /// In range [0, 1]. All animation weights on the same layer will be normalized. /// Applies the blend over a specified time period, increasing the weight as the time /// passes. Set to zero to blend immediately. In seconds. /// Layer to play the clip in. Multiple clips can be playing at once in separate layers. public void Blend(AnimationClip clip, float weight = 1.0f, float fadeLength = 0.0f, int layer = 0) { if (_native != null) _native.Blend(clip, weight, fadeLength, layer); } /// /// Fades the specified animation clip in, while fading other playing animations out, over the specified time /// period. /// /// Clip to fade in. /// Determines the time period over which the fade occurs. In seconds. /// Layer to play the clip in. Multiple clips can be playing at once in separate layers. /// Determines should all other animations be faded out, or just the ones on the same layer. /// public void CrossFade(AnimationClip clip, float fadeLength = 0.0f, int layer = 0, AnimPlayMode playMode = AnimPlayMode.StopLayer) { if (_native != null) _native.CrossFade(clip, fadeLength, layer, playMode); } /// /// Stops playing all animations on the provided layer. /// /// Layer on which to stop animations on. public void Stop(int layer) { if (_native != null) _native.Stop(layer); } /// /// Stops playing all animations. /// public void StopAll() { if (_native != null) _native.StopAll(); } /// /// Retrieves detailed information about a currently playing animation clip. /// /// Clip to retrieve the information for. /// Animation clip state containing the requested information. Only valid if the method returns /// true. /// True if the state was found (animation clip is playing), false otherwise. public bool GetState(AnimationClip clip, out AnimationClipState state) { if (_native != null) return _native.GetState(clip, out state); state = new AnimationClipState(); return false; } /// /// Changes the state of a playing animation clip. If animation clip is not currently playing the state change is /// ignored. /// /// Clip to change the state for. /// New state of the animation (e.g. changing the time for seeking). public void SetState(AnimationClip clip, AnimationClipState state) { if (_native != null) _native.SetState(clip, state); } private void OnEnabled() { RestoreNative(); } private void OnDisabled() { DestroyNative(); } private void OnDestroy() { DestroyNative(); } /// /// Creates the internal representation of the animation and restores the values saved by the component. /// private void RestoreNative() { if (_native != null) _native.Destroy(); _native = new NativeAnimation(); // Restore saved values after reset _native.WrapMode = serializableData.wrapMode; _native.Speed = serializableData.speed; if (serializableData.defaultClip != null) _native.Play(serializableData.defaultClip, 0, AnimPlayMode.StopLayer); Renderable renderable = SceneObject.GetComponent(); if (renderable == null) return; NativeRenderable nativeRenderable = renderable.Native; if (nativeRenderable != null) nativeRenderable.Animation = _native; } /// /// Destroys the internal animation representation. /// private void DestroyNative() { Renderable renderableComponent = SceneObject.GetComponent(); if (renderableComponent != null) { NativeRenderable renderable = renderableComponent.Native; if (renderable != null) renderable.Animation = null; } if (_native != null) { _native.Destroy(); _native = null; } } /// /// Holds all data the animation component needs to persist through serialization. /// [SerializeObject] private class SerializableData { public AnimationClip defaultClip; public AnimWrapMode wrapMode = AnimWrapMode.Loop; public float speed = 1.0f; } } /** @} */ }