From ecc98b5a31e3077be73bd1c5659d7fbf6e9f5354 Mon Sep 17 00:00:00 2001 From: vun Date: Sat, 3 Feb 2024 11:53:48 +0800 Subject: [PATCH 01/14] Convert maximum and minimum aspect into bindable floats --- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index c67f61052c..c8738aa67c 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -12,8 +12,8 @@ namespace osu.Game.Rulesets.Taiko.UI { public partial class TaikoPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer { - public const float MAXIMUM_ASPECT = 16f / 9f; - public const float MINIMUM_ASPECT = 5f / 4f; + public BindableFloat MaximumAspect = new BindableFloat(16f / 9f); + public BindableFloat MinimumAspect = new BindableFloat(5f / 4f); private const float stable_gamefield_height = 480f; @@ -46,10 +46,10 @@ namespace osu.Game.Rulesets.Taiko.UI { float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - if (currentAspect > MAXIMUM_ASPECT) - relativeHeight *= currentAspect / MAXIMUM_ASPECT; - else if (currentAspect < MINIMUM_ASPECT) - relativeHeight *= currentAspect / MINIMUM_ASPECT; + if (currentAspect > MaximumAspect.Value) + relativeHeight *= currentAspect / MaximumAspect.Value; + else if (currentAspect < MinimumAspect.Value) + relativeHeight *= currentAspect / MinimumAspect.Value; } // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.UI float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; if (LockPlayfieldAspectRange.Value) - currentAspect = Math.Clamp(currentAspect, MINIMUM_ASPECT, MAXIMUM_ASPECT); + currentAspect = Math.Clamp(currentAspect, MinimumAspect.Value, MaximumAspect.Value); // in a game resolution of 1024x768, stable's scrolling system consists of objects being placed 600px (widthScaled - 40) away from their hit location. // however, the point at which the object renders at the end of the screen is exactly x=640, but stable makes the object start moving from beyond the screen instead of the boundary point. From 34e888ddc6be80516cd6650d355bfd02c4caa7e2 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 4 Feb 2024 17:26:30 +0800 Subject: [PATCH 02/14] Match stable's nomod timerange on classic --- .../Mods/TaikoModClassic.cs | 4 +- .../UI/DrawableTaikoRuleset.cs | 10 ++++- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 37 ++++++++++++------- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index f63d6c2673..9de90852e6 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -15,7 +15,9 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; - drawableTaikoRuleset.LockPlayfieldAspectRange.Value = false; + drawableTaikoRuleset.MaximumAspect.Value = 22f / 9f; + drawableTaikoRuleset.MinimumAspect.Value = 5f / 4f; + drawableTaikoRuleset.TrimOnOverflow.Value = true; } public void ApplyToDrawableHitObject(DrawableHitObject drawable) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 77b2b06c0e..ec79d40c83 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -31,7 +31,11 @@ namespace osu.Game.Rulesets.Taiko.UI { public new BindableDouble TimeRange => base.TimeRange; - public readonly BindableBool LockPlayfieldAspectRange = new BindableBool(true); + public readonly BindableFloat MaximumAspect = new BindableFloat(16f / 9f); + + public readonly BindableFloat MinimumAspect = new BindableFloat(5f / 4f); + + public readonly BindableBool TrimOnOverflow = new BindableBool(false); public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; @@ -89,7 +93,9 @@ namespace osu.Game.Rulesets.Taiko.UI public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer { - LockPlayfieldAspectRange = { BindTarget = LockPlayfieldAspectRange } + MaximumAspect = { BindTarget = MaximumAspect }, + MinimumAspect = { BindTarget = MinimumAspect }, + TrimOnOverflow = { BindTarget = TrimOnOverflow } }; protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index c8738aa67c..cc523c755c 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -12,12 +12,13 @@ namespace osu.Game.Rulesets.Taiko.UI { public partial class TaikoPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer { - public BindableFloat MaximumAspect = new BindableFloat(16f / 9f); - public BindableFloat MinimumAspect = new BindableFloat(5f / 4f); - private const float stable_gamefield_height = 480f; - public readonly IBindable LockPlayfieldAspectRange = new BindableBool(true); + public BindableFloat MaximumAspect = new BindableFloat(16f / 9f); + + public BindableFloat MinimumAspect = new BindableFloat(5f / 4f); + + public BindableBool TrimOnOverflow = new BindableBool(false); public TaikoPlayfieldAdjustmentContainer() { @@ -37,34 +38,44 @@ namespace osu.Game.Rulesets.Taiko.UI float relativeHeight = base_relative_height; + float widthScale = 1.0f; + // Players coming from stable expect to be able to change the aspect ratio regardless of the window size. // We originally wanted to limit this more, but there was considerable pushback from the community. // // As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit. // This is still a bit weird, because readability changes with window size, but it is what it is. - if (LockPlayfieldAspectRange.Value) - { - float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; + float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - if (currentAspect > MaximumAspect.Value) + if (currentAspect > MaximumAspect.Value) + { + if (TrimOnOverflow.Value) + { + widthScale = MaximumAspect.Value / currentAspect; + } + else + { relativeHeight *= currentAspect / MaximumAspect.Value; - else if (currentAspect < MinimumAspect.Value) - relativeHeight *= currentAspect / MinimumAspect.Value; + } } + else if (currentAspect < MinimumAspect.Value) + { + relativeHeight *= currentAspect / MinimumAspect.Value; + } + // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. relativeHeight = Math.Min(relativeHeight, 1f / 3f); Scale = new Vector2(Math.Max((Parent!.ChildSize.Y / 768f) * (relativeHeight / base_relative_height), 1f)); - Width = 1 / Scale.X; + Width = 1 / Scale.X * widthScale; } public double ComputeTimeRange() { float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - if (LockPlayfieldAspectRange.Value) - currentAspect = Math.Clamp(currentAspect, MinimumAspect.Value, MaximumAspect.Value); + currentAspect = Math.Clamp(currentAspect, MinimumAspect.Value, MaximumAspect.Value); // in a game resolution of 1024x768, stable's scrolling system consists of objects being placed 600px (widthScaled - 40) away from their hit location. // however, the point at which the object renders at the end of the screen is exactly x=640, but stable makes the object start moving from beyond the screen instead of the boundary point. From c75ce34f758f14448f682c138359e11921bbd1d8 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 4 Feb 2024 18:44:45 +0800 Subject: [PATCH 03/14] Classic hidden --- .../Mods/TaikoModClassic.cs | 48 +++++++++++++++++++ .../Mods/TaikoModHidden.cs | 19 +++++++- .../UI/DrawableTaikoRuleset.cs | 5 +- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 14 +++--- 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 9de90852e6..19d177f1cb 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -1,6 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; @@ -12,12 +17,41 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset, IApplicableToDrawableHitObject { + private IReadOnlyList mods = Array.Empty(); + + private const float default_hidden_fade_out_duration = 0.375f; + + private const float default_hidden_initial_alpha = 0.75f; + + private const float hidden_base_aspect = 4f / 3f; + + private readonly BindableFloat hiddenFadeOutDuration = new BindableFloat(default_hidden_fade_out_duration); + + private readonly BindableFloat hiddenInitialAlpha = new BindableFloat(default_hidden_initial_alpha); + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + + // drawableRuleset.Mods should always be non-null here, but just in case. + mods = drawableRuleset.Mods ?? mods; + drawableTaikoRuleset.MaximumAspect.Value = 22f / 9f; drawableTaikoRuleset.MinimumAspect.Value = 5f / 4f; drawableTaikoRuleset.TrimOnOverflow.Value = true; + + TaikoModHidden? hidden = mods.OfType().FirstOrDefault(); + if (hidden != null) + { + // Stable limits the aspect ratio to 4:3 + drawableTaikoRuleset.MaximumAspect.Value = hidden_base_aspect; + + // Enable aspect ratio adjustment for hidden (see adjustHidden) + hiddenInitialAlpha.BindTo(hidden.InitialAlpha); + hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); + drawableRuleset.OnUpdate += adjustHidden; + } } public void ApplyToDrawableHitObject(DrawableHitObject drawable) @@ -25,5 +59,19 @@ namespace osu.Game.Rulesets.Taiko.Mods if (drawable is DrawableTaikoHitObject hit) hit.SnapJudgementLocation = true; } + + // Compensate for aspect ratios narrower than 4:3 by scaling the fade out duration and initial alpha + private void adjustHidden(Drawable drawableRuleset) + { + var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + + if (drawableTaikoRuleset.CurrentAspect.Value < hidden_base_aspect) + { + hiddenFadeOutDuration.Value = + default_hidden_fade_out_duration * (drawableTaikoRuleset.CurrentAspect.Value / hidden_base_aspect); + hiddenInitialAlpha.Value = + default_hidden_initial_alpha * (drawableTaikoRuleset.CurrentAspect.Value / hidden_base_aspect); + } + } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs index 2c3b4a8d18..914e279e24 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Rulesets.Mods; @@ -29,7 +30,20 @@ namespace osu.Game.Rulesets.Taiko.Mods /// How long hitobjects take to fade out, in terms of the scrolling length. /// Range: [0, 1] /// - private const float fade_out_duration = 0.375f; + public readonly BindableFloat FadeOutDuration = new BindableFloat(0.375f) + { + MinValue = 0f, + MaxValue = 1f + }; + + /// + /// The initial alpha of hitobjects when they appear. + /// + public readonly BindableFloat InitialAlpha = new BindableFloat(1f) + { + MinValue = 0f, + MaxValue = 1f + }; private DrawableTaikoRuleset drawableRuleset = null!; @@ -51,7 +65,8 @@ namespace osu.Game.Rulesets.Taiko.Mods case DrawableHit: double preempt = drawableRuleset.TimeRange.Value / drawableRuleset.ControlPointAt(hitObject.HitObject.StartTime).Multiplier; double start = hitObject.HitObject.StartTime - preempt * fade_out_start_time; - double duration = preempt * fade_out_duration; + double duration = preempt * FadeOutDuration.Value; + hitObject.Alpha = InitialAlpha.Value; using (hitObject.BeginAbsoluteSequence(start)) { diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index ec79d40c83..a2a5fb7686 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -37,6 +37,8 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool TrimOnOverflow = new BindableBool(false); + public readonly BindableFloat CurrentAspect = new BindableFloat(16f / 9f); + public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; protected new TaikoPlayfieldAdjustmentContainer PlayfieldAdjustmentContainer => (TaikoPlayfieldAdjustmentContainer)base.PlayfieldAdjustmentContainer; @@ -95,7 +97,8 @@ namespace osu.Game.Rulesets.Taiko.UI { MaximumAspect = { BindTarget = MaximumAspect }, MinimumAspect = { BindTarget = MinimumAspect }, - TrimOnOverflow = { BindTarget = TrimOnOverflow } + TrimOnOverflow = { BindTarget = TrimOnOverflow }, + CurrentAspect = { BindTarget = CurrentAspect } }; protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index cc523c755c..413bafd95b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Taiko.UI public BindableFloat MinimumAspect = new BindableFloat(5f / 4f); + public BindableFloat CurrentAspect = new BindableFloat(16f / 9f); + public BindableBool TrimOnOverflow = new BindableBool(false); public TaikoPlayfieldAdjustmentContainer() @@ -45,22 +47,22 @@ namespace osu.Game.Rulesets.Taiko.UI // // As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit. // This is still a bit weird, because readability changes with window size, but it is what it is. - float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; + CurrentAspect.Value = Parent!.ChildSize.X / Parent!.ChildSize.Y; - if (currentAspect > MaximumAspect.Value) + if (CurrentAspect.Value > MaximumAspect.Value) { if (TrimOnOverflow.Value) { - widthScale = MaximumAspect.Value / currentAspect; + widthScale = MaximumAspect.Value / CurrentAspect.Value; } else { - relativeHeight *= currentAspect / MaximumAspect.Value; + relativeHeight *= CurrentAspect.Value / MaximumAspect.Value; } } - else if (currentAspect < MinimumAspect.Value) + else if (CurrentAspect.Value < MinimumAspect.Value) { - relativeHeight *= currentAspect / MinimumAspect.Value; + relativeHeight *= CurrentAspect.Value / MinimumAspect.Value; } From 01a380370a6b43d95deaf738895386796e226e81 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 4 Feb 2024 20:01:23 +0800 Subject: [PATCH 04/14] Classic hardrock --- osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs | 15 +++++++++++++++ .../UI/DrawableTaikoRuleset.cs | 8 +++++++- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 6 +++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 19d177f1cb..132717105e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -52,6 +52,21 @@ namespace osu.Game.Rulesets.Taiko.Mods hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); drawableRuleset.OnUpdate += adjustHidden; } + + if (mods.OfType().Any()) + { + // For hardrock, the playfield time range is clamped to within classicMaxTimeRange and the equivalent + // time range for a 16:10 aspect ratio. + drawableTaikoRuleset.TrimOnOverflow.Value = false; + + // Apply stable aspect ratio limits for hardrock (visually taken) + drawableTaikoRuleset.MaximumAspect.Value = 1.963f; + drawableTaikoRuleset.MinimumAspect.Value = 1.64f; + + // Visually taken from different aspect ratios + drawableTaikoRuleset.MinimumRelativeHeight.Value = 0.26f; + drawableTaikoRuleset.MaximumRelativeHeight.Value = 0.26f; + } } public void ApplyToDrawableHitObject(DrawableHitObject drawable) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index a2a5fb7686..c9389bc0c8 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -37,6 +37,10 @@ namespace osu.Game.Rulesets.Taiko.UI public readonly BindableBool TrimOnOverflow = new BindableBool(false); + public readonly BindableFloat MinimumRelativeHeight = new BindableFloat(0f); + + public readonly BindableFloat MaximumRelativeHeight = new BindableFloat(1f / 3f); + public readonly BindableFloat CurrentAspect = new BindableFloat(16f / 9f); public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; @@ -98,7 +102,9 @@ namespace osu.Game.Rulesets.Taiko.UI MaximumAspect = { BindTarget = MaximumAspect }, MinimumAspect = { BindTarget = MinimumAspect }, TrimOnOverflow = { BindTarget = TrimOnOverflow }, - CurrentAspect = { BindTarget = CurrentAspect } + CurrentAspect = { BindTarget = CurrentAspect }, + MaximumRelativeHeight = { BindTarget = MaximumRelativeHeight }, + MinimumRelativeHeight = { BindTarget = MinimumRelativeHeight } }; protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index 413bafd95b..861917b8af 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -20,6 +20,10 @@ namespace osu.Game.Rulesets.Taiko.UI public BindableFloat CurrentAspect = new BindableFloat(16f / 9f); + public BindableFloat MaximumRelativeHeight = new BindableFloat(1f / 3f); + + public BindableFloat MinimumRelativeHeight = new BindableFloat(0f); + public BindableBool TrimOnOverflow = new BindableBool(false); public TaikoPlayfieldAdjustmentContainer() @@ -67,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.UI // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. - relativeHeight = Math.Min(relativeHeight, 1f / 3f); + relativeHeight = Math.Clamp(relativeHeight, MinimumRelativeHeight.Value, MaximumRelativeHeight.Value); Scale = new Vector2(Math.Max((Parent!.ChildSize.Y / 768f) * (relativeHeight / base_relative_height), 1f)); Width = 1 / Scale.X * widthScale; From 0fe62284a2cb0f92fcd07b48d2d8180f5e96576b Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 12 Feb 2024 13:40:03 +0800 Subject: [PATCH 05/14] Implement classic hdhr --- .../Mods/TaikoModClassic.cs | 69 +++++++++++++------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 132717105e..863e222422 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -19,15 +19,19 @@ namespace osu.Game.Rulesets.Taiko.Mods { private IReadOnlyList mods = Array.Empty(); - private const float default_hidden_fade_out_duration = 0.375f; + private const float hd_base_fade_out_duration = 0.375f; - private const float default_hidden_initial_alpha = 0.75f; + private const float hd_base_initial_alpha = 0.75f; + + private const float hdhr_base_fade_out_duration = 0.2f; + + private const float hdhr_base_initial_alpha = 0.2f; private const float hidden_base_aspect = 4f / 3f; - private readonly BindableFloat hiddenFadeOutDuration = new BindableFloat(default_hidden_fade_out_duration); + private readonly BindableFloat hiddenFadeOutDuration = new BindableFloat(hd_base_fade_out_duration); - private readonly BindableFloat hiddenInitialAlpha = new BindableFloat(default_hidden_initial_alpha); + private readonly BindableFloat hiddenInitialAlpha = new BindableFloat(hd_base_initial_alpha); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) @@ -42,16 +46,7 @@ namespace osu.Game.Rulesets.Taiko.Mods drawableTaikoRuleset.TrimOnOverflow.Value = true; TaikoModHidden? hidden = mods.OfType().FirstOrDefault(); - if (hidden != null) - { - // Stable limits the aspect ratio to 4:3 - drawableTaikoRuleset.MaximumAspect.Value = hidden_base_aspect; - // Enable aspect ratio adjustment for hidden (see adjustHidden) - hiddenInitialAlpha.BindTo(hidden.InitialAlpha); - hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); - drawableRuleset.OnUpdate += adjustHidden; - } if (mods.OfType().Any()) { @@ -61,11 +56,32 @@ namespace osu.Game.Rulesets.Taiko.Mods // Apply stable aspect ratio limits for hardrock (visually taken) drawableTaikoRuleset.MaximumAspect.Value = 1.963f; - drawableTaikoRuleset.MinimumAspect.Value = 1.64f; + + // This is accurate to 4:3, but slightly off for 5:4 + drawableTaikoRuleset.MinimumAspect.Value = 1.666f; // Visually taken from different aspect ratios drawableTaikoRuleset.MinimumRelativeHeight.Value = 0.26f; drawableTaikoRuleset.MaximumRelativeHeight.Value = 0.26f; + + if (hidden != null) + { + hiddenInitialAlpha.BindTo(hidden.InitialAlpha); + hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); + drawableRuleset.OnUpdate += d => adjustHidden( + d, hdhr_base_fade_out_duration, hdhr_base_initial_alpha, 16f / 9f, 0.8f); + } + } + else if (hidden != null) + { + // Stable limits the aspect ratio to 4:3 + drawableTaikoRuleset.MaximumAspect.Value = hidden_base_aspect; + + // Enable aspect ratio adjustment for hidden (see adjustHidden) + hiddenInitialAlpha.BindTo(hidden.InitialAlpha); + hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); + drawableRuleset.OnUpdate += d => adjustHidden( + d, hd_base_fade_out_duration, hd_base_initial_alpha, hidden_base_aspect); } } @@ -76,17 +92,26 @@ namespace osu.Game.Rulesets.Taiko.Mods } // Compensate for aspect ratios narrower than 4:3 by scaling the fade out duration and initial alpha - private void adjustHidden(Drawable drawableRuleset) + private void adjustHidden( + Drawable drawableRuleset, + float baseFadeOutDuration, + float baseInitialAlpha, + float baseAspect, + float adjustmentRatio = 1f) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + float aspect = Math.Clamp( + drawableTaikoRuleset.CurrentAspect.Value, + drawableTaikoRuleset.MinimumAspect.Value, + drawableTaikoRuleset.MaximumAspect.Value); - if (drawableTaikoRuleset.CurrentAspect.Value < hidden_base_aspect) - { - hiddenFadeOutDuration.Value = - default_hidden_fade_out_duration * (drawableTaikoRuleset.CurrentAspect.Value / hidden_base_aspect); - hiddenInitialAlpha.Value = - default_hidden_initial_alpha * (drawableTaikoRuleset.CurrentAspect.Value / hidden_base_aspect); - } + float fadeOutDurationAdjustment = aspect / baseAspect - 1; + fadeOutDurationAdjustment *= adjustmentRatio; + hiddenFadeOutDuration.Value = baseFadeOutDuration + fadeOutDurationAdjustment; + + float initialAlphaAdjustment = aspect / baseAspect - 1; + initialAlphaAdjustment *= adjustmentRatio; + hiddenInitialAlpha.Value = baseInitialAlpha + initialAlphaAdjustment; } } } From 46b2c123a6911b079d2c2c24af98ef51695cb4d7 Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 12 Feb 2024 14:00:49 +0800 Subject: [PATCH 06/14] Fix codeinspect issues --- osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs | 2 -- osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 863e222422..9a874faa7a 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -33,7 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Mods private readonly BindableFloat hiddenInitialAlpha = new BindableFloat(hd_base_initial_alpha); - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; @@ -47,7 +46,6 @@ namespace osu.Game.Rulesets.Taiko.Mods TaikoModHidden? hidden = mods.OfType().FirstOrDefault(); - if (mods.OfType().Any()) { // For hardrock, the playfield time range is clamped to within classicMaxTimeRange and the equivalent diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index 861917b8af..ed87b8d3ba 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -69,7 +69,6 @@ namespace osu.Game.Rulesets.Taiko.UI relativeHeight *= CurrentAspect.Value / MinimumAspect.Value; } - // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. relativeHeight = Math.Clamp(relativeHeight, MinimumRelativeHeight.Value, MaximumRelativeHeight.Value); From 549552393ae15c38eb85d6f1fcf864a4ac3a862e Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 12 Feb 2024 14:44:49 +0800 Subject: [PATCH 07/14] Fix inaccurate comments --- osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 9a874faa7a..f195b4afbf 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -58,7 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Mods // This is accurate to 4:3, but slightly off for 5:4 drawableTaikoRuleset.MinimumAspect.Value = 1.666f; - // Visually taken from different aspect ratios drawableTaikoRuleset.MinimumRelativeHeight.Value = 0.26f; drawableTaikoRuleset.MaximumRelativeHeight.Value = 0.26f; @@ -89,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Mods hit.SnapJudgementLocation = true; } - // Compensate for aspect ratios narrower than 4:3 by scaling the fade out duration and initial alpha + // Adjust hidden initial alpha and fade out duration for different aspect ratios private void adjustHidden( Drawable drawableRuleset, float baseFadeOutDuration, From 79cfcc87f3c5cb0cfb41b131003947948723978c Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 18 Feb 2024 10:01:29 +0800 Subject: [PATCH 08/14] Change TaikoPlayfieldAdjustmentContainer parameters to be protected internal --- .../Mods/TaikoModClassic.cs | 27 +++++----- .../UI/DrawableTaikoRuleset.cs | 22 +-------- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 49 +++++++++++++------ 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index f195b4afbf..bd029287de 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -36,13 +36,14 @@ namespace osu.Game.Rulesets.Taiko.Mods public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + var adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableTaikoRuleset.PlayfieldAdjustmentContainer; // drawableRuleset.Mods should always be non-null here, but just in case. mods = drawableRuleset.Mods ?? mods; - drawableTaikoRuleset.MaximumAspect.Value = 22f / 9f; - drawableTaikoRuleset.MinimumAspect.Value = 5f / 4f; - drawableTaikoRuleset.TrimOnOverflow.Value = true; + adjustmentContainer.MaximumAspect = 22f / 9f; + adjustmentContainer.MinimumAspect = 5f / 4f; + adjustmentContainer.TrimOnOverflow = true; TaikoModHidden? hidden = mods.OfType().FirstOrDefault(); @@ -50,16 +51,16 @@ namespace osu.Game.Rulesets.Taiko.Mods { // For hardrock, the playfield time range is clamped to within classicMaxTimeRange and the equivalent // time range for a 16:10 aspect ratio. - drawableTaikoRuleset.TrimOnOverflow.Value = false; + adjustmentContainer.TrimOnOverflow = false; // Apply stable aspect ratio limits for hardrock (visually taken) - drawableTaikoRuleset.MaximumAspect.Value = 1.963f; + adjustmentContainer.MaximumAspect = 1.963f; // This is accurate to 4:3, but slightly off for 5:4 - drawableTaikoRuleset.MinimumAspect.Value = 1.666f; + adjustmentContainer.MinimumAspect = 1.666f; - drawableTaikoRuleset.MinimumRelativeHeight.Value = 0.26f; - drawableTaikoRuleset.MaximumRelativeHeight.Value = 0.26f; + adjustmentContainer.MinimumRelativeHeight = 0.26f; + adjustmentContainer.MaximumRelativeHeight = 0.26f; if (hidden != null) { @@ -72,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Mods else if (hidden != null) { // Stable limits the aspect ratio to 4:3 - drawableTaikoRuleset.MaximumAspect.Value = hidden_base_aspect; + adjustmentContainer.MaximumAspect = hidden_base_aspect; // Enable aspect ratio adjustment for hidden (see adjustHidden) hiddenInitialAlpha.BindTo(hidden.InitialAlpha); @@ -97,10 +98,12 @@ namespace osu.Game.Rulesets.Taiko.Mods float adjustmentRatio = 1f) { var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + var adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableTaikoRuleset.PlayfieldAdjustmentContainer; + float aspect = Math.Clamp( - drawableTaikoRuleset.CurrentAspect.Value, - drawableTaikoRuleset.MinimumAspect.Value, - drawableTaikoRuleset.MaximumAspect.Value); + adjustmentContainer.CurrentAspect, + adjustmentContainer.MinimumAspect, + adjustmentContainer.MaximumAspect); float fadeOutDurationAdjustment = aspect / baseAspect - 1; fadeOutDurationAdjustment *= adjustmentRatio; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index c9389bc0c8..b1a951edaf 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -31,18 +31,6 @@ namespace osu.Game.Rulesets.Taiko.UI { public new BindableDouble TimeRange => base.TimeRange; - public readonly BindableFloat MaximumAspect = new BindableFloat(16f / 9f); - - public readonly BindableFloat MinimumAspect = new BindableFloat(5f / 4f); - - public readonly BindableBool TrimOnOverflow = new BindableBool(false); - - public readonly BindableFloat MinimumRelativeHeight = new BindableFloat(0f); - - public readonly BindableFloat MaximumRelativeHeight = new BindableFloat(1f / 3f); - - public readonly BindableFloat CurrentAspect = new BindableFloat(16f / 9f); - public new TaikoInputManager KeyBindingInputManager => (TaikoInputManager)base.KeyBindingInputManager; protected new TaikoPlayfieldAdjustmentContainer PlayfieldAdjustmentContainer => (TaikoPlayfieldAdjustmentContainer)base.PlayfieldAdjustmentContainer; @@ -97,15 +85,7 @@ namespace osu.Game.Rulesets.Taiko.UI return ControlPoints[result]; } - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer - { - MaximumAspect = { BindTarget = MaximumAspect }, - MinimumAspect = { BindTarget = MinimumAspect }, - TrimOnOverflow = { BindTarget = TrimOnOverflow }, - CurrentAspect = { BindTarget = CurrentAspect }, - MaximumRelativeHeight = { BindTarget = MaximumRelativeHeight }, - MinimumRelativeHeight = { BindTarget = MinimumRelativeHeight } - }; + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer(); protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index ed87b8d3ba..bde449734d 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.UI; @@ -14,17 +13,35 @@ namespace osu.Game.Rulesets.Taiko.UI { private const float stable_gamefield_height = 480f; - public BindableFloat MaximumAspect = new BindableFloat(16f / 9f); + // + // The maximum aspect ratio the playfield can be adjusted to. + // + protected internal float MaximumAspect = 16f / 9f; - public BindableFloat MinimumAspect = new BindableFloat(5f / 4f); + // + // The minimum aspect ratio the playfield can be adjusted to. + // + protected internal float MinimumAspect = 5f / 4f; - public BindableFloat CurrentAspect = new BindableFloat(16f / 9f); + // + // The current aspect ratio of the playfield. This should be the aspect ratio of the game's resolution. + // + protected internal float CurrentAspect { get; private set; } = 16f / 9f; - public BindableFloat MaximumRelativeHeight = new BindableFloat(1f / 3f); + // + // The maximum relative height of the playfield. This is a fraction of the available area. + // + protected internal float MaximumRelativeHeight = 1f / 3f; - public BindableFloat MinimumRelativeHeight = new BindableFloat(0f); + // + // The minimum relative height of the playfield. This is a fraction of the available area. + // + protected internal float MinimumRelativeHeight = 0f; - public BindableBool TrimOnOverflow = new BindableBool(false); + // + // Whether the playfield should be trimmed when the aspect ratio exceeds the maximum. + // + protected internal bool TrimOnOverflow = false; public TaikoPlayfieldAdjustmentContainer() { @@ -51,26 +68,26 @@ namespace osu.Game.Rulesets.Taiko.UI // // As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit. // This is still a bit weird, because readability changes with window size, but it is what it is. - CurrentAspect.Value = Parent!.ChildSize.X / Parent!.ChildSize.Y; + float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - if (CurrentAspect.Value > MaximumAspect.Value) + if (currentAspect > MaximumAspect) { - if (TrimOnOverflow.Value) + if (TrimOnOverflow) { - widthScale = MaximumAspect.Value / CurrentAspect.Value; + widthScale = MaximumAspect / currentAspect; } else { - relativeHeight *= CurrentAspect.Value / MaximumAspect.Value; + relativeHeight *= currentAspect / MaximumAspect; } } - else if (CurrentAspect.Value < MinimumAspect.Value) + else if (currentAspect < MinimumAspect) { - relativeHeight *= CurrentAspect.Value / MinimumAspect.Value; + relativeHeight *= currentAspect / MinimumAspect; } // Limit the maximum relative height of the playfield to one-third of available area to avoid it masking out on extreme resolutions. - relativeHeight = Math.Clamp(relativeHeight, MinimumRelativeHeight.Value, MaximumRelativeHeight.Value); + relativeHeight = Math.Clamp(relativeHeight, MinimumRelativeHeight, MaximumRelativeHeight); Scale = new Vector2(Math.Max((Parent!.ChildSize.Y / 768f) * (relativeHeight / base_relative_height), 1f)); Width = 1 / Scale.X * widthScale; @@ -80,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.UI { float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - currentAspect = Math.Clamp(currentAspect, MinimumAspect.Value, MaximumAspect.Value); + currentAspect = Math.Clamp(currentAspect, MinimumAspect, MaximumAspect); // in a game resolution of 1024x768, stable's scrolling system consists of objects being placed 600px (widthScaled - 40) away from their hit location. // however, the point at which the object renders at the end of the screen is exactly x=640, but stable makes the object start moving from beyond the screen instead of the boundary point. From 98f8fa22a79f69eb1ed5274187404c8099d12095 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 18 Feb 2024 10:11:26 +0800 Subject: [PATCH 09/14] Fix barline appearing before the beginning of playfield --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 0510f08068..81ebc7c988 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -122,7 +122,9 @@ namespace osu.Game.Rulesets.Taiko.UI { Name = "Bar line content", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = hit_target_width / 2 + hit_target_offset }, + Padding = new MarginPadding { + Left = hit_target_width / 2 + hit_target_offset, + Right = hit_target_width / 2}, // To avoid barline appearing before the beginning of playfield when trimmed Children = new Drawable[] { UnderlayElements = new Container From 67e86a81b48e9f1065be1ae95027958ea31c8140 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 18 Feb 2024 10:20:53 +0800 Subject: [PATCH 10/14] Fix codeinspect issues --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 81ebc7c988..1f509b8c96 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -123,7 +123,9 @@ namespace osu.Game.Rulesets.Taiko.UI Name = "Bar line content", RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { + Left = hit_target_width / 2 + hit_target_offset, + Right = hit_target_width / 2}, // To avoid barline appearing before the beginning of playfield when trimmed Children = new Drawable[] { From e2bd8da75d4730807babb8161eebe4c71fba2158 Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 18 Feb 2024 10:38:54 +0800 Subject: [PATCH 11/14] Fix InspectCode issue --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 1f509b8c96..476c3ea207 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -122,11 +122,11 @@ namespace osu.Game.Rulesets.Taiko.UI { Name = "Bar line content", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { - + Padding = new MarginPadding + { Left = hit_target_width / 2 + hit_target_offset, - - Right = hit_target_width / 2}, // To avoid barline appearing before the beginning of playfield when trimmed + Right = hit_target_width / 2 + }, // To avoid barline appearing before the beginning of playfield when trimmed Children = new Drawable[] { UnderlayElements = new Container From 8e052fe79520f18d3b3c63eb15c864c29b481cbb Mon Sep 17 00:00:00 2001 From: vun Date: Sun, 18 Feb 2024 12:12:02 +0800 Subject: [PATCH 12/14] Fix CurrentAspect not being set & revert barline fix --- .../Mods/TaikoModClassic.cs | 9 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +-- .../UI/TaikoPlayfieldAdjustmentContainer.cs | 47 ++++++++++--------- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index bd029287de..642eee89f7 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -100,16 +100,13 @@ namespace osu.Game.Rulesets.Taiko.Mods var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; var adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableTaikoRuleset.PlayfieldAdjustmentContainer; - float aspect = Math.Clamp( - adjustmentContainer.CurrentAspect, - adjustmentContainer.MinimumAspect, - adjustmentContainer.MaximumAspect); + float clampedAspect = adjustmentContainer.ClampedCurrentAspect; - float fadeOutDurationAdjustment = aspect / baseAspect - 1; + float fadeOutDurationAdjustment = clampedAspect / baseAspect - 1; fadeOutDurationAdjustment *= adjustmentRatio; hiddenFadeOutDuration.Value = baseFadeOutDuration + fadeOutDurationAdjustment; - float initialAlphaAdjustment = aspect / baseAspect - 1; + float initialAlphaAdjustment = clampedAspect / baseAspect - 1; initialAlphaAdjustment *= adjustmentRatio; hiddenInitialAlpha.Value = baseInitialAlpha + initialAlphaAdjustment; } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 476c3ea207..6240f62b3b 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -122,11 +122,7 @@ namespace osu.Game.Rulesets.Taiko.UI { Name = "Bar line content", RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding - { - Left = hit_target_width / 2 + hit_target_offset, - Right = hit_target_width / 2 - }, // To avoid barline appearing before the beginning of playfield when trimmed + Padding = new MarginPadding { Left = hit_target_width / 2 + hit_target_offset }, // To avoid barline appearing before the beginning of playfield when trimmed Children = new Drawable[] { UnderlayElements = new Container diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs index bde449734d..7475c64a7e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfieldAdjustmentContainer.cs @@ -13,34 +13,35 @@ namespace osu.Game.Rulesets.Taiko.UI { private const float stable_gamefield_height = 480f; - // - // The maximum aspect ratio the playfield can be adjusted to. - // + /// + /// The maximum aspect ratio the playfield can be adjusted to. + /// protected internal float MaximumAspect = 16f / 9f; - // - // The minimum aspect ratio the playfield can be adjusted to. - // + /// + /// The minimum aspect ratio the playfield can be adjusted to. + /// protected internal float MinimumAspect = 5f / 4f; - // - // The current aspect ratio of the playfield. This should be the aspect ratio of the game's resolution. - // - protected internal float CurrentAspect { get; private set; } = 16f / 9f; + /// + /// Aspect ratio of the playfield's resolution clamped between and + /// . + /// + protected internal float ClampedCurrentAspect { get; private set; } = 16f / 9f; - // - // The maximum relative height of the playfield. This is a fraction of the available area. - // + /// + /// The maximum relative height of the playfield. This is a fraction of the available area. + /// protected internal float MaximumRelativeHeight = 1f / 3f; - // - // The minimum relative height of the playfield. This is a fraction of the available area. - // + /// + /// The minimum relative height of the playfield. This is a fraction of the available area. + /// protected internal float MinimumRelativeHeight = 0f; - // - // Whether the playfield should be trimmed when the aspect ratio exceeds the maximum. - // + /// + /// Whether the playfield should be trimmed when the aspect ratio exceeds the maximum. + /// protected internal bool TrimOnOverflow = false; public TaikoPlayfieldAdjustmentContainer() @@ -68,6 +69,8 @@ namespace osu.Game.Rulesets.Taiko.UI // // As a middle-ground, the aspect ratio can still be adjusted in the downwards direction but has a maximum limit. // This is still a bit weird, because readability changes with window size, but it is what it is. + // + // This is separate from CurrentAspect as this needs to be the unbounded aspect ratio. float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; if (currentAspect > MaximumAspect) @@ -95,16 +98,14 @@ namespace osu.Game.Rulesets.Taiko.UI public double ComputeTimeRange() { - float currentAspect = Parent!.ChildSize.X / Parent!.ChildSize.Y; - - currentAspect = Math.Clamp(currentAspect, MinimumAspect, MaximumAspect); + ClampedCurrentAspect = Math.Clamp(Parent!.ChildSize.X / Parent!.ChildSize.Y, MinimumAspect, MaximumAspect); // in a game resolution of 1024x768, stable's scrolling system consists of objects being placed 600px (widthScaled - 40) away from their hit location. // however, the point at which the object renders at the end of the screen is exactly x=640, but stable makes the object start moving from beyond the screen instead of the boundary point. // therefore, in lazer we have to adjust the "in length" so that it's in a 640px->160px fashion before passing it down as a "time range". // see stable's "in length": https://github.com/peppy/osu-stable-reference/blob/013c3010a9d495e3471a9c59518de17006f9ad89/osu!/GameplayElements/HitObjectManagerTaiko.cs#L168 const float stable_hit_location = 160f; - float widthScaled = currentAspect * stable_gamefield_height; + float widthScaled = ClampedCurrentAspect * stable_gamefield_height; float inLength = widthScaled - stable_hit_location; // also in a game resolution of 1024x768, stable makes hit objects scroll from 760px->160px at a duration of 6000ms, divided by slider velocity (i.e. at a rate of 0.1px/ms) From a88b56a4dc0db8cdddbd860b12c2e48e59d74a60 Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 4 Mar 2024 18:51:38 +0800 Subject: [PATCH 13/14] Enable masking in bar line content container --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 6240f62b3b..376d42fc6e 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -121,8 +121,9 @@ namespace osu.Game.Rulesets.Taiko.UI new Container { Name = "Bar line content", + Masking = true, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = hit_target_width / 2 + hit_target_offset }, // To avoid barline appearing before the beginning of playfield when trimmed + Padding = new MarginPadding { Left = hit_target_width / 2 + hit_target_offset }, Children = new Drawable[] { UnderlayElements = new Container From 6c96f79913bef41bf439ce1bc11d2a884c84a8c0 Mon Sep 17 00:00:00 2001 From: vun Date: Mon, 11 Mar 2024 22:11:56 +0800 Subject: [PATCH 14/14] Implement NM fade-in --- .../Mods/TaikoModClassic.cs | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs index 642eee89f7..dad08cbc6b 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModClassic.cs @@ -17,6 +17,10 @@ namespace osu.Game.Rulesets.Taiko.Mods { public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset, IApplicableToDrawableHitObject { + private DrawableTaikoRuleset drawableTaikoRuleset = null!; + + private TaikoPlayfieldAdjustmentContainer adjustmentContainer = null!; + private IReadOnlyList mods = Array.Empty(); private const float hd_base_fade_out_duration = 0.375f; @@ -29,26 +33,45 @@ namespace osu.Game.Rulesets.Taiko.Mods private const float hidden_base_aspect = 4f / 3f; + /// + /// Time range at which notes start to become visible in milliseconds. + /// + private const double nm_fade_in_time_range = 10000d / 1.4d; + + /// + /// Duration notes take to fade in in milliseconds. + /// + private const float nm_fade_in_duration = 400f; + + /// + /// Whether note fading in is enabled. + /// + private bool fadeInEnabled = true; + private readonly BindableFloat hiddenFadeOutDuration = new BindableFloat(hd_base_fade_out_duration); private readonly BindableFloat hiddenInitialAlpha = new BindableFloat(hd_base_initial_alpha); public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { - var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; - var adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableTaikoRuleset.PlayfieldAdjustmentContainer; + drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; + adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableRuleset.PlayfieldAdjustmentContainer; // drawableRuleset.Mods should always be non-null here, but just in case. mods = drawableRuleset.Mods ?? mods; - adjustmentContainer.MaximumAspect = 22f / 9f; + // Disable uppper bound for playfield aspect ratio + // In stable, nomod time range is limited by fading notes in instead + adjustmentContainer.MaximumAspect = float.PositiveInfinity; adjustmentContainer.MinimumAspect = 5f / 4f; - adjustmentContainer.TrimOnOverflow = true; TaikoModHidden? hidden = mods.OfType().FirstOrDefault(); if (mods.OfType().Any()) { + // Hardrock disables note fading in + fadeInEnabled = false; + // For hardrock, the playfield time range is clamped to within classicMaxTimeRange and the equivalent // time range for a 16:10 aspect ratio. adjustmentContainer.TrimOnOverflow = false; @@ -67,39 +90,61 @@ namespace osu.Game.Rulesets.Taiko.Mods hiddenInitialAlpha.BindTo(hidden.InitialAlpha); hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); drawableRuleset.OnUpdate += d => adjustHidden( - d, hdhr_base_fade_out_duration, hdhr_base_initial_alpha, 16f / 9f, 0.8f); + hdhr_base_fade_out_duration, hdhr_base_initial_alpha, 16f / 9f, 0.8f); } } else if (hidden != null) { + // Hidden disables note fading in + fadeInEnabled = false; + // Stable limits the aspect ratio to 4:3 adjustmentContainer.MaximumAspect = hidden_base_aspect; + // Enable playfield trimming for hidden + adjustmentContainer.TrimOnOverflow = true; + // Enable aspect ratio adjustment for hidden (see adjustHidden) hiddenInitialAlpha.BindTo(hidden.InitialAlpha); hiddenFadeOutDuration.BindTo(hidden.FadeOutDuration); drawableRuleset.OnUpdate += d => adjustHidden( - d, hd_base_fade_out_duration, hd_base_initial_alpha, hidden_base_aspect); + hd_base_fade_out_duration, hd_base_initial_alpha, hidden_base_aspect); } } public void ApplyToDrawableHitObject(DrawableHitObject drawable) { if (drawable is DrawableTaikoHitObject hit) + { hit.SnapJudgementLocation = true; + + if (fadeInEnabled) + { + drawable.ApplyCustomUpdateState += fadeIn; + } + } + } + + // Fade in notes with fixed duration. + private void fadeIn(DrawableHitObject o, ArmedState state) + { + double preempt = nm_fade_in_time_range / drawableTaikoRuleset.ControlPointAt(o.HitObject.StartTime).Multiplier; + double start = o.HitObject.StartTime - preempt; + o.Alpha = 0; + + using (o.BeginAbsoluteSequence(start)) + { + o.FadeInFromZero(nm_fade_in_duration); + } } // Adjust hidden initial alpha and fade out duration for different aspect ratios private void adjustHidden( - Drawable drawableRuleset, float baseFadeOutDuration, float baseInitialAlpha, float baseAspect, float adjustmentRatio = 1f) { - var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset; - var adjustmentContainer = (TaikoPlayfieldAdjustmentContainer)drawableTaikoRuleset.PlayfieldAdjustmentContainer; - float clampedAspect = adjustmentContainer.ClampedCurrentAspect; float fadeOutDurationAdjustment = clampedAspect / baseAspect - 1;