diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs index 2427b6d12c..ba17cfc3d8 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs @@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests base.Reset(); Add(new BackButton()); - Add(new SkipButton()); + Add(new SkipButton(Clock.CurrentTime + 5000)); } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5ef6b227c1..f9307595d2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -63,8 +63,6 @@ namespace osu.Game.Screens.Play #endregion - private SkipButton skipButton; - private HUDOverlay hudOverlay; private FailOverlay failOverlay; @@ -162,6 +160,7 @@ namespace osu.Game.Screens.Play }, Children = new Drawable[] { + new SkipButton(firstObjectTime) { AudioClock = decoupledClock }, new Container { RelativeSizeAxes = Axes.Both, @@ -169,11 +168,6 @@ namespace osu.Game.Screens.Play Children = new Drawable[] { HitRenderer, - skipButton = new SkipButton - { - Alpha = 0, - Margin = new MarginPadding { Bottom = 140 } // this is temporary - }, } }, hudOverlay = new StandardHUDOverlay @@ -219,33 +213,6 @@ namespace osu.Game.Screens.Play scoreProcessor.Failed += onFail; } - private void initializeSkipButton() - { - const double skip_required_cutoff = 3000; - const double fade_time = 300; - - double firstHitObject = Beatmap.Beatmap.HitObjects.First().StartTime; - - if (firstHitObject < skip_required_cutoff) - { - skipButton.Alpha = 0; - skipButton.Expire(); - return; - } - - skipButton.FadeInFromZero(fade_time); - - skipButton.Action = () => - { - decoupledClock.Seek(firstHitObject - skip_required_cutoff - fade_time); - skipButton.Action = null; - }; - - using (skipButton.BeginDelayedSequence(firstHitObject - skip_required_cutoff - fade_time)) - skipButton.FadeOut(fade_time); - skipButton.Expire(); - } - public void Restart() { ValidForResume = false; @@ -312,7 +279,6 @@ namespace osu.Game.Screens.Play if (!pauseContainer.IsPaused) decoupledClock.Start(); - initializeSkipButton(); }); pauseContainer.Alpha = 0; diff --git a/osu.Game/Screens/Play/SkipButton.cs b/osu.Game/Screens/Play/SkipButton.cs index f57bbd4cb0..86bbb26412 100644 --- a/osu.Game/Screens/Play/SkipButton.cs +++ b/osu.Game/Screens/Play/SkipButton.cs @@ -1,32 +1,123 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input; +using osu.Framework.Threading; +using osu.Framework.Timing; using osu.Game.Graphics; -using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Ranking; +using OpenTK; +using OpenTK.Graphics; using OpenTK.Input; +using osu.Framework.Audio.Sample; namespace osu.Game.Screens.Play { - public class SkipButton : TwoLayerButton + public class SkipButton : Container { - public SkipButton() + private readonly double startTime; + public IAdjustableClock AudioClock; + + private Button button; + private Box remainingTimeBox; + + private FadeContainer fadeContainer; + private double displayTime; + + public SkipButton(double startTime) { - Text = @"Skip"; - Icon = FontAwesome.fa_osu_right_o; - Anchor = Anchor.BottomRight; - Origin = Anchor.BottomRight; + AlwaysReceiveInput = true; + + this.startTime = startTime; + + RelativePositionAxes = Axes.Both; + RelativeSizeAxes = Axes.Both; + + Position = new Vector2(0.5f, 0.7f); + Size = new Vector2(1, 0.14f); + + Origin = Anchor.Centre; + } + + protected override bool OnMouseMove(InputState state) + { + fadeContainer.State = Visibility.Visible; + return base.OnMouseMove(state); } [BackgroundDependencyLoader] - private void load(AudioManager audio, OsuColour colours) + private void load(OsuColour colours) { - ActivationSound = audio.Sample.Get(@"Menu/menuhit"); - BackgroundColour = colours.Yellow; - HoverColour = colours.YellowDark; + var baseClock = Clock; + + if (AudioClock != null) + Clock = new FramedClock(AudioClock) { ProcessSourceClockFrames = false }; + + Children = new Drawable[] + { + fadeContainer = new FadeContainer + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + button = new Button + { + Clock = baseClock, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + remainingTimeBox = new Box + { + Height = 5, + RelativeSizeAxes = Axes.X, + Colour = colours.Yellow, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + } + } + } + }; + } + + private const double skip_required_cutoff = 3000; + private const double fade_time = 300; + + private double beginFadeTime => startTime - skip_required_cutoff - fade_time; + + protected override void LoadComplete() + { + base.LoadComplete(); + + if (startTime < skip_required_cutoff) + { + Alpha = 0; + Expire(); + return; + } + + FadeInFromZero(fade_time); + using (BeginAbsoluteSequence(beginFadeTime)) + FadeOut(fade_time); + + button.Action = () => AudioClock?.Seek(startTime - skip_required_cutoff - fade_time); + + displayTime = Time.Current; + + Expire(); + } + + protected override void Update() + { + base.Update(); + remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, EasingTypes.OutQuint); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) @@ -36,11 +127,171 @@ namespace osu.Game.Screens.Play switch (args.Key) { case Key.Space: - TriggerClick(); + button.TriggerClick(); return true; } return base.OnKeyDown(state, args); } + + private class FadeContainer : Container, IStateful + { + private Visibility state; + private ScheduledDelegate scheduledHide; + + public Visibility State + { + get + { + return state; + } + set + { + var lastState = state; + + state = value; + + scheduledHide?.Cancel(); + + switch (state) + { + case Visibility.Visible: + if (lastState == Visibility.Hidden) + FadeIn(500, EasingTypes.OutExpo); + + if (!Hovering) + using (BeginDelayedSequence(1000)) + scheduledHide = Schedule(() => State = Visibility.Hidden); + break; + case Visibility.Hidden: + FadeOut(1000, EasingTypes.OutExpo); + break; + } + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + State = Visibility.Visible; + } + } + + private class Button : Container + { + public Action Action; + private Color4 colourNormal; + private Color4 colourHover; + private Box box; + private FillFlowContainer flow; + private Box background; + private AspectContainer aspect; + private SampleChannel activationSound; + + public Button() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + activationSound = audio.Sample.Get(@"Menu/menuhit"); + + colourNormal = colours.Yellow; + colourHover = colours.YellowDark; + + Children = new Drawable[] + { + background = new Box + { + Alpha = 0.2f, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + }, + aspect = new AspectContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + Height = 0.6f, + Masking = true, + CornerRadius = 15, + Children = new Drawable[] + { + box = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourNormal, + }, + flow = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + RelativePositionAxes = Axes.Y, + Y = 0.4f, + AutoSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Children = new [] + { + new TextAwesome { Icon = FontAwesome.fa_chevron_right }, + new TextAwesome { Icon = FontAwesome.fa_chevron_right }, + new TextAwesome { Icon = FontAwesome.fa_chevron_right }, + } + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + RelativePositionAxes = Axes.Y, + Y = 0.7f, + TextSize = 12, + Font = @"Exo2.0-Bold", + Origin = Anchor.Centre, + Text = @"SKIP", + }, + } + } + }; + } + + protected override bool OnHover(InputState state) + { + flow.TransformSpacingTo(new Vector2(5), 500, EasingTypes.OutQuint); + box.FadeColour(colourHover, 500, EasingTypes.OutQuint); + background.FadeTo(0.4f, 500, EasingTypes.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + flow.TransformSpacingTo(new Vector2(0), 500, EasingTypes.OutQuint); + box.FadeColour(colourNormal, 500, EasingTypes.OutQuint); + background.FadeTo(0.2f, 500, EasingTypes.OutQuint); + base.OnHoverLost(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + aspect.ScaleTo(0.75f, 2000, EasingTypes.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + aspect.ScaleTo(1, 1000, EasingTypes.OutElastic); + return base.OnMouseUp(state, args); + } + + protected override bool OnClick(InputState state) + { + Action?.Invoke(); + + activationSound.Play(); + + box.FlashColour(Color4.White, 500, EasingTypes.OutQuint); + aspect.ScaleTo(1.2f, 2000, EasingTypes.OutQuint); + return true; + } + } } }