Add live-preview option for editing scene animation

#519
This commit is contained in:
Wojtek Figat
2021-09-22 17:09:36 +02:00
parent 2a5a9e9581
commit 8e6c7a6194
6 changed files with 154 additions and 37 deletions

View File

@@ -231,7 +231,6 @@ namespace FlaxEditor.GUI.Timeline
state = PlaybackStates.Disabled;
PlaybackState = state;
CanPlayPauseStop = Editor.IsPlayMode;
if (_player && _player.Animation)
{

View File

@@ -221,7 +221,7 @@ namespace FlaxEditor.GUI.Timeline
private Vector2 _rightMouseButtonMovePos;
private float _zoom = 1.0f;
private bool _isMovingPositionHandle;
private bool _canPlayPauseStop = true;
private bool _canPlayPause = true, _canStop = true;
private List<IUndoAction> _batchedUndoActions;
/// <summary>
@@ -290,7 +290,11 @@ namespace FlaxEditor.GUI.Timeline
/// <summary>
/// Gets the current animation time position (in seconds).
/// </summary>
public float CurrentTime => _currentFrame / _framesPerSecond;
public float CurrentTime
{
get => _currentFrame / _framesPerSecond;
set => CurrentFrame = (int)(value * _framesPerSecond);
}
/// <summary>
/// Occurs when current playback animation frame gets changed.
@@ -511,16 +515,31 @@ namespace FlaxEditor.GUI.Timeline
}
/// <summary>
/// Gets or sets a value indicating whether user can use Play/Pause/Stop buttons, otherwise those should be disabled.
/// Gets or sets a value indicating whether user can use Play and Pause buttons, otherwise those should be disabled.
/// </summary>
public bool CanPlayPauseStop
public bool CanPlayPause
{
get => _canPlayPauseStop;
get => _canPlayPause;
set
{
if (_canPlayPauseStop == value)
if (_canPlayPause == value)
return;
_canPlayPauseStop = value;
_canPlayPause = value;
UpdatePlaybackButtons();
}
}
/// <summary>
/// Gets or sets a value indicating whether user can use Stop button, otherwise those should be disabled.
/// </summary>
public bool CanPlayStop
{
get => _canStop;
set
{
if (_canStop == value)
return;
_canStop = value;
UpdatePlaybackButtons();
}
}
@@ -1146,7 +1165,7 @@ namespace FlaxEditor.GUI.Timeline
if (_playbackPlay != null)
{
_playbackPlay.Visible = true;
_playbackPlay.Enabled = _canPlayPauseStop;
_playbackPlay.Enabled = _canPlayPause;
_playbackPlay.Brush = new SpriteBrush(icons.Play64);
_playbackPlay.Tag = false;
}
@@ -1167,12 +1186,12 @@ namespace FlaxEditor.GUI.Timeline
if (_playbackStop != null)
{
_playbackStop.Visible = true;
_playbackStop.Enabled = _canPlayPauseStop;
_playbackStop.Enabled = _canStop;
}
if (_playbackPlay != null)
{
_playbackPlay.Visible = true;
_playbackPlay.Enabled = _canPlayPauseStop;
_playbackPlay.Enabled = _canPlayPause;
_playbackPlay.Brush = new SpriteBrush(icons.Pause64);
_playbackPlay.Tag = true;
}
@@ -1193,12 +1212,12 @@ namespace FlaxEditor.GUI.Timeline
if (_playbackStop != null)
{
_playbackStop.Visible = true;
_playbackStop.Enabled = _canPlayPauseStop;
_playbackStop.Enabled = _canStop;
}
if (_playbackPlay != null)
{
_playbackPlay.Visible = true;
_playbackPlay.Enabled = _canPlayPauseStop;
_playbackPlay.Enabled = _canPlayPause;
_playbackPlay.Brush = new SpriteBrush(icons.Play64);
_playbackPlay.Tag = false;
}
@@ -2038,7 +2057,7 @@ namespace FlaxEditor.GUI.Timeline
}
break;
case KeyboardKeys.Spacebar:
if (CanPlayPauseStop)
if (CanPlayPause)
{
if (PlaybackState == PlaybackStates.Playing)
OnPause();

View File

@@ -592,8 +592,10 @@ namespace FlaxEditor.Windows.Assets
private ToolStripButton _saveButton;
private ToolStripButton _undoButton;
private ToolStripButton _redoButton;
private ToolStripButton _previewButton;
private ToolStripButton _renderButton;
private FlaxObjectRefPickerControl _previewPlayerPicker;
private SceneAnimationPlayer _previewPlayer;
private Undo _undo;
private bool _tmpSceneAnimationIsDirty;
private bool _isWaitingForTimelineLoad;
@@ -628,7 +630,9 @@ namespace FlaxEditor.Windows.Assets
AnchorPreset = AnchorPresets.StretchAll,
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
Parent = this,
Enabled = false
CanPlayPause = Editor.IsPlayMode,
CanPlayStop = Editor.IsPlayMode,
Enabled = false,
};
_timeline.Modified += OnTimelineModified;
_timeline.PlayerChanged += OnTimelinePlayerChanged;
@@ -640,6 +644,7 @@ namespace FlaxEditor.Windows.Assets
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip("Undo (Ctrl+Z)");
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip("Redo (Ctrl+Y)");
_toolstrip.AddSeparator();
_previewButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Refresh64, OnPreviewButtonClicked).SetAutoCheck(true).LinkTooltip("If checked, enables live-preview of the animation on a scene while editing");
_renderButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Build64, OnRenderButtonClicked).LinkTooltip("Open the scene animation rendering utility...");
_toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/scene-animations/index.html")).LinkTooltip("See documentation to learn more");
@@ -680,6 +685,54 @@ namespace FlaxEditor.Windows.Assets
UpdateToolstrip();
}
private void OnPreviewButtonClicked()
{
if (_previewButton.Checked)
{
// Use utility player actor for live-preview
if (!_previewPlayer)
{
_previewPlayer = new SceneAnimationPlayer
{
Animation = Asset,
HideFlags = HideFlags.FullyHidden,
RestoreStateOnStop = true,
PlayOnStart = false,
RandomStartTime = false,
UseTimeScale = false,
UpdateMode = SceneAnimationPlayer.UpdateModes.Manual,
};
Level.SpawnActor(_previewPlayer);
// Live-preview player is in pause or play mode (stopped on the usage end)
_previewPlayer.Play();
_previewPlayer.Pause();
}
var time = _timeline.CurrentTime;
_timeline.Player = _previewPlayer;
_previewPlayerPicker.Value = null;
_cachedPlayerId = Guid.Empty;
_previewPlayer.Time = time;
}
else
{
if (_timeline.Player == _previewPlayer)
{
_timeline.Player = null;
_previewPlayerPicker.Value = null;
_cachedPlayerId = Guid.Empty;
}
if (_previewPlayer)
{
_previewPlayer.Stop();
Object.Destroy(_previewPlayer);
_previewPlayer = null;
}
}
_previewPlayerPicker.Visible = !_previewButton.Checked;
_timeline.CanPlayPause = _previewButton.Checked || Editor.IsPlayMode;
}
private void OnRenderButtonClicked()
{
if (_popup != null)
@@ -713,16 +766,29 @@ namespace FlaxEditor.Windows.Assets
_timeline.Player = null;
_cachedPlayerId = id;
}
if (actor == _previewPlayer)
{
_previewPlayer = null;
if (_previewButton.Checked)
{
_previewButton.Checked = false;
OnPreviewButtonClicked();
}
}
}
private void OnTimelinePlayerChanged()
{
if (_previewButton.Checked)
return;
_previewPlayerPicker.Value = _timeline.Player;
_cachedPlayerId = _timeline.Player?.ID ?? Guid.Empty;
}
private void OnPreviewPlayerPickerChanged()
{
if (_previewButton.Checked)
return;
_timeline.Player = _previewPlayerPicker.Value as SceneAnimationPlayer;
}
@@ -743,7 +809,19 @@ namespace FlaxEditor.Windows.Assets
if (_timeline.IsModified)
{
var time = _timeline.CurrentTime;
var isPlaying = _previewPlayer?.IsPlaying ?? false;
_timeline.Save(_asset);
if (_previewButton.Checked && _previewPlayer != null)
{
// Preserve playback time in live-preview mode when editing the asset
_asset.WaitForLoaded();
_previewPlayer.Play();
if (!isPlaying)
_previewPlayer.Pause();
_previewPlayer.Time = time;
_timeline.CurrentTime = time;
}
}
return false;
@@ -770,6 +848,7 @@ namespace FlaxEditor.Windows.Assets
_saveButton.Enabled = IsEdited;
_undoButton.Enabled = _undo.CanUndo;
_redoButton.Enabled = _undo.CanRedo;
_previewButton.Enabled = Level.IsAnySceneLoaded && Editor.StateMachine.IsEditMode;
_renderButton.Enabled = Level.IsAnySceneLoaded && (Editor.IsPlayMode || Editor.StateMachine.IsEditMode);
base.UpdateToolstrip();
@@ -813,6 +892,8 @@ namespace FlaxEditor.Windows.Assets
base.OnPlayBegin();
UpdateToolstrip();
_timeline.CanPlayPause = true;
_timeline.CanPlayStop = true;
}
/// <inheritdoc />
@@ -821,6 +902,8 @@ namespace FlaxEditor.Windows.Assets
base.OnPlayEnd();
UpdateToolstrip();
_timeline.CanPlayPause = _previewButton.Checked;
_timeline.CanPlayStop = false;
}
/// <inheritdoc />
@@ -839,24 +922,16 @@ namespace FlaxEditor.Windows.Assets
// Check if temporary asset need to be updated
if (_tmpSceneAnimationIsDirty)
{
// Clear flag
_tmpSceneAnimationIsDirty = false;
// Update
RefreshTempAsset();
}
// Check if need to load timeline
if (_isWaitingForTimelineLoad && _asset.IsLoaded)
{
// Clear flag
_isWaitingForTimelineLoad = false;
// Load timeline data from the asset
_timeline._id = OriginalAsset.ID;
_timeline.Load(_asset);
// Setup
_undo.Clear();
_timeline.SetNoTracksText(null);
_timeline.Enabled = true;
@@ -874,6 +949,27 @@ namespace FlaxEditor.Windows.Assets
_timeline.Player = obj;
}
}
// Manually tick the live-preview animation player
if (_previewButton.Checked && _previewPlayer != null)
{
if (_previewPlayer.IsPlaying)
{
// Preview is playing
_previewPlayer.Tick(Time.UnscaledDeltaTime);
}
else if (Mathf.NearEqual(_previewPlayer.Time, _timeline.CurrentFrame))
{
// Preview is paused
_previewPlayer.Time = _timeline.CurrentTime;
}
else
{
// User is seeking
_previewPlayer.Time = _timeline.CurrentTime;
_previewPlayer.Tick(0.0f);
}
}
}
/// <inheritdoc />
@@ -884,7 +980,7 @@ namespace FlaxEditor.Windows.Assets
{
writer.WriteAttributeString("TimelineSplitter", _timeline.Splitter.SplitterValue.ToString());
writer.WriteAttributeString("TimeShowMode", _timeline.TimeShowMode.ToString());
var id = _timeline.Player?.ID ?? _cachedPlayerId;
var id = _previewButton.Checked ? Guid.Empty : (_timeline.Player?.ID ?? _cachedPlayerId);
writer.WriteAttributeString("SelectedPlayer", id.ToString());
writer.WriteAttributeString("ShowPreviewValues", _timeline.ShowPreviewValues.ToString());
writer.WriteAttributeString("ShowSelected3dTrack", _timeline.ShowSelected3dTrack.ToString());
@@ -920,6 +1016,11 @@ namespace FlaxEditor.Windows.Assets
{
Level.ActorDeleted -= OnActorDeleted;
if (_previewButton.Checked)
{
_previewButton.Checked = false;
OnPreviewButtonClicked();
}
if (_popup != null)
{
_popup.Dispose();