mirror of
https://github.com/ppy/osu.git
synced 2024-09-21 18:47:27 +08:00
Merge pull request #26446 from LeNitrous/add/replay-playback-controls
Add replay playback controls
This commit is contained in:
commit
c1c2e61723
24
osu.Game/Localisation/PlayerSettingsOverlayStrings.cs
Normal file
24
osu.Game/Localisation/PlayerSettingsOverlayStrings.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class PlayerSettingsOverlayStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.PlaybackSettings";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Seek backward {0} seconds"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString SeekBackwardSeconds(double arg0) => new TranslatableString(getKey(@"seek_backward_seconds"), @"Seek backward {0} seconds", arg0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Seek forward {0} seconds"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString SeekForwardSeconds(double arg0) => new TranslatableString(getKey(@"seek_forward_seconds"), @"Seek forward {0} seconds", arg0);
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -12,17 +12,19 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
private const int fade_duration = 200;
|
private const int fade_duration = 200;
|
||||||
|
|
||||||
public readonly PlaybackSettings PlaybackSettings;
|
|
||||||
|
|
||||||
public readonly VisualSettings VisualSettings;
|
public readonly VisualSettings VisualSettings;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private readonly FillFlowContainer content;
|
||||||
|
|
||||||
public PlayerSettingsOverlay()
|
public PlayerSettingsOverlay()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight;
|
Anchor = Anchor.TopRight;
|
||||||
Origin = Anchor.TopRight;
|
Origin = Anchor.TopRight;
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
Child = new FillFlowContainer<PlayerSettingsGroup>
|
InternalChild = content = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
@ -31,7 +33,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Spacing = new Vector2(0, 20),
|
Spacing = new Vector2(0, 20),
|
||||||
Children = new PlayerSettingsGroup[]
|
Children = new PlayerSettingsGroup[]
|
||||||
{
|
{
|
||||||
PlaybackSettings = new PlaybackSettings { Expanded = { Value = false } },
|
|
||||||
VisualSettings = new VisualSettings { Expanded = { Value = false } },
|
VisualSettings = new VisualSettings { Expanded = { Value = false } },
|
||||||
new AudioSettings { Expanded = { Value = false } }
|
new AudioSettings { Expanded = { Value = false } }
|
||||||
}
|
}
|
||||||
@ -40,5 +41,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(fade_duration);
|
protected override void PopIn() => this.FadeIn(fade_duration);
|
||||||
protected override void PopOut() => this.FadeOut(fade_duration);
|
protected override void PopOut() => this.FadeOut(fade_duration);
|
||||||
|
|
||||||
|
public void AddAtStart(PlayerSettingsGroup drawable) => content.Insert(-1, drawable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public readonly BindableNumber<double> UserPlaybackRate = new BindableDouble(1)
|
public readonly BindableNumber<double> UserPlaybackRate = new BindableDouble(1)
|
||||||
{
|
{
|
||||||
MinValue = 0.5,
|
MinValue = 0.05,
|
||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
Precision = 0.1,
|
Precision = 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -274,7 +274,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
track.BindAdjustments(AdjustmentsFromMods);
|
track.BindAdjustments(AdjustmentsFromMods);
|
||||||
track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
track.AddAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||||
|
|
||||||
speedAdjustmentsApplied = true;
|
speedAdjustmentsApplied = true;
|
||||||
}
|
}
|
||||||
@ -286,7 +286,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
track.UnbindAdjustments(AdjustmentsFromMods);
|
track.UnbindAdjustments(AdjustmentsFromMods);
|
||||||
track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||||
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
track.RemoveAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||||
|
|
||||||
speedAdjustmentsApplied = false;
|
speedAdjustmentsApplied = false;
|
||||||
}
|
}
|
||||||
|
@ -470,9 +470,6 @@ namespace osu.Game.Screens.Play
|
|||||||
skipOutroOverlay.Expire();
|
skipOutroOverlay.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
|
||||||
HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.UserPlaybackRate.BindTarget = master.UserPlaybackRate;
|
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Screens.Edit.Timing;
|
||||||
|
using osuTK;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.PlayerSettings
|
namespace osu.Game.Screens.Play.PlayerSettings
|
||||||
{
|
{
|
||||||
@ -15,49 +23,161 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
|||||||
|
|
||||||
public readonly Bindable<double> UserPlaybackRate = new BindableDouble(1)
|
public readonly Bindable<double> UserPlaybackRate = new BindableDouble(1)
|
||||||
{
|
{
|
||||||
MinValue = 0.5,
|
MinValue = 0.05,
|
||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
Precision = 0.1,
|
Precision = 0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly PlayerSliderBar<double> rateSlider;
|
private readonly PlayerSliderBar<double> rateSlider;
|
||||||
|
|
||||||
private readonly OsuSpriteText multiplierText;
|
private readonly OsuSpriteText multiplierText;
|
||||||
|
|
||||||
|
private readonly BindableBool isPaused = new BindableBool();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private GameplayClockContainer? gameplayClock { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private GameplayState? gameplayState { get; set; }
|
||||||
|
|
||||||
public PlaybackSettings()
|
public PlaybackSettings()
|
||||||
: base("playback")
|
: base("playback")
|
||||||
{
|
{
|
||||||
|
const double seek_amount = 5000;
|
||||||
|
const double seek_fast_amount = 10000;
|
||||||
|
|
||||||
|
IconButton play;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Horizontal = padding },
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(0, padding),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
RelativeSizeAxes = Axes.X,
|
||||||
Origin = Anchor.CentreLeft,
|
AutoSizeAxes = Axes.Y,
|
||||||
Text = "Playback speed",
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5, 0),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new SeekButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Icon = FontAwesome.Solid.FastBackward,
|
||||||
|
Action = () => seek(-1, seek_fast_amount),
|
||||||
|
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_fast_amount / 1000),
|
||||||
|
},
|
||||||
|
new SeekButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Icon = FontAwesome.Solid.Backward,
|
||||||
|
Action = () => seek(-1, seek_amount),
|
||||||
|
TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000),
|
||||||
|
},
|
||||||
|
play = new IconButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(1.4f),
|
||||||
|
IconScale = new Vector2(1.4f),
|
||||||
|
Icon = FontAwesome.Regular.PlayCircle,
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
if (gameplayClock != null)
|
||||||
|
{
|
||||||
|
if (gameplayClock.IsRunning)
|
||||||
|
gameplayClock.Stop();
|
||||||
|
else
|
||||||
|
gameplayClock.Start();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new SeekButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Icon = FontAwesome.Solid.Forward,
|
||||||
|
Action = () => seek(1, seek_amount),
|
||||||
|
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_amount / 1000),
|
||||||
|
},
|
||||||
|
new SeekButton
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Icon = FontAwesome.Solid.FastForward,
|
||||||
|
Action = () => seek(1, seek_fast_amount),
|
||||||
|
TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_fast_amount / 1000),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
multiplierText = new OsuSpriteText
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
RelativeSizeAxes = Axes.X,
|
||||||
Origin = Anchor.CentreRight,
|
AutoSizeAxes = Axes.Y,
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
Children = new Drawable[]
|
||||||
}
|
{
|
||||||
|
rateSlider = new PlayerSliderBar<double>
|
||||||
|
{
|
||||||
|
LabelText = "Playback speed",
|
||||||
|
Current = UserPlaybackRate,
|
||||||
|
},
|
||||||
|
multiplierText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
||||||
|
Margin = new MarginPadding { Right = 20 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rateSlider = new PlayerSliderBar<double> { Current = UserPlaybackRate }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isPaused.BindValueChanged(paused =>
|
||||||
|
{
|
||||||
|
if (!paused.NewValue)
|
||||||
|
{
|
||||||
|
play.TooltipText = ToastStrings.PauseTrack;
|
||||||
|
play.Icon = FontAwesome.Regular.PauseCircle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
play.TooltipText = ToastStrings.PlayTrack;
|
||||||
|
play.Icon = FontAwesome.Regular.PlayCircle;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
void seek(int direction, double amount)
|
||||||
|
{
|
||||||
|
double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState?.Beatmap.GetLastObjectTime() ?? 0);
|
||||||
|
gameplayClock?.Seek(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true);
|
rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.00}x", true);
|
||||||
|
|
||||||
|
if (gameplayClock != null)
|
||||||
|
isPaused.BindTarget = gameplayClock.IsPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class SeekButton : IconButton
|
||||||
|
{
|
||||||
|
public SeekButton()
|
||||||
|
{
|
||||||
|
AddInternal(new RepeatingButtonBehaviour(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -15,6 +16,7 @@ using osu.Game.Input.Bindings;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -49,6 +51,24 @@ namespace osu.Game.Screens.Play
|
|||||||
this.createScore = createScore;
|
this.createScore = createScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
if (!LoadedBeatmapSuccessfully)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var playbackSettings = new PlaybackSettings
|
||||||
|
{
|
||||||
|
Depth = float.MaxValue,
|
||||||
|
Expanded = { Value = false }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
||||||
|
playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate);
|
||||||
|
|
||||||
|
HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void PrepareReplay()
|
protected override void PrepareReplay()
|
||||||
{
|
{
|
||||||
DrawableRuleset?.SetReplayScore(Score);
|
DrawableRuleset?.SetReplayScore(Score);
|
||||||
|
Loading…
Reference in New Issue
Block a user