You're breathtaking!
This commit is contained in:
639
Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs
Normal file
639
Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs
Normal file
@@ -0,0 +1,639 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.GUI.Timeline.Undo;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||
{
|
||||
/// <summary>
|
||||
/// The timeline media that represents an audio clip media event.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Media" />
|
||||
public class AudioMedia : SingleMediaAssetMedia
|
||||
{
|
||||
/// <summary>
|
||||
/// True if loop track, otherwise audio clip will stop on the end.
|
||||
/// </summary>
|
||||
public bool Loop
|
||||
{
|
||||
get => Track.Loop;
|
||||
set
|
||||
{
|
||||
if (Track.Loop != value)
|
||||
{
|
||||
Track.Loop = value;
|
||||
Preview.DrawMode = value ? AudioClipPreview.DrawModes.Looped : AudioClipPreview.DrawModes.Single;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Proxy : ProxyBase<AudioTrack, AudioMedia>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the audio clip to play.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(10), Tooltip("The audio clip to play.")]
|
||||
public AudioClip Audio
|
||||
{
|
||||
get => Track.Asset;
|
||||
set => Track.Asset = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio clip looping mode.
|
||||
/// </summary>
|
||||
[EditorDisplay("General"), EditorOrder(20), Tooltip("If checked, the audio clip will loop when playback exceeds its duration. Otherwise it will stop play.")]
|
||||
public bool Loop
|
||||
{
|
||||
get => Track.TrackLoop;
|
||||
set => Track.TrackLoop = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Proxy(AudioTrack track, AudioMedia media)
|
||||
: base(track, media)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The audio clip preview.
|
||||
/// </summary>
|
||||
public AudioClipPreview Preview;
|
||||
|
||||
/// <inheritdoc />
|
||||
public AudioMedia()
|
||||
{
|
||||
Preview = new AudioClipPreview
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
DrawMode = AudioClipPreview.DrawModes.Single,
|
||||
Parent = this,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineChanged(Track track)
|
||||
{
|
||||
base.OnTimelineChanged(track);
|
||||
|
||||
PropertiesEditObject = new Proxy(Track as AudioTrack, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineZoomChanged()
|
||||
{
|
||||
base.OnTimelineZoomChanged();
|
||||
|
||||
Preview.ViewScale = Timeline.UnitsPerSecond / AudioClipPreview.UnitsPerSecond * Timeline.Zoom;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The timeline track that represents an audio clip playback.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||
public class AudioTrack : SingleMediaAssetTrack<AudioClip, AudioMedia>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the archetype.
|
||||
/// </summary>
|
||||
/// <returns>The archetype.</returns>
|
||||
public static TrackArchetype GetArchetype()
|
||||
{
|
||||
return new TrackArchetype
|
||||
{
|
||||
TypeId = 5,
|
||||
Name = "Audio",
|
||||
Create = options => new AudioTrack(ref options),
|
||||
Load = LoadTrack,
|
||||
Save = SaveTrack,
|
||||
};
|
||||
}
|
||||
|
||||
private static void LoadTrack(int version, Track track, BinaryReader stream)
|
||||
{
|
||||
var e = (AudioTrack)track;
|
||||
Guid id = new Guid(stream.ReadBytes(16));
|
||||
e.Asset = FlaxEngine.Content.LoadAsync<AudioClip>(id);
|
||||
var m = e.TrackMedia;
|
||||
m.StartFrame = stream.ReadInt32();
|
||||
m.DurationFrames = stream.ReadInt32();
|
||||
m.Preview.DrawMode = track.Loop ? AudioClipPreview.DrawModes.Looped : AudioClipPreview.DrawModes.Single;
|
||||
}
|
||||
|
||||
private static void SaveTrack(Track track, BinaryWriter stream)
|
||||
{
|
||||
var e = (AudioTrack)track;
|
||||
var assetId = e.Asset?.ID ?? Guid.Empty;
|
||||
|
||||
stream.Write(assetId.ToByteArray());
|
||||
|
||||
if (e.Media.Count != 0)
|
||||
{
|
||||
var m = e.TrackMedia;
|
||||
stream.Write(m.StartFrame);
|
||||
stream.Write(m.DurationFrames);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream.Write(0);
|
||||
stream.Write(track.Timeline.DurationFrames);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio clip looping mode.
|
||||
/// </summary>
|
||||
public bool TrackLoop
|
||||
{
|
||||
get => TrackMedia.Loop;
|
||||
set
|
||||
{
|
||||
AudioMedia media = TrackMedia;
|
||||
if (media.Loop == value)
|
||||
return;
|
||||
|
||||
media.Loop = value;
|
||||
Timeline?.MarkAsEdited();
|
||||
}
|
||||
}
|
||||
|
||||
private Button _addButton;
|
||||
|
||||
/// <inheritdoc />
|
||||
public AudioTrack(ref TrackCreateOptions options)
|
||||
: base(ref options)
|
||||
{
|
||||
// Add button
|
||||
const float buttonSize = 14;
|
||||
_addButton = new Button
|
||||
{
|
||||
Text = "+",
|
||||
TooltipText = "Add sub-tracks",
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
IsScrollable = false,
|
||||
Offsets = new Margin(-buttonSize - 2 + _muteCheckbox.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize),
|
||||
Parent = this,
|
||||
};
|
||||
_addButton.Clicked += OnAddButtonClicked;
|
||||
_picker.Location = new Vector2(_addButton.Left - _picker.Width - 2, 2);
|
||||
}
|
||||
|
||||
private void OnAddButtonClicked()
|
||||
{
|
||||
var cm = new ContextMenu.ContextMenu();
|
||||
cm.AddButton("Volume", OnAddVolumeTrack);
|
||||
cm.Show(_addButton.Parent, _addButton.BottomLeft);
|
||||
}
|
||||
|
||||
private void OnAddVolumeTrack()
|
||||
{
|
||||
var track = Timeline.NewTrack(AudioVolumeTrack.GetArchetype());
|
||||
track.ParentTrack = this;
|
||||
track.TrackIndex = TrackIndex + 1;
|
||||
track.Name = Guid.NewGuid().ToString("N");
|
||||
Timeline.AddTrack(track);
|
||||
|
||||
Expand();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSubTracksChanged()
|
||||
{
|
||||
base.OnSubTracksChanged();
|
||||
|
||||
_addButton.Enabled = SubTracks.Count == 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetChanged()
|
||||
{
|
||||
base.OnAssetChanged();
|
||||
|
||||
TrackMedia.Preview.Asset = Asset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The child volume track for audio track. Used to animate audio volume over time.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||
class AudioVolumeTrack : Track
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the archetype.
|
||||
/// </summary>
|
||||
/// <returns>The archetype.</returns>
|
||||
public static TrackArchetype GetArchetype()
|
||||
{
|
||||
return new TrackArchetype
|
||||
{
|
||||
TypeId = 6,
|
||||
Name = "Audio Volume",
|
||||
DisableSpawnViaGUI = true,
|
||||
Create = options => new AudioVolumeTrack(ref options),
|
||||
Load = LoadTrack,
|
||||
Save = SaveTrack,
|
||||
};
|
||||
}
|
||||
|
||||
private static void LoadTrack(int version, Track track, BinaryReader stream)
|
||||
{
|
||||
var e = (AudioVolumeTrack)track;
|
||||
int count = stream.ReadInt32();
|
||||
var keyframes = new BezierCurve<float>.Keyframe[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
keyframes[i] = new BezierCurve<float>.Keyframe
|
||||
{
|
||||
Time = stream.ReadSingle(),
|
||||
Value = stream.ReadSingle(),
|
||||
TangentIn = stream.ReadSingle(),
|
||||
TangentOut = stream.ReadSingle(),
|
||||
};
|
||||
}
|
||||
e.Curve.SetKeyframes(keyframes);
|
||||
}
|
||||
|
||||
private static void SaveTrack(Track track, BinaryWriter stream)
|
||||
{
|
||||
var e = (AudioVolumeTrack)track;
|
||||
var keyframes = e.Curve.Keyframes;
|
||||
int count = keyframes.Count;
|
||||
stream.Write(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var keyframe = keyframes[i];
|
||||
stream.Write(keyframe.Time);
|
||||
stream.Write(keyframe.Value);
|
||||
stream.Write(keyframe.TangentIn);
|
||||
stream.Write(keyframe.TangentOut);
|
||||
}
|
||||
}
|
||||
|
||||
private AudioMedia _audioMedia;
|
||||
private const float CollapsedHeight = 20.0f;
|
||||
private const float ExpandedHeight = 64.0f;
|
||||
private Label _previewValue;
|
||||
private byte[] _curveEditingStartData;
|
||||
|
||||
/// <summary>
|
||||
/// The volume curve. Values can be in range 0-1 to animate volume intensity and the track playback starts at the parent audio track media beginning. This curve does not loop.
|
||||
/// </summary>
|
||||
public BezierCurveEditor<float> Curve;
|
||||
|
||||
/// <inheritdoc />
|
||||
public AudioVolumeTrack(ref TrackCreateOptions options)
|
||||
: base(ref options)
|
||||
{
|
||||
Title = "Volume";
|
||||
Height = CollapsedHeight;
|
||||
|
||||
// Curve editor
|
||||
Curve = new BezierCurveEditor<float>
|
||||
{
|
||||
Visible = false,
|
||||
EnableZoom = CurveEditorBase.UseMode.Off,
|
||||
EnablePanning = CurveEditorBase.UseMode.Off,
|
||||
ScrollBars = ScrollBars.None,
|
||||
DefaultValue = 1.0f,
|
||||
ShowStartEndLines = true,
|
||||
};
|
||||
Curve.Edited += OnCurveEdited;
|
||||
Curve.EditingStart += OnCurveEditingStart;
|
||||
Curve.EditingEnd += OnCurveEditingEnd;
|
||||
Curve.UnlockChildrenRecursive();
|
||||
|
||||
// Navigation buttons
|
||||
const float buttonSize = 14;
|
||||
var icons = Editor.Instance.Icons;
|
||||
var rightKey = new Image
|
||||
{
|
||||
TooltipText = "Sets the time to the next key",
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
IsScrollable = false,
|
||||
Color = Style.Current.ForegroundGrey,
|
||||
Margin = new Margin(1),
|
||||
Brush = new SpriteBrush(icons.ArrowRight32),
|
||||
Offsets = new Margin(-buttonSize - 2 + _muteCheckbox.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize),
|
||||
Parent = this,
|
||||
};
|
||||
rightKey.Clicked += OnRightKeyClicked;
|
||||
var addKey = new Image
|
||||
{
|
||||
TooltipText = "Adds a new key at the current time",
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
IsScrollable = false,
|
||||
Color = Style.Current.ForegroundGrey,
|
||||
Margin = new Margin(3),
|
||||
Brush = new SpriteBrush(icons.Add48),
|
||||
Offsets = new Margin(-buttonSize - 2 + rightKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize),
|
||||
Parent = this,
|
||||
};
|
||||
addKey.Clicked += OnAddKeyClicked;
|
||||
var leftKey = new Image
|
||||
{
|
||||
TooltipText = "Sets the time to the previous key",
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
IsScrollable = false,
|
||||
Color = Style.Current.ForegroundGrey,
|
||||
Margin = new Margin(1),
|
||||
Brush = new SpriteBrush(icons.ArrowLeft32),
|
||||
Offsets = new Margin(-buttonSize - 2 + addKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize),
|
||||
Parent = this,
|
||||
};
|
||||
leftKey.Clicked += OnLeftKeyClicked;
|
||||
|
||||
// Value preview
|
||||
var previewWidth = 50.0f;
|
||||
_previewValue = new Label
|
||||
{
|
||||
AutoFocus = true,
|
||||
AnchorPreset = AnchorPresets.MiddleRight,
|
||||
IsScrollable = false,
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
TextColor = Style.Current.ForegroundGrey,
|
||||
Margin = new Margin(1),
|
||||
Offsets = new Margin(-previewWidth - 2 + leftKey.Offsets.Left, previewWidth, TextBox.DefaultHeight * -0.5f, TextBox.DefaultHeight),
|
||||
Parent = this,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnRightKeyClicked(Image image, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && GetNextKeyframeFrame(Timeline.CurrentTime, out var frame))
|
||||
{
|
||||
Timeline.OnSeek(frame);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnContextMenu(ContextMenu.ContextMenu menu)
|
||||
{
|
||||
base.OnContextMenu(menu);
|
||||
|
||||
if (_audioMedia == null || Curve == null)
|
||||
return;
|
||||
menu.AddSeparator();
|
||||
menu.AddButton("Copy Preview Value", () =>
|
||||
{
|
||||
var time = (Timeline.CurrentFrame - _audioMedia.StartFrame) / Timeline.FramesPerSecond;
|
||||
Curve.Evaluate(out var value, time, false);
|
||||
Clipboard.Text = Utils.RoundTo2DecimalPlaces(Mathf.Saturate(value)).ToString("0.00");
|
||||
}).LinkTooltip("Copies the current track value to the clipboard").Enabled = Timeline.ShowPreviewValues;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool GetNextKeyframeFrame(float time, out int result)
|
||||
{
|
||||
if (_audioMedia != null)
|
||||
{
|
||||
var mediaTime = time - _audioMedia.StartFrame / Timeline.FramesPerSecond;
|
||||
for (int i = 0; i < Curve.Keyframes.Count; i++)
|
||||
{
|
||||
var k = Curve.Keyframes[i];
|
||||
if (k.Time > mediaTime)
|
||||
{
|
||||
result = Mathf.FloorToInt(k.Time * Timeline.FramesPerSecond) + _audioMedia.StartFrame;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.GetNextKeyframeFrame(time, out result);
|
||||
}
|
||||
|
||||
private void OnAddKeyClicked(Image image, MouseButton button)
|
||||
{
|
||||
var currentFrame = Timeline.CurrentFrame;
|
||||
if (button == MouseButton.Left && _audioMedia != null && currentFrame >= _audioMedia.StartFrame && currentFrame < _audioMedia.StartFrame + _audioMedia.DurationFrames)
|
||||
{
|
||||
var time = (currentFrame - _audioMedia.StartFrame) / Timeline.FramesPerSecond;
|
||||
for (int i = Curve.Keyframes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var k = Curve.Keyframes[i];
|
||||
var frame = Mathf.FloorToInt(k.Time * Timeline.FramesPerSecond) + _audioMedia.StartFrame;
|
||||
if (frame == Timeline.CurrentFrame)
|
||||
{
|
||||
// Already added
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
using (new TrackUndoBlock(this))
|
||||
Curve.AddKeyframe(new BezierCurve<float>.Keyframe(time, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLeftKeyClicked(Image image, MouseButton button)
|
||||
{
|
||||
if (button == MouseButton.Left && GetPreviousKeyframeFrame(Timeline.CurrentTime, out var frame))
|
||||
{
|
||||
Timeline.OnSeek(frame);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool GetPreviousKeyframeFrame(float time, out int result)
|
||||
{
|
||||
if (_audioMedia != null)
|
||||
{
|
||||
var mediaTime = time - _audioMedia.StartFrame / Timeline.FramesPerSecond;
|
||||
for (int i = Curve.Keyframes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var k = Curve.Keyframes[i];
|
||||
if (k.Time < mediaTime)
|
||||
{
|
||||
result = Mathf.FloorToInt(k.Time * Timeline.FramesPerSecond) + _audioMedia.StartFrame;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return base.GetPreviousKeyframeFrame(time, out result);
|
||||
}
|
||||
|
||||
private void UpdatePreviewValue()
|
||||
{
|
||||
if (_audioMedia == null || Curve == null || Timeline == null)
|
||||
return;
|
||||
|
||||
var time = (Timeline.CurrentFrame - _audioMedia.StartFrame) / Timeline.FramesPerSecond;
|
||||
Curve.Evaluate(out var value, time, false);
|
||||
_previewValue.Text = Utils.RoundTo2DecimalPlaces(Mathf.Saturate(value)).ToString("0.00");
|
||||
}
|
||||
|
||||
private void UpdateCurve()
|
||||
{
|
||||
if (_audioMedia == null || Curve == null || Timeline == null)
|
||||
return;
|
||||
|
||||
Curve.Bounds = new Rectangle(_audioMedia.X, Y + 1.0f, _audioMedia.Width, Height - 2.0f);
|
||||
|
||||
var expanded = IsExpanded;
|
||||
if (expanded)
|
||||
{
|
||||
//Curve.ViewScale = new Vector2(1.0f, CurveEditor<float>.UnitsPerSecond / Curve.Height);
|
||||
Curve.ViewScale = new Vector2(Timeline.Zoom, 0.4f);
|
||||
Curve.ViewOffset = new Vector2(0.0f, 30.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Curve.ViewScale = new Vector2(Timeline.Zoom, 1.0f);
|
||||
Curve.ViewOffset = Vector2.Zero;
|
||||
}
|
||||
Curve.ShowCollapsed = !expanded;
|
||||
Curve.ShowBackground = expanded;
|
||||
Curve.ShowAxes = expanded;
|
||||
Curve.Visible = Visible;
|
||||
Curve.UpdateKeyframes();
|
||||
}
|
||||
|
||||
private void OnCurveEdited()
|
||||
{
|
||||
UpdatePreviewValue();
|
||||
Timeline.MarkAsEdited();
|
||||
}
|
||||
|
||||
private void OnCurveEditingStart()
|
||||
{
|
||||
_curveEditingStartData = EditTrackAction.CaptureData(this);
|
||||
}
|
||||
|
||||
private void OnCurveEditingEnd()
|
||||
{
|
||||
var after = EditTrackAction.CaptureData(this);
|
||||
if (!Utils.ArraysEqual(_curveEditingStartData, after))
|
||||
Timeline.Undo.AddAction(new EditTrackAction(Timeline, this, _curveEditingStartData, after));
|
||||
_curveEditingStartData = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool CanDrag => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool CanRename => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool CanExpand => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnParentTrackChanged(Track parent)
|
||||
{
|
||||
base.OnParentTrackChanged(parent);
|
||||
|
||||
if (_audioMedia != null)
|
||||
{
|
||||
_audioMedia.StartFrameChanged -= UpdateCurve;
|
||||
_audioMedia.DurationFramesChanged -= UpdateCurve;
|
||||
_audioMedia = null;
|
||||
}
|
||||
|
||||
if (parent is AudioTrack audioTrack)
|
||||
{
|
||||
var media = audioTrack.TrackMedia;
|
||||
media.StartFrameChanged += UpdateCurve;
|
||||
media.DurationFramesChanged += UpdateCurve;
|
||||
_audioMedia = media;
|
||||
UpdateCurve();
|
||||
UpdatePreviewValue();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnExpandedChanged()
|
||||
{
|
||||
Height = IsExpanded ? ExpandedHeight : CollapsedHeight;
|
||||
UpdateCurve();
|
||||
|
||||
base.OnExpandedChanged();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnVisibleChanged()
|
||||
{
|
||||
base.OnVisibleChanged();
|
||||
|
||||
Curve.Visible = Visible;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineChanged(Timeline timeline)
|
||||
{
|
||||
base.OnTimelineChanged(timeline);
|
||||
|
||||
Curve.Parent = timeline?.MediaPanel;
|
||||
Curve.FPS = timeline?.FramesPerSecond;
|
||||
UpdateCurve();
|
||||
UpdatePreviewValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnUndo()
|
||||
{
|
||||
base.OnUndo();
|
||||
|
||||
if (Curve != null)
|
||||
{
|
||||
// TODO: fix this hack: curve UI doesn't update properly after undo that modified keyframes
|
||||
IsExpanded = !IsExpanded;
|
||||
IsExpanded = !IsExpanded;
|
||||
}
|
||||
UpdatePreviewValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineZoomChanged()
|
||||
{
|
||||
base.OnTimelineZoomChanged();
|
||||
|
||||
UpdateCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineArrange()
|
||||
{
|
||||
base.OnTimelineArrange();
|
||||
|
||||
UpdateCurve();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineFpsChanged(float before, float after)
|
||||
{
|
||||
base.OnTimelineFpsChanged(before, after);
|
||||
|
||||
Curve.FPS = after;
|
||||
UpdatePreviewValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnTimelineCurrentFrameChanged(int frame)
|
||||
{
|
||||
base.OnTimelineCurrentFrameChanged(frame);
|
||||
|
||||
UpdatePreviewValue();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
if (Curve != null)
|
||||
{
|
||||
Curve.Dispose();
|
||||
Curve = null;
|
||||
}
|
||||
_previewValue = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user