Skip to content

Commit

Permalink
Merge pull request #15122 from peppy/fail-animation-update
Browse files Browse the repository at this point in the history
Add new fail animation to better match new sound effects
  • Loading branch information
bdach authored Oct 15, 2021
2 parents b1989e6 + f6d98e8 commit e925c41
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 51 deletions.
69 changes: 58 additions & 11 deletions osu.Game/Screens/Play/FailAnimation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Utils;
using osu.Game.Audio.Effects;
using osu.Game.Beatmaps;
Expand All @@ -25,14 +26,17 @@ namespace osu.Game.Screens.Play
/// Manage the animation to be applied when a player fails.
/// Single use and automatically disposed after use.
/// </summary>
public class FailAnimation : CompositeDrawable
public class FailAnimation : Container
{
public Action OnComplete;

private readonly DrawableRuleset drawableRuleset;

private readonly BindableDouble trackFreq = new BindableDouble(1);

private Container filters;

private Box failFlash;

private Track track;

private AudioFilter failLowPassFilter;
Expand All @@ -42,9 +46,18 @@ public class FailAnimation : CompositeDrawable

private Sample failSample;

protected override Container<Drawable> Content { get; } = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
};

public FailAnimation(DrawableRuleset drawableRuleset)
{
this.drawableRuleset = drawableRuleset;

RelativeSizeAxes = Axes.Both;
}

[BackgroundDependencyLoader]
Expand All @@ -53,8 +66,26 @@ private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap)
track = beatmap.Value.Track;
failSample = audio.Samples.Get(@"Gameplay/failsound");

AddInternal(failLowPassFilter = new AudioFilter(audio.TrackMixer));
AddInternal(failHighPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass));
AddRangeInternal(new Drawable[]
{
filters = new Container
{
Children = new Drawable[]
{
failLowPassFilter = new AudioFilter(audio.TrackMixer),
failHighPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass),
},
},
Content,
failFlash = new Box
{
Colour = Color4.Red,
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
Depth = float.MinValue,
Alpha = 0
},
});
}

private bool started;
Expand All @@ -81,8 +112,30 @@ public void Start()
track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);

applyToPlayfield(drawableRuleset.Playfield);
drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500);
drawableRuleset.Playfield.HitObjectContainer.FadeOut(duration / 2);

failFlash.FadeOutFromOne(1000);

Content.Masking = true;

Content.Add(new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
Depth = float.MaxValue
});

Content.ScaleTo(0.85f, duration, Easing.OutQuart);
Content.RotateTo(1, duration, Easing.OutQuart);
Content.FadeColour(Color4.Gray, duration);
}

public void RemoveFilters()
{
RemoveInternal(filters);
filters.Dispose();

track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
}

protected override void Update()
Expand Down Expand Up @@ -131,11 +184,5 @@ private void dropOffScreen(DrawableHitObject obj, double failTime, float randomR
obj.MoveTo(originalPosition + new Vector2(0, 400), duration);
}
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
}
}
}
86 changes: 46 additions & 40 deletions osu.Game/Screens/Play/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,17 +230,53 @@ private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game)
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
GameplayClockContainer.Add(rulesetSkinProvider);

rulesetSkinProvider.AddRange(new[]
rulesetSkinProvider.AddRange(new Drawable[]
{
// underlay and gameplay should have access to the skinning sources.
createUnderlayComponents(),
createGameplayComponents(Beatmap.Value, playableBeatmap)
failAnimationLayer = new FailAnimation(DrawableRuleset)
{
OnComplete = onFailComplete,
Children = new[]
{
// underlay and gameplay should have access to the skinning sources.
createUnderlayComponents(),
createGameplayComponents(Beatmap.Value, playableBeatmap)
}
},
FailOverlay = new FailOverlay
{
OnRetry = Restart,
OnQuit = () => PerformExit(true),
},
new HotkeyExitOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;

fadeOut(true);
PerformExit(false);
},
},
});

if (Configuration.AllowRestart)
{
rulesetSkinProvider.Add(new HotkeyRetryOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;

fadeOut(true);
Restart();
},
});
}

// add the overlay components as a separate step as they proxy some elements from the above underlay/gameplay components.
// also give the overlays the ruleset skin provider to allow rulesets to potentially override HUD elements (used to disable combo counters etc.)
// we may want to limit this in the future to disallow rulesets from outright replacing elements the user expects to be there.
rulesetSkinProvider.Add(createOverlayComponents(Beatmap.Value));
failAnimationLayer.Add(createOverlayComponents(Beatmap.Value));

if (!DrawableRuleset.AllowGameplayOverlays)
{
Expand Down Expand Up @@ -375,30 +411,14 @@ private Drawable createOverlayComponents(WorkingBeatmap working)
RequestSkip = () => progressToResults(false),
Alpha = 0
},
FailOverlay = new FailOverlay
{
OnRetry = Restart,
OnQuit = () => PerformExit(true),
},
PauseOverlay = new PauseOverlay
{
OnResume = Resume,
Retries = RestartCount,
OnRetry = Restart,
OnQuit = () => PerformExit(true),
},
new HotkeyExitOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;

fadeOut(true);
PerformExit(false);
},
},
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
}
},
};

if (!Configuration.AllowSkipping || !DrawableRuleset.AllowGameplayOverlays)
Expand All @@ -410,20 +430,6 @@ private Drawable createOverlayComponents(WorkingBeatmap working)
if (GameplayClockContainer is MasterGameplayClockContainer master)
HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.UserPlaybackRate.BindTarget = master.UserPlaybackRate;

if (Configuration.AllowRestart)
{
container.Add(new HotkeyRetryOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;

fadeOut(true);
Restart();
},
});
}

return container;
}

Expand Down Expand Up @@ -541,7 +547,7 @@ protected void PerformExit(bool showDialogFirst)
// if the fail animation is currently in progress, accelerate it (it will show the pause dialog on completion).
if (ValidForResume && HasFailed)
{
failAnimation.FinishTransforms(true);
failAnimationLayer.FinishTransforms(true);
return;
}

Expand Down Expand Up @@ -766,7 +772,7 @@ private void progressToResults(bool withDelay)

protected FailOverlay FailOverlay { get; private set; }

private FailAnimation failAnimation;
private FailAnimation failAnimationLayer;

private bool onFail()
{
Expand All @@ -782,7 +788,7 @@ private bool onFail()
if (PauseOverlay.State.Value == Visibility.Visible)
PauseOverlay.Hide();

failAnimation.Start();
failAnimationLayer.Start();

if (GameplayState.Mods.OfType<IApplicableFailOverride>().Any(m => m.RestartOnFail))
Restart();
Expand Down Expand Up @@ -956,7 +962,7 @@ public override void OnSuspending(IScreen next)
public override bool OnExiting(IScreen next)
{
screenSuspension?.RemoveAndDisposeImmediately();
failAnimation?.RemoveAndDisposeImmediately();
failAnimationLayer?.RemoveFilters();

// if arriving here and the results screen preparation task hasn't run, it's safe to say the user has not completed the beatmap.
if (prepareScoreForDisplayTask == null)
Expand Down

0 comments on commit e925c41

Please sign in to comment.