mirror of
https://github.com/ppy/osu
synced 2025-01-29 17:23:03 +00:00
Merge pull request #13330 from nekodex/results-screen-sfx
Add sound effects to the results screen
This commit is contained in:
commit
4a4a561ca4
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.609.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.609.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new AccuracyCircle(score)
|
new AccuracyCircle(score, true)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -5,14 +5,18 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
||||||
@ -79,13 +83,28 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
private Container<RankBadge> badges;
|
private Container<RankBadge> badges;
|
||||||
private RankText rankText;
|
private RankText rankText;
|
||||||
|
|
||||||
public AccuracyCircle(ScoreInfo score)
|
private PoolableSkinnableSample scoreTickSound;
|
||||||
|
private PoolableSkinnableSample badgeTickSound;
|
||||||
|
private PoolableSkinnableSample badgeMaxSound;
|
||||||
|
private PoolableSkinnableSample swooshUpSound;
|
||||||
|
private PoolableSkinnableSample rankImpactSound;
|
||||||
|
private PoolableSkinnableSample rankApplauseSound;
|
||||||
|
|
||||||
|
private readonly Bindable<double> tickPlaybackRate = new Bindable<double>();
|
||||||
|
|
||||||
|
private double lastTickPlaybackTime;
|
||||||
|
private bool isTicking;
|
||||||
|
|
||||||
|
private readonly bool withFlair;
|
||||||
|
|
||||||
|
public AccuracyCircle(ScoreInfo score, bool withFlair = false)
|
||||||
{
|
{
|
||||||
this.score = score;
|
this.score = score;
|
||||||
|
this.withFlair = withFlair;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(AudioManager audio)
|
private void load(GameHost host)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -204,14 +223,19 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
},
|
},
|
||||||
rankText = new RankText(score.Rank)
|
rankText = new RankText(score.Rank)
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private ScoreRank getRank(ScoreRank rank)
|
if (withFlair)
|
||||||
{
|
{
|
||||||
foreach (var mod in score.Mods.OfType<IApplicableToScoreProcessor>())
|
AddRangeInternal(new Drawable[]
|
||||||
rank = mod.AdjustRank(rank, score.Accuracy);
|
{
|
||||||
|
rankImpactSound = new PoolableSkinnableSample(new SampleInfo(impactSampleName)),
|
||||||
return rank;
|
rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(@"applause", applauseSampleName)),
|
||||||
|
scoreTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/score-tick")),
|
||||||
|
badgeTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink")),
|
||||||
|
badgeMaxSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink-max")),
|
||||||
|
swooshUpSound = new PoolableSkinnableSample(new SampleInfo(@"Results/swoosh-up")),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -220,33 +244,170 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|||||||
|
|
||||||
this.ScaleTo(0).Then().ScaleTo(1, APPEAR_DURATION, Easing.OutQuint);
|
this.ScaleTo(0).Then().ScaleTo(1, APPEAR_DURATION, Easing.OutQuint);
|
||||||
|
|
||||||
using (BeginDelayedSequence(RANK_CIRCLE_TRANSFORM_DELAY, true))
|
if (withFlair)
|
||||||
|
{
|
||||||
|
const double swoosh_pre_delay = 443f;
|
||||||
|
const double swoosh_volume = 0.4f;
|
||||||
|
|
||||||
|
this.Delay(swoosh_pre_delay).Schedule(() =>
|
||||||
|
{
|
||||||
|
swooshUpSound.VolumeTo(swoosh_volume);
|
||||||
|
swooshUpSound.Play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(RANK_CIRCLE_TRANSFORM_DELAY))
|
||||||
innerMask.FillTo(1f, RANK_CIRCLE_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
innerMask.FillTo(1f, RANK_CIRCLE_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
||||||
|
|
||||||
using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY, true))
|
using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY))
|
||||||
{
|
{
|
||||||
double targetAccuracy = score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH ? 1 : Math.Min(1 - virtual_ss_percentage, score.Accuracy);
|
double targetAccuracy = score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH ? 1 : Math.Min(1 - virtual_ss_percentage, score.Accuracy);
|
||||||
|
|
||||||
accuracyCircle.FillTo(targetAccuracy, ACCURACY_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
accuracyCircle.FillTo(targetAccuracy, ACCURACY_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
||||||
|
|
||||||
|
if (withFlair)
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
const double score_tick_debounce_rate_start = 18f;
|
||||||
|
const double score_tick_debounce_rate_end = 300f;
|
||||||
|
const double score_tick_volume_start = 0.6f;
|
||||||
|
const double score_tick_volume_end = 1.0f;
|
||||||
|
|
||||||
|
this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_start);
|
||||||
|
this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
||||||
|
|
||||||
|
scoreTickSound.FrequencyTo(1 + targetAccuracy, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
||||||
|
scoreTickSound.VolumeTo(score_tick_volume_start).Then().VolumeTo(score_tick_volume_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
||||||
|
|
||||||
|
isTicking = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int badgeNum = 0;
|
||||||
|
|
||||||
foreach (var badge in badges)
|
foreach (var badge in badges)
|
||||||
{
|
{
|
||||||
if (badge.Accuracy > score.Accuracy)
|
if (badge.Accuracy > score.Accuracy)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(1 - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION, true))
|
using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(1 - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION))
|
||||||
{
|
{
|
||||||
badge.Appear();
|
badge.Appear();
|
||||||
|
|
||||||
|
if (withFlair)
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
var dink = badgeNum < badges.Count - 1 ? badgeTickSound : badgeMaxSound;
|
||||||
|
|
||||||
|
dink.FrequencyTo(1 + badgeNum++ * 0.05);
|
||||||
|
dink.Play();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using (BeginDelayedSequence(TEXT_APPEAR_DELAY, true))
|
using (BeginDelayedSequence(TEXT_APPEAR_DELAY))
|
||||||
{
|
{
|
||||||
rankText.Appear();
|
rankText.Appear();
|
||||||
|
|
||||||
|
if (!withFlair) return;
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
isTicking = false;
|
||||||
|
rankImpactSound.Play();
|
||||||
|
});
|
||||||
|
|
||||||
|
const double applause_pre_delay = 545f;
|
||||||
|
const double applause_volume = 0.8f;
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(applause_pre_delay))
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
rankApplauseSound.VolumeTo(applause_volume);
|
||||||
|
rankApplauseSound.Play();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (isTicking && Clock.CurrentTime - lastTickPlaybackTime >= tickPlaybackRate.Value)
|
||||||
|
{
|
||||||
|
scoreTickSound?.Play();
|
||||||
|
lastTickPlaybackTime = Clock.CurrentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string applauseSampleName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (score.Rank)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case ScoreRank.D:
|
||||||
|
return @"Results/applause-d";
|
||||||
|
|
||||||
|
case ScoreRank.C:
|
||||||
|
return @"Results/applause-c";
|
||||||
|
|
||||||
|
case ScoreRank.B:
|
||||||
|
return @"Results/applause-b";
|
||||||
|
|
||||||
|
case ScoreRank.A:
|
||||||
|
return @"Results/applause-a";
|
||||||
|
|
||||||
|
case ScoreRank.S:
|
||||||
|
case ScoreRank.SH:
|
||||||
|
case ScoreRank.X:
|
||||||
|
case ScoreRank.XH:
|
||||||
|
return @"Results/applause-s";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string impactSampleName
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (score.Rank)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case ScoreRank.D:
|
||||||
|
return @"Results/rank-impact-fail-d";
|
||||||
|
|
||||||
|
case ScoreRank.C:
|
||||||
|
case ScoreRank.B:
|
||||||
|
return @"Results/rank-impact-fail";
|
||||||
|
|
||||||
|
case ScoreRank.A:
|
||||||
|
case ScoreRank.S:
|
||||||
|
case ScoreRank.SH:
|
||||||
|
return @"Results/rank-impact-pass";
|
||||||
|
|
||||||
|
case ScoreRank.X:
|
||||||
|
case ScoreRank.XH:
|
||||||
|
return @"Results/rank-impact-pass-ss";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScoreRank getRank(ScoreRank rank)
|
||||||
|
{
|
||||||
|
foreach (var mod in score.Mods.OfType<IApplicableToScoreProcessor>())
|
||||||
|
rank = mod.AdjustRank(rank, score.Accuracy);
|
||||||
|
|
||||||
|
return rank;
|
||||||
|
}
|
||||||
|
|
||||||
private double inverseEasing(Easing easing, double targetValue)
|
private double inverseEasing(Easing easing, double targetValue)
|
||||||
{
|
{
|
||||||
double test = 0;
|
double test = 0;
|
||||||
|
@ -122,7 +122,7 @@ namespace osu.Game.Screens.Ranking.Expanded
|
|||||||
Margin = new MarginPadding { Top = 40 },
|
Margin = new MarginPadding { Top = 40 },
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 230,
|
Height = 230,
|
||||||
Child = new AccuracyCircle(score)
|
Child = new AccuracyCircle(score, withFlair)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Audio;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -20,20 +19,13 @@ using osu.Game.Input.Bindings;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking.Expanded.Accuracy;
|
|
||||||
using osu.Game.Screens.Ranking.Statistics;
|
using osu.Game.Screens.Ranking.Statistics;
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Ranking
|
namespace osu.Game.Screens.Ranking
|
||||||
{
|
{
|
||||||
public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>
|
public abstract class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Delay before the default applause sound should be played, in order to match the grade display timing in <see cref="AccuracyCircle"/>.
|
|
||||||
/// </summary>
|
|
||||||
public const double APPLAUSE_DELAY = AccuracyCircle.ACCURACY_TRANSFORM_DELAY + AccuracyCircle.TEXT_APPEAR_DELAY + ScorePanel.RESIZE_DURATION + ScorePanel.TOP_LAYER_EXPAND_DELAY - 1440;
|
|
||||||
|
|
||||||
protected const float BACKGROUND_BLUR = 20;
|
protected const float BACKGROUND_BLUR = 20;
|
||||||
private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y;
|
private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y;
|
||||||
|
|
||||||
@ -64,8 +56,6 @@ namespace osu.Game.Screens.Ranking
|
|||||||
private readonly bool allowRetry;
|
private readonly bool allowRetry;
|
||||||
private readonly bool allowWatchingReplay;
|
private readonly bool allowWatchingReplay;
|
||||||
|
|
||||||
private SkinnableSound applauseSound;
|
|
||||||
|
|
||||||
protected ResultsScreen(ScoreInfo score, bool allowRetry, bool allowWatchingReplay = true)
|
protected ResultsScreen(ScoreInfo score, bool allowRetry, bool allowWatchingReplay = true)
|
||||||
{
|
{
|
||||||
Score = score;
|
Score = score;
|
||||||
@ -156,13 +146,6 @@ namespace osu.Game.Screens.Ranking
|
|||||||
bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable);
|
bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable);
|
||||||
|
|
||||||
ScorePanelList.AddScore(Score, shouldFlair);
|
ScorePanelList.AddScore(Score, shouldFlair);
|
||||||
|
|
||||||
if (shouldFlair)
|
|
||||||
{
|
|
||||||
AddInternal(applauseSound = Score.Rank >= ScoreRank.A
|
|
||||||
? new SkinnableSound(new SampleInfo("Results/rankpass", "applause"))
|
|
||||||
: new SkinnableSound(new SampleInfo("Results/rankfail")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allowWatchingReplay)
|
if (allowWatchingReplay)
|
||||||
@ -200,9 +183,6 @@ namespace osu.Game.Screens.Ranking
|
|||||||
api.Queue(req);
|
api.Queue(req);
|
||||||
|
|
||||||
statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true);
|
statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true);
|
||||||
|
|
||||||
using (BeginDelayedSequence(APPLAUSE_DELAY))
|
|
||||||
Schedule(() => applauseSound?.Play());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.609.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.609.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.4.0" />
|
<PackageReference Include="Sentry" Version="3.4.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.609.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.609.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user