Merge pull request #13330 from nekodex/results-screen-sfx

Add sound effects to the results screen
This commit is contained in:
Dean Herbert 2021-06-10 18:27:17 +09:00 committed by GitHub
commit 4a4a561ca4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 179 additions and 38 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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()

View File

@ -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" />

View File

@ -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>