mirror of
https://github.com/osukey/osukey.git
synced 2025-08-07 16:43:52 +09:00
Add animation on failing
This commit is contained in:
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()
|
public void TestPauseAfterFail()
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||||
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
|
AddUntilStep("fail overlay shown", () => Player.FailOverlayVisible);
|
||||||
|
|
||||||
confirmClockRunning(false);
|
confirmClockRunning(false);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The playfield.
|
/// The playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Playfield Playfield => playfield.Value;
|
public override Playfield Playfield => playfield.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Place to put drawables above hit objects but below UI.
|
/// Place to put drawables above hit objects but below UI.
|
||||||
@ -342,6 +342,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableBool IsPaused = new BindableBool();
|
public readonly BindableBool IsPaused = new BindableBool();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The playfield.
|
||||||
|
/// </summary>
|
||||||
|
public abstract Playfield Playfield { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The frame-stable clock which is being used for playfield display.
|
/// The frame-stable clock which is being used for playfield display.
|
||||||
/// </summary>
|
/// </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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -173,7 +173,8 @@ namespace osu.Game.Screens.Play
|
|||||||
fadeOut(true);
|
fadeOut(true);
|
||||||
Restart();
|
Restart();
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true);
|
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; }
|
protected FailOverlay FailOverlay { get; private set; }
|
||||||
|
|
||||||
|
private FailAnimation failAnimation;
|
||||||
|
|
||||||
private bool onFail()
|
private bool onFail()
|
||||||
{
|
{
|
||||||
if (Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
if (Mods.Value.OfType<IApplicableFailOverride>().Any(m => !m.AllowFail))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GameplayClockContainer.Stop();
|
|
||||||
|
|
||||||
HasFailed = true;
|
HasFailed = true;
|
||||||
|
|
||||||
// There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer)
|
// 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)
|
if (PauseOverlay.State == Visibility.Visible)
|
||||||
PauseOverlay.Hide();
|
PauseOverlay.Hide();
|
||||||
|
|
||||||
|
failAnimation.Start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called back when the transform finishes
|
||||||
|
private void onFailComplete()
|
||||||
|
{
|
||||||
|
GameplayClockContainer.Stop();
|
||||||
|
|
||||||
FailOverlay.Retries = RestartCount;
|
FailOverlay.Retries = RestartCount;
|
||||||
FailOverlay.Show();
|
FailOverlay.Show();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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.
|
// still want to block if we are within the cooldown period and not already paused.
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (HasFailed && ValidForResume && !FailOverlay.IsPresent)
|
||||||
|
// ValidForResume is false when restarting
|
||||||
|
{
|
||||||
|
failAnimation.FinishTransforms(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
GameplayClockContainer.ResetLocalAdjustments();
|
GameplayClockContainer.ResetLocalAdjustments();
|
||||||
|
|
||||||
fadeOut();
|
fadeOut();
|
||||||
|
Reference in New Issue
Block a user