mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 06:03:08 +08:00
Add animation on failing
This commit is contained in:
parent
76474987ef
commit
9c214c3f0e
50
osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
Normal file
50
osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs
Normal file
@ -0,0 +1,50 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneFailAnimation : AllPlayersTestScene
|
||||
{
|
||||
protected override Player CreatePlayer(Ruleset ruleset)
|
||||
{
|
||||
Mods.Value = Array.Empty<Mod>();
|
||||
return new FailPlayer();
|
||||
}
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(AllPlayersTestScene),
|
||||
typeof(TestPlayer),
|
||||
typeof(Player),
|
||||
};
|
||||
|
||||
protected override void AddCheckSteps()
|
||||
{
|
||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||
AddUntilStep("wait for fail overlay", () => ((FailPlayer)Player).FailOverlay.State == Visibility.Visible);
|
||||
}
|
||||
|
||||
private class FailPlayer : TestPlayer
|
||||
{
|
||||
public new FailOverlay FailOverlay => base.FailOverlay;
|
||||
|
||||
public FailPlayer()
|
||||
: base(false, false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
ScoreProcessor.FailConditions += _ => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void TestPauseAfterFail()
|
||||
{
|
||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
|
||||
AddUntilStep("fail overlay shown", () => Player.FailOverlayVisible);
|
||||
|
||||
confirmClockRunning(false);
|
||||
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
/// </summary>
|
||||
public Playfield Playfield => playfield.Value;
|
||||
public override Playfield Playfield => playfield.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Place to put drawables above hit objects but below UI.
|
||||
@ -342,6 +342,11 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public readonly BindableBool IsPaused = new BindableBool();
|
||||
|
||||
/// <summary>
|
||||
/// The playfield.
|
||||
/// </summary>
|
||||
public abstract Playfield Playfield { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The frame-stable clock which is being used for playfield display.
|
||||
/// </summary>
|
||||
|
113
osu.Game/Screens/Play/FailAnimation.cs
Normal file
113
osu.Game/Screens/Play/FailAnimation.cs
Normal file
@ -0,0 +1,113 @@
|
||||
// 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.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// Manage the animation to be applied when a player fails.
|
||||
/// Single file; automatically disposed after use.
|
||||
/// </summary>
|
||||
public class FailAnimation : Component
|
||||
{
|
||||
public Action OnComplete;
|
||||
|
||||
private readonly DrawableRuleset drawableRuleset;
|
||||
|
||||
private readonly BindableDouble trackFreq = new BindableDouble(1);
|
||||
|
||||
private Track track;
|
||||
|
||||
private const float duration = 2500;
|
||||
|
||||
private SampleChannel failSample;
|
||||
|
||||
public FailAnimation(DrawableRuleset drawableRuleset)
|
||||
{
|
||||
this.drawableRuleset = drawableRuleset;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
track = beatmap.Value.Track;
|
||||
failSample = audio.Samples.Get(@"Gameplay/failsound");
|
||||
}
|
||||
|
||||
private bool started;
|
||||
|
||||
/// <summary>
|
||||
/// Start the fail animation playing.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if started more than once.</exception>
|
||||
public void Start()
|
||||
{
|
||||
if (started) throw new InvalidOperationException("Animation cannot be started more than once.");
|
||||
|
||||
started = true;
|
||||
|
||||
failSample.Play();
|
||||
|
||||
this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ =>
|
||||
{
|
||||
OnComplete?.Invoke();
|
||||
Expire();
|
||||
});
|
||||
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
|
||||
applyToPlayfield(drawableRuleset.Playfield);
|
||||
drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500);
|
||||
drawableRuleset.Playfield.HitObjectContainer.FadeOut(duration / 2);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!started)
|
||||
return;
|
||||
|
||||
applyToPlayfield(drawableRuleset.Playfield);
|
||||
}
|
||||
|
||||
private readonly List<DrawableHitObject> appliedObjects = new List<DrawableHitObject>();
|
||||
|
||||
private void applyToPlayfield(Playfield playfield)
|
||||
{
|
||||
foreach (var nested in playfield.NestedPlayfields)
|
||||
applyToPlayfield(nested);
|
||||
|
||||
foreach (DrawableHitObject obj in playfield.HitObjectContainer.AliveObjects)
|
||||
{
|
||||
if (appliedObjects.Contains(obj))
|
||||
continue;
|
||||
|
||||
obj.RotateTo(RNG.NextSingle(-90, 90), duration);
|
||||
obj.ScaleTo(obj.Scale * 0.5f, duration);
|
||||
obj.MoveToOffset(new Vector2(0, 400), duration);
|
||||
appliedObjects.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
@ -173,7 +173,8 @@ namespace osu.Game.Screens.Play
|
||||
fadeOut(true);
|
||||
Restart();
|
||||
},
|
||||
}
|
||||
},
|
||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
||||
};
|
||||
|
||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true);
|
||||
@ -345,13 +346,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected FailOverlay FailOverlay { get; private set; }
|
||||
|
||||
private FailAnimation failAnimation;
|
||||
|
||||
private bool onFail()
|
||||
{
|
||||
if (Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
||||
return false;
|
||||
|
||||
GameplayClockContainer.Stop();
|
||||
|
||||
HasFailed = true;
|
||||
|
||||
// There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer)
|
||||
@ -360,9 +361,17 @@ namespace osu.Game.Screens.Play
|
||||
if (PauseOverlay.State == Visibility.Visible)
|
||||
PauseOverlay.Hide();
|
||||
|
||||
failAnimation.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called back when the transform finishes
|
||||
private void onFailComplete()
|
||||
{
|
||||
GameplayClockContainer.Stop();
|
||||
|
||||
FailOverlay.Retries = RestartCount;
|
||||
FailOverlay.Show();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -489,6 +498,13 @@ namespace osu.Game.Screens.Play
|
||||
// still want to block if we are within the cooldown period and not already paused.
|
||||
return true;
|
||||
|
||||
if (HasFailed && ValidForResume && !FailOverlay.IsPresent)
|
||||
// ValidForResume is false when restarting
|
||||
{
|
||||
failAnimation.FinishTransforms(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
GameplayClockContainer.ResetLocalAdjustments();
|
||||
|
||||
fadeOut();
|
||||
|
Loading…
Reference in New Issue
Block a user