2022-06-07 13:40:21 +08:00
// 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 ;
2022-06-07 17:11:54 +08:00
using System.Diagnostics ;
2022-06-07 19:28:42 +08:00
using System.Linq ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Allocation ;
2022-06-10 15:22:34 +08:00
using osu.Framework.Bindables ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Configuration ;
using osu.Framework.Extensions.Color4Extensions ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Colour ;
using osu.Framework.Graphics.Containers ;
2022-06-07 21:52:24 +08:00
using osu.Framework.Graphics.Effects ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Graphics.Shapes ;
2022-06-07 19:02:26 +08:00
using osu.Framework.Graphics.Sprites ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Input.Events ;
2022-06-07 18:03:26 +08:00
using osu.Framework.Platform ;
using osu.Framework.Platform.Windows ;
2022-06-07 13:40:21 +08:00
using osu.Framework.Screens ;
using osu.Framework.Utils ;
using osu.Game.Graphics ;
using osu.Game.Graphics.Containers ;
2022-06-07 21:52:24 +08:00
using osu.Game.Graphics.Sprites ;
2022-06-07 13:40:21 +08:00
using osu.Game.Overlays ;
2022-06-10 19:04:51 +08:00
using osu.Game.Overlays.Settings ;
2022-06-07 13:40:21 +08:00
using osuTK ;
using osuTK.Input ;
2022-06-07 22:10:08 +08:00
namespace osu.Game.Screens.Utility
2022-06-07 13:40:21 +08:00
{
2022-06-10 19:04:51 +08:00
[Cached]
2022-06-07 22:34:19 +08:00
public class LatencyCertifierScreen : OsuScreen
2022-06-07 13:40:21 +08:00
{
private FrameSync previousFrameSyncMode ;
2022-06-07 18:03:26 +08:00
private double previousActiveHz ;
2022-06-07 13:40:21 +08:00
2022-06-07 13:51:16 +08:00
private readonly OsuTextFlowContainer statusText ;
2022-06-07 13:40:21 +08:00
public override bool HideOverlaysOnEnter = > true ;
public override float BackgroundParallaxAmount = > 0 ;
2022-06-10 19:08:53 +08:00
private readonly LinkFlowContainer explanatoryText ;
2022-06-07 14:21:19 +08:00
2022-06-07 19:28:42 +08:00
private readonly Container < LatencyArea > mainArea ;
2022-06-07 13:51:16 +08:00
private readonly Container resultsArea ;
2022-06-07 13:40:21 +08:00
2022-06-11 20:27:44 +08:00
public readonly BindableDouble SampleBPM = new BindableDouble ( 120 ) { MinValue = 60 , MaxValue = 300 , Precision = 1 } ;
2022-06-10 19:21:03 +08:00
public readonly BindableDouble SampleApproachRate = new BindableDouble ( 9 ) { MinValue = 5 , MaxValue = 12 , Precision = 0.1 } ;
public readonly BindableFloat SampleVisualSpacing = new BindableFloat ( 0.5f ) { MinValue = 0f , MaxValue = 1 , Precision = 0.1f } ;
2022-06-10 19:04:51 +08:00
2022-06-07 18:03:26 +08:00
/// <summary>
/// The rate at which the game host should attempt to run.
/// </summary>
private const int target_host_update_frames = 4000 ;
2022-06-07 13:40:21 +08:00
[Cached]
private readonly OverlayColourProvider overlayColourProvider = new OverlayColourProvider ( OverlayColourScheme . Orange ) ;
2022-06-07 17:11:54 +08:00
[Resolved]
private OsuColour colours { get ; set ; } = null ! ;
2022-06-07 13:40:21 +08:00
[Resolved]
private FrameworkConfigManager config { get ; set ; } = null ! ;
2022-06-10 17:18:18 +08:00
public readonly Bindable < LatencyVisualMode > VisualMode = new Bindable < LatencyVisualMode > ( ) ;
2022-06-10 15:22:34 +08:00
2022-06-07 17:11:54 +08:00
private const int rounds_to_complete = 5 ;
2022-06-07 21:52:24 +08:00
private const int rounds_to_complete_certified = 20 ;
2022-06-10 14:19:10 +08:00
/// <summary>
/// Whether we are now in certification mode and decreasing difficulty.
/// </summary>
private bool isCertifying ;
private int totalRoundForNextResultsScreen = > isCertifying ? rounds_to_complete_certified : rounds_to_complete ;
2022-06-10 13:23:24 +08:00
private int attemptsAtCurrentDifficulty ;
private int correctAtCurrentDifficulty ;
2022-06-07 17:11:54 +08:00
2022-06-10 14:19:10 +08:00
public int DifficultyLevel { get ; private set ; } = 1 ;
2022-06-07 17:11:54 +08:00
2022-06-07 18:43:33 +08:00
private double lastPoll ;
private int pollingMax ;
2022-06-11 20:32:02 +08:00
private readonly FillFlowContainer settings ;
2022-06-07 18:03:26 +08:00
[Resolved]
private GameHost host { get ; set ; } = null ! ;
2022-06-10 17:20:15 +08:00
[Resolved]
private MusicController musicController { get ; set ; } = null ! ;
2022-06-07 22:34:19 +08:00
public LatencyCertifierScreen ( )
2022-06-07 13:40:21 +08:00
{
InternalChildren = new Drawable [ ]
{
2022-06-07 13:51:16 +08:00
new Box
{
Colour = overlayColourProvider . Background6 ,
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 19:28:42 +08:00
mainArea = new Container < LatencyArea >
2022-06-07 13:40:21 +08:00
{
RelativeSizeAxes = Axes . Both ,
} ,
// Make sure the edge between the two comparisons can't be used to ascertain latency.
new Box
{
Name = "separator" ,
Colour = ColourInfo . GradientHorizontal ( overlayColourProvider . Background6 , overlayColourProvider . Background6 . Opacity ( 0 ) ) ,
2022-06-07 19:42:19 +08:00
Width = 100 ,
2022-06-07 13:40:21 +08:00
RelativeSizeAxes = Axes . Y ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopLeft ,
} ,
new Box
{
Name = "separator" ,
Colour = ColourInfo . GradientHorizontal ( overlayColourProvider . Background6 . Opacity ( 0 ) , overlayColourProvider . Background6 ) ,
2022-06-07 19:42:19 +08:00
Width = 100 ,
2022-06-07 13:40:21 +08:00
RelativeSizeAxes = Axes . Y ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopRight ,
} ,
2022-06-11 20:32:02 +08:00
settings = new FillFlowContainer
2022-06-07 14:21:19 +08:00
{
2022-06-10 19:04:51 +08:00
Name = "Settings" ,
AutoSizeAxes = Axes . Y ,
2022-06-10 19:08:53 +08:00
Width = 800 ,
2022-06-10 19:04:51 +08:00
Padding = new MarginPadding ( 10 ) ,
Spacing = new Vector2 ( 2 ) ,
Direction = FillDirection . Vertical ,
2022-06-07 17:11:54 +08:00
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
2022-06-10 19:33:01 +08:00
Children = new Drawable [ ]
2022-06-10 19:04:51 +08:00
{
2022-06-10 19:33:01 +08:00
explanatoryText = new LinkFlowContainer ( cp = > cp . Font = OsuFont . Default . With ( size : 20 ) )
{
AutoSizeAxes = Axes . Y ,
RelativeSizeAxes = Axes . X ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
TextAnchor = Anchor . TopCentre ,
} ,
new SettingsSlider < double >
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
RelativeSizeAxes = Axes . None ,
Width = 400 ,
LabelText = "bpm" ,
Current = SampleBPM
} ,
new SettingsSlider < float >
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
RelativeSizeAxes = Axes . None ,
Width = 400 ,
LabelText = "visual spacing" ,
Current = SampleVisualSpacing
} ,
new SettingsSlider < double >
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
RelativeSizeAxes = Axes . None ,
Width = 400 ,
LabelText = "approach rate" ,
Current = SampleApproachRate
} ,
2022-06-10 19:04:51 +08:00
} ,
2022-06-07 14:21:19 +08:00
} ,
2022-06-07 21:52:24 +08:00
resultsArea = new Container
{
RelativeSizeAxes = Axes . Both ,
} ,
2022-06-07 13:51:16 +08:00
statusText = new OsuTextFlowContainer ( cp = > cp . Font = OsuFont . Default . With ( size : 40 ) )
2022-06-07 13:40:21 +08:00
{
2022-06-07 17:11:54 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
2022-06-07 13:51:16 +08:00
TextAnchor = Anchor . TopCentre ,
2022-06-07 19:02:26 +08:00
Y = 150 ,
2022-06-07 13:51:16 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
} ,
2022-06-07 13:40:21 +08:00
} ;
2022-06-10 19:08:53 +08:00
explanatoryText . AddParagraph ( @"Welcome to the latency certifier!" ) ;
explanatoryText . AddParagraph ( @"Do whatever you need to try and perceive the difference in latency, then choose your best side. Read more about the methodology " ) ;
2022-06-11 20:34:00 +08:00
explanatoryText . AddLink ( "here" , "https://github.com/ppy/osu/wiki/Latency-and-unlimited-frame-rates#methodology" ) ;
2022-06-10 19:08:53 +08:00
explanatoryText . AddParagraph ( @"Use the arrow keys or Z/X/F/J to control the display." ) ;
explanatoryText . AddParagraph ( @"Tab key to change focus. Space to change display mode" ) ;
2022-06-07 13:40:21 +08:00
}
2022-06-07 18:43:33 +08:00
protected override bool OnMouseMove ( MouseMoveEvent e )
{
2022-06-11 19:58:16 +08:00
if ( lastPoll > 0 & & Clock . CurrentTime ! = lastPoll )
2022-06-07 18:43:33 +08:00
pollingMax = ( int ) Math . Max ( pollingMax , 1000 / ( Clock . CurrentTime - lastPoll ) ) ;
lastPoll = Clock . CurrentTime ;
return base . OnMouseMove ( e ) ;
}
2022-06-07 13:40:21 +08:00
public override void OnEntering ( ScreenTransitionEvent e )
{
base . OnEntering ( e ) ;
previousFrameSyncMode = config . Get < FrameSync > ( FrameworkSetting . FrameSync ) ;
2022-06-07 18:03:26 +08:00
previousActiveHz = host . UpdateThread . ActiveHz ;
2022-06-07 13:40:21 +08:00
config . SetValue ( FrameworkSetting . FrameSync , FrameSync . Unlimited ) ;
2022-06-07 18:03:26 +08:00
host . UpdateThread . ActiveHz = target_host_update_frames ;
2022-06-07 22:25:45 +08:00
host . AllowBenchmarkUnlimitedFrames = true ;
2022-06-10 17:20:15 +08:00
musicController . Stop ( ) ;
2022-06-07 13:40:21 +08:00
}
public override bool OnExiting ( ScreenExitEvent e )
{
2022-06-07 22:25:45 +08:00
host . AllowBenchmarkUnlimitedFrames = false ;
2022-06-07 13:40:21 +08:00
config . SetValue ( FrameworkSetting . FrameSync , previousFrameSyncMode ) ;
2022-06-07 18:03:26 +08:00
host . UpdateThread . ActiveHz = previousActiveHz ;
2022-06-07 13:40:21 +08:00
return base . OnExiting ( e ) ;
}
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
loadNextRound ( ) ;
}
2022-06-07 19:28:42 +08:00
protected override bool OnKeyDown ( KeyDownEvent e )
{
switch ( e . Key )
{
2022-06-10 15:22:34 +08:00
case Key . Space :
2022-06-11 20:36:41 +08:00
int availableModes = Enum . GetValues ( typeof ( LatencyVisualMode ) ) . Length ;
VisualMode . Value = ( LatencyVisualMode ) ( ( ( int ) VisualMode . Value + 1 ) % availableModes ) ;
2022-06-10 15:22:34 +08:00
return true ;
2022-06-07 19:28:42 +08:00
case Key . Tab :
var firstArea = mainArea . FirstOrDefault ( a = > ! a . IsActiveArea . Value ) ;
if ( firstArea ! = null )
firstArea . IsActiveArea . Value = true ;
return true ;
}
return base . OnKeyDown ( e ) ;
}
2022-06-07 13:40:21 +08:00
private void showResults ( )
{
2022-06-07 13:51:16 +08:00
mainArea . Clear ( ) ;
2022-06-10 19:33:01 +08:00
resultsArea . Clear ( ) ;
2022-06-11 20:32:02 +08:00
settings . Hide ( ) ;
2022-06-07 13:51:16 +08:00
2022-06-07 23:36:19 +08:00
var displayMode = host . Window ? . CurrentDisplayMode . Value ;
2022-06-07 18:03:26 +08:00
string exclusive = "unknown" ;
2022-09-22 20:01:23 +08:00
if ( host . Renderer is IWindowsRenderer windowsRenderer )
exclusive = windowsRenderer . FullscreenCapability . ToString ( ) ;
2022-06-07 18:03:26 +08:00
statusText . Clear ( ) ;
2022-06-10 14:19:10 +08:00
float successRate = ( float ) correctAtCurrentDifficulty / attemptsAtCurrentDifficulty ;
2022-06-07 21:52:24 +08:00
bool isPass = successRate = = 1 ;
2022-06-07 18:03:26 +08:00
2022-06-10 14:19:10 +08:00
statusText . AddParagraph ( $"You scored {correctAtCurrentDifficulty} out of {attemptsAtCurrentDifficulty} ({successRate:0%})!" , cp = > cp . Colour = isPass ? colours . Green : colours . Red ) ;
statusText . AddParagraph ( $"Level {DifficultyLevel} ({mapDifficultyToTargetFrameRate(DifficultyLevel):N0} Hz)" ,
2022-06-07 18:43:33 +08:00
cp = > cp . Font = OsuFont . Default . With ( size : 24 ) ) ;
2022-06-07 18:03:26 +08:00
2022-06-07 19:02:26 +08:00
statusText . AddParagraph ( string . Empty ) ;
statusText . AddParagraph ( string . Empty ) ;
statusText . AddIcon ( isPass ? FontAwesome . Regular . CheckCircle : FontAwesome . Regular . TimesCircle , cp = > cp . Colour = isPass ? colours . Green : colours . Red ) ;
statusText . AddParagraph ( string . Empty ) ;
2022-06-10 14:19:10 +08:00
if ( ! isPass & & DifficultyLevel > 1 )
2022-06-07 21:52:24 +08:00
{
2022-06-10 14:19:10 +08:00
statusText . AddParagraph ( "To complete certification, the difficulty level will now decrease until you can get 20 rounds correct in a row!" ,
2022-06-10 13:23:24 +08:00
cp = > cp . Font = OsuFont . Default . With ( size : 24 , weight : FontWeight . SemiBold ) ) ;
2022-06-07 21:52:24 +08:00
statusText . AddParagraph ( string . Empty ) ;
}
2022-06-10 13:38:04 +08:00
statusText . AddParagraph ( $"Polling: {pollingMax} Hz Monitor: {displayMode?.RefreshRate ?? 0:N0} Hz Exclusive: {exclusive}" ,
2022-06-10 13:23:24 +08:00
cp = > cp . Font = OsuFont . Default . With ( size : 15 , weight : FontWeight . SemiBold ) ) ;
2022-06-07 19:02:26 +08:00
2022-06-10 13:38:04 +08:00
statusText . AddParagraph ( $"Input: {host.InputThread.Clock.FramesPerSecond} Hz "
+ $"Update: {host.UpdateThread.Clock.FramesPerSecond} Hz "
+ $"Draw: {host.DrawThread.Clock.FramesPerSecond} Hz"
2022-06-09 18:26:24 +08:00
, cp = > cp . Font = OsuFont . Default . With ( size : 15 , weight : FontWeight . SemiBold ) ) ;
2022-06-07 18:03:26 +08:00
2022-06-10 14:19:10 +08:00
if ( isCertifying & & isPass )
2022-06-07 21:52:24 +08:00
{
2022-06-10 14:19:10 +08:00
showCertifiedScreen ( ) ;
2022-06-07 21:52:24 +08:00
return ;
}
2022-06-07 18:03:26 +08:00
string cannotIncreaseReason = string . Empty ;
2022-06-10 14:19:10 +08:00
if ( mapDifficultyToTargetFrameRate ( DifficultyLevel + 1 ) > target_host_update_frames )
2022-06-10 13:25:15 +08:00
cannotIncreaseReason = "You've reached the maximum level." ;
2022-06-10 14:19:10 +08:00
else if ( mapDifficultyToTargetFrameRate ( DifficultyLevel + 1 ) > Clock . FramesPerSecond )
2022-06-07 18:03:26 +08:00
cannotIncreaseReason = "Game is not running fast enough to test this level" ;
2022-06-07 13:51:16 +08:00
2022-06-10 14:19:10 +08:00
FillFlowContainer buttonFlow ;
resultsArea . Add ( buttonFlow = new FillFlowContainer
2022-06-07 13:40:21 +08:00
{
2022-06-07 19:02:26 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Anchor = Anchor . BottomLeft ,
Origin = Anchor . BottomLeft ,
2022-06-07 17:11:54 +08:00
Spacing = new Vector2 ( 20 ) ,
2022-06-07 19:02:26 +08:00
Padding = new MarginPadding ( 20 ) ,
2022-06-10 14:19:10 +08:00
} ) ;
if ( isPass )
{
buttonFlow . Add ( new ButtonWithKeyBind ( Key . Enter )
2022-06-07 13:40:21 +08:00
{
2022-06-10 14:19:10 +08:00
Text = "Continue to next level" ,
BackgroundColour = colours . Green ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Action = ( ) = > changeDifficulty ( DifficultyLevel + 1 ) ,
Enabled = { Value = string . IsNullOrEmpty ( cannotIncreaseReason ) } ,
TooltipText = cannotIncreaseReason
} ) ;
}
else
{
if ( DifficultyLevel = = 1 )
{
buttonFlow . Add ( new ButtonWithKeyBind ( Key . Enter )
2022-06-07 17:11:54 +08:00
{
2022-06-10 14:19:10 +08:00
Text = "Retry" ,
TooltipText = "Are you even trying..?" ,
BackgroundColour = colours . Pink2 ,
2022-06-07 17:11:54 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2022-06-10 14:19:10 +08:00
Action = ( ) = >
{
isCertifying = false ;
changeDifficulty ( 1 ) ;
} ,
} ) ;
}
else
{
buttonFlow . Add ( new ButtonWithKeyBind ( Key . Enter )
2022-06-07 21:52:24 +08:00
{
2022-06-10 14:19:10 +08:00
Text = "Begin certification at last level" ,
BackgroundColour = colours . Yellow ,
2022-06-07 21:52:24 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Action = ( ) = >
{
2022-06-10 14:19:10 +08:00
isCertifying = true ;
changeDifficulty ( DifficultyLevel - 1 ) ;
2022-06-07 21:52:24 +08:00
} ,
2022-06-10 15:22:34 +08:00
TooltipText = isPass
? $"Chain {rounds_to_complete_certified} rounds to confirm your perception!"
: "You've reached your limits. Go to the previous level to complete certification!" ,
2022-06-10 14:19:10 +08:00
} ) ;
}
}
}
private void showCertifiedScreen ( )
{
Drawable background ;
Drawable certifiedText ;
resultsArea . AddRange ( new [ ]
{
background = new Box
{
Colour = overlayColourProvider . Background4 ,
RelativeSizeAxes = Axes . Both ,
} ,
( certifiedText = new OsuSpriteText
{
Alpha = 0 ,
Font = OsuFont . TorusAlternate . With ( size : 80 , weight : FontWeight . Bold ) ,
Text = "Certified!" ,
Blending = BlendingParameters . Additive ,
} ) . WithEffect ( new GlowEffect
{
Colour = overlayColourProvider . Colour1 ,
PadExtent = true
} ) . With ( e = >
{
e . Anchor = Anchor . Centre ;
e . Origin = Anchor . Centre ;
} ) ,
new OsuSpriteText
{
Text = $"You should use a frame limiter with update rate of {mapDifficultyToTargetFrameRate(DifficultyLevel + 1)} Hz (or fps) for best results!" ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Font = OsuFont . Torus . With ( size : 24 , weight : FontWeight . SemiBold ) ,
Y = 80 ,
2022-06-07 13:40:21 +08:00
}
} ) ;
2022-06-10 14:19:10 +08:00
background . FadeInFromZero ( 1000 , Easing . OutQuint ) ;
certifiedText . FadeInFromZero ( 500 , Easing . InQuint ) ;
certifiedText
. ScaleTo ( 10 )
. ScaleTo ( 1 , 600 , Easing . InQuad )
. Then ( )
. ScaleTo ( 1.05f , 10000 , Easing . OutQuint ) ;
2022-06-07 13:40:21 +08:00
}
2022-06-07 22:10:08 +08:00
private void changeDifficulty ( int difficulty )
2022-06-07 17:11:54 +08:00
{
2022-06-07 22:10:08 +08:00
Debug . Assert ( difficulty > 0 ) ;
2022-06-07 17:11:54 +08:00
resultsArea . Clear ( ) ;
2022-06-10 13:23:24 +08:00
correctAtCurrentDifficulty = 0 ;
attemptsAtCurrentDifficulty = 0 ;
2022-06-07 18:43:33 +08:00
pollingMax = 0 ;
lastPoll = 0 ;
2022-06-07 17:11:54 +08:00
2022-06-10 14:19:10 +08:00
DifficultyLevel = difficulty ;
2022-06-07 22:10:08 +08:00
2022-06-07 17:11:54 +08:00
loadNextRound ( ) ;
}
2022-06-07 22:10:08 +08:00
private void loadNextRound ( )
{
2022-06-11 20:32:02 +08:00
settings . Show ( ) ;
2022-06-10 13:23:24 +08:00
attemptsAtCurrentDifficulty + + ;
2022-06-10 14:19:10 +08:00
statusText . Text = $"Level {DifficultyLevel}\nRound {attemptsAtCurrentDifficulty} of {totalRoundForNextResultsScreen}" ;
2022-06-07 22:10:08 +08:00
mainArea . Clear ( ) ;
int betterSide = RNG . Next ( 0 , 2 ) ;
mainArea . AddRange ( new [ ]
{
2022-06-24 13:48:43 +08:00
new LatencyArea ( Key . Number1 , betterSide = = 1 ? mapDifficultyToTargetFrameRate ( DifficultyLevel ) : null )
2022-06-07 22:10:08 +08:00
{
Width = 0.5f ,
2022-06-10 17:18:18 +08:00
VisualMode = { BindTarget = VisualMode } ,
2022-06-07 22:10:08 +08:00
IsActiveArea = { Value = true } ,
ReportUserBest = ( ) = > recordResult ( betterSide = = 0 ) ,
} ,
2022-06-24 13:48:43 +08:00
new LatencyArea ( Key . Number2 , betterSide = = 0 ? mapDifficultyToTargetFrameRate ( DifficultyLevel ) : null )
2022-06-07 22:10:08 +08:00
{
Width = 0.5f ,
2022-06-10 17:18:18 +08:00
VisualMode = { BindTarget = VisualMode } ,
2022-06-07 22:10:08 +08:00
Anchor = Anchor . TopRight ,
Origin = Anchor . TopRight ,
ReportUserBest = ( ) = > recordResult ( betterSide = = 1 )
}
} ) ;
foreach ( var area in mainArea )
{
area . IsActiveArea . BindValueChanged ( active = >
{
if ( active . NewValue )
mainArea . Children . First ( a = > a ! = area ) . IsActiveArea . Value = false ;
} ) ;
}
}
private void recordResult ( bool correct )
{
// Fading this out will improve the frame rate after the first round due to less text on screen.
explanatoryText . FadeOut ( 500 , Easing . OutQuint ) ;
if ( correct )
2022-06-10 13:23:24 +08:00
correctAtCurrentDifficulty + + ;
2022-06-07 22:10:08 +08:00
2022-06-10 13:23:24 +08:00
if ( attemptsAtCurrentDifficulty < totalRoundForNextResultsScreen )
2022-06-07 22:10:08 +08:00
loadNextRound ( ) ;
else
showResults ( ) ;
}
2022-06-07 18:03:26 +08:00
private static int mapDifficultyToTargetFrameRate ( int difficulty )
{
switch ( difficulty )
{
case 1 :
return 15 ;
case 2 :
return 30 ;
case 3 :
return 45 ;
case 4 :
return 60 ;
case 5 :
return 120 ;
case 6 :
return 240 ;
case 7 :
return 480 ;
case 8 :
return 720 ;
case 9 :
return 960 ;
default :
return 1000 + ( ( difficulty - 10 ) * 500 ) ;
}
}
2022-06-07 13:40:21 +08:00
}
}