mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +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;
|
||||
|
||||
public readonly PlaybackSettings PlaybackSettings;
|
||||
|
||||
public readonly VisualSettings VisualSettings;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private readonly FillFlowContainer content;
|
||||
|
||||
public PlayerSettingsOverlay()
|
||||
{
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Child = new FillFlowContainer<PlayerSettingsGroup>
|
||||
InternalChild = content = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
@ -31,7 +33,6 @@ namespace osu.Game.Screens.Play.HUD
|
||||
Spacing = new Vector2(0, 20),
|
||||
Children = new PlayerSettingsGroup[]
|
||||
{
|
||||
PlaybackSettings = new PlaybackSettings { Expanded = { Value = false } },
|
||||
VisualSettings = new VisualSettings { 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 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)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MinValue = 0.05,
|
||||
MaxValue = 2,
|
||||
Precision = 0.1,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@ -274,7 +274,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
track.BindAdjustments(AdjustmentsFromMods);
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
}
|
||||
@ -286,7 +286,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
track.UnbindAdjustments(AdjustmentsFromMods);
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust);
|
||||
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = false;
|
||||
}
|
||||
|
@ -470,9 +470,6 @@ namespace osu.Game.Screens.Play
|
||||
skipOutroOverlay.Expire();
|
||||
}
|
||||
|
||||
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
||||
HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.UserPlaybackRate.BindTarget = master.UserPlaybackRate;
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,19 @@
|
||||
// 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 System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
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
|
||||
{
|
||||
@ -15,49 +23,161 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
|
||||
public readonly Bindable<double> UserPlaybackRate = new BindableDouble(1)
|
||||
{
|
||||
MinValue = 0.5,
|
||||
MinValue = 0.05,
|
||||
MaxValue = 2,
|
||||
Precision = 0.1,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
private readonly PlayerSliderBar<double> rateSlider;
|
||||
|
||||
private readonly OsuSpriteText multiplierText;
|
||||
|
||||
private readonly BindableBool isPaused = new BindableBool();
|
||||
|
||||
[Resolved]
|
||||
private GameplayClockContainer? gameplayClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private GameplayState? gameplayState { get; set; }
|
||||
|
||||
public PlaybackSettings()
|
||||
: base("playback")
|
||||
{
|
||||
const double seek_amount = 5000;
|
||||
const double seek_fast_amount = 10000;
|
||||
|
||||
IconButton play;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, padding),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
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),
|
||||
},
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = padding },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new OsuSpriteText
|
||||
rateSlider = new PlayerSliderBar<double>
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Text = "Playback speed",
|
||||
LabelText = "Playback speed",
|
||||
Current = UserPlaybackRate,
|
||||
},
|
||||
multiplierText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
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()
|
||||
{
|
||||
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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
@ -15,6 +16,7 @@ using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Users;
|
||||
|
||||
@ -49,6 +51,24 @@ namespace osu.Game.Screens.Play
|
||||
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()
|
||||
{
|
||||
DrawableRuleset?.SetReplayScore(Score);
|
||||
|
Loading…
Reference in New Issue
Block a user