diff --git a/osu.Android.props b/osu.Android.props
index 067431596c..3e10e6cc4d 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
similarity index 53%
rename from osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
rename to osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
index 9a50bc3926..718dbbea93 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestSceneHitExplosion.cs
+++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs
@@ -3,42 +3,32 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
-using osu.Game.Rulesets.UI.Scrolling;
-using osu.Game.Tests.Visual;
+using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Tests
+namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
[TestFixture]
- public class TestSceneHitExplosion : OsuTestScene
+ public class TestSceneHitExplosion : ManiaSkinnableTestScene
{
- private ScrollingTestContainer scrolling;
-
public override IReadOnlyList RequiredTypes => new[]
{
typeof(DrawableNote),
typeof(DrawableManiaHitObject),
};
- protected override void LoadComplete()
+ public TestSceneHitExplosion()
{
- base.LoadComplete();
-
- Child = scrolling = new ScrollingTestContainer(ScrollingDirection.Down)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativePositionAxes = Axes.Y,
- Y = -0.25f,
- Size = new Vector2(Column.COLUMN_WIDTH, DefaultNotePiece.NOTE_HEIGHT),
- };
-
int runcount = 0;
AddRepeatStep("explode", () =>
@@ -48,15 +38,29 @@ namespace osu.Game.Rulesets.Mania.Tests
if (runcount % 15 > 12)
return;
- scrolling.AddRange(new Drawable[]
+ CreatedDrawables.OfType().ForEach(c =>
{
- new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- }
+ c.Add(new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion),
+ _ => new DefaultHitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ }));
});
}, 100);
}
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ SetContents(() => new ColumnTestContainer(0, ManiaAction.Key1)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativePositionAxes = Axes.Y,
+ Y = -0.25f,
+ Size = new Vector2(Column.COLUMN_WIDTH, DefaultNotePiece.NOTE_HEIGHT),
+ });
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
index dd1052ad0e..7d1c4ff8b3 100644
--- a/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
+++ b/osu.Game.Rulesets.Mania/ManiaSkinComponent.cs
@@ -26,5 +26,6 @@ namespace osu.Game.Rulesets.Mania
HoldNoteHead,
HoldNoteTail,
HoldNoteBody,
+ HitExplosion
}
}
diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
new file mode 100644
index 0000000000..4868dd87ef
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs
@@ -0,0 +1,73 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Animations;
+using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Mania.Skinning
+{
+ public class LegacyHitExplosion : LegacyManiaColumnElement
+ {
+ private readonly IBindable direction = new Bindable();
+
+ private Drawable explosion;
+
+ public LegacyHitExplosion()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
+ {
+ string imageName = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ExplosionImage)?.Value
+ ?? "lightingN";
+
+ float explosionScale = GetManiaSkinConfig(skin, LegacyManiaSkinConfigurationLookups.ExplosionScale)?.Value
+ ?? 1;
+
+ // Create a temporary animation to retrieve the number of frames, in an effort to calculate the intended frame length.
+ // This animation is discarded and re-queried with the appropriate frame length afterwards.
+ var tmp = skin.GetAnimation(imageName, true, false);
+ double frameLength = 0;
+ if (tmp is IAnimation tmpAnimation && tmpAnimation.FrameCount > 0)
+ frameLength = Math.Max(1000 / 60.0, 170.0 / tmpAnimation.FrameCount);
+
+ explosion = skin.GetAnimation(imageName, true, false, startAtCurrentTime: true, frameLength: frameLength).With(d =>
+ {
+ if (d == null)
+ return;
+
+ d.Origin = Anchor.Centre;
+ d.Blending = BlendingParameters.Additive;
+ d.Scale = new Vector2(explosionScale);
+ });
+
+ if (explosion != null)
+ InternalChild = explosion;
+
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (explosion != null)
+ explosion.Anchor = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ explosion?.FadeInFromZero(80)
+ .Then().FadeOut(120);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
index 3e423c6b0f..02fd6c0572 100644
--- a/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
+++ b/osu.Game.Rulesets.Mania/Skinning/ManiaLegacySkinTransformer.cs
@@ -74,6 +74,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
case ManiaSkinComponents.HoldNoteBody:
return new LegacyBodyPiece();
+
+ case ManiaSkinComponents.HitExplosion:
+ return new LegacyHitExplosion();
}
break;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 153345dde7..5a6cd7e229 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -105,11 +105,15 @@ namespace osu.Game.Rulesets.Mania.UI
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
return;
- hitObjectArea.Explosions.Add(new HitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick)
+ var explosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion), _ =>
+ new DefaultHitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick))
{
- Anchor = Direction.Value == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre,
- Origin = Anchor.Centre
- });
+ RelativeSizeAxes = Axes.Both
+ };
+
+ hitObjectArea.Explosions.Add(explosion);
+
+ explosion.Delay(200).Expire(true);
}
public bool OnPressed(ManiaAction action)
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index c3c69b0ff3..7d280f0bea 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
@@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
public class ColumnHitObjectArea : HitObjectArea
{
- public readonly Container Explosions;
+ public readonly Container Explosions;
private readonly Drawable hitTarget;
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
@@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
RelativeSizeAxes = Axes.X,
Depth = 1
},
- Explosions = new Container
+ Explosions = new Container
{
RelativeSizeAxes = Axes.Both,
Depth = -1,
@@ -38,15 +37,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
base.UpdateHitPosition();
if (Direction.Value == ScrollingDirection.Up)
- {
hitTarget.Anchor = hitTarget.Origin = Anchor.TopLeft;
- Explosions.Padding = new MarginPadding { Top = DefaultNotePiece.NOTE_HEIGHT / 2 };
- }
else
- {
hitTarget.Anchor = hitTarget.Origin = Anchor.BottomLeft;
- Explosions.Padding = new MarginPadding { Bottom = DefaultNotePiece.NOTE_HEIGHT / 2 };
- }
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
similarity index 81%
rename from osu.Game.Rulesets.Mania/UI/HitExplosion.cs
rename to osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
index 824b087cb9..7a047ed121 100644
--- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs
+++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs
@@ -1,26 +1,33 @@
// 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.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.UI.Scrolling;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI
{
- public class HitExplosion : CompositeDrawable
+ public class DefaultHitExplosion : CompositeDrawable
{
public override bool RemoveWhenNotAlive => true;
+ private readonly IBindable direction = new Bindable();
+
private readonly CircularContainer largeFaint;
private readonly CircularContainer mainGlow1;
- public HitExplosion(Color4 objectColour, bool isSmall = false)
+ public DefaultHitExplosion(Color4 objectColour, bool isSmall = false)
{
+ Origin = Anchor.Centre;
+
RelativeSizeAxes = Axes.X;
Height = DefaultNotePiece.NOTE_HEIGHT;
@@ -109,6 +116,13 @@ namespace osu.Game.Rulesets.Mania.UI
};
}
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
+ direction.BindTo(scrollingInfo.Direction);
+ direction.BindValueChanged(onDirectionChanged, true);
+ }
+
protected override void LoadComplete()
{
const double duration = 200;
@@ -122,7 +136,20 @@ namespace osu.Game.Rulesets.Mania.UI
mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint);
this.FadeOut(duration, Easing.Out);
- Expire(true);
+ }
+
+ private void onDirectionChanged(ValueChangedEvent direction)
+ {
+ if (direction.NewValue == ScrollingDirection.Up)
+ {
+ Anchor = Anchor.TopCentre;
+ Y = DefaultNotePiece.NOTE_HEIGHT / 2;
+ }
+ else
+ {
+ Anchor = Anchor.BottomCentre;
+ Y = -DefaultNotePiece.NOTE_HEIGHT / 2;
+ }
}
}
}
diff --git a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
index 95886fa97f..ac257b8c80 100644
--- a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
+++ b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
@@ -10,6 +10,16 @@ namespace osu.Game.Skinning
{
public class LegacyManiaSkinConfiguration : IHasCustomColours
{
+ ///
+ /// Conversion factor from converting legacy positioning values (based in x480 dimensions) to x768.
+ ///
+ public const float POSITION_SCALE_FACTOR = 1.6f;
+
+ ///
+ /// Size of a legacy column in the default skin, used for determining relative scale factors.
+ ///
+ public const float DEFAULT_COLUMN_SIZE = 30 * POSITION_SCALE_FACTOR;
+
public readonly int Keys;
public Dictionary CustomColours { get; set; } = new Dictionary();
@@ -17,9 +27,10 @@ namespace osu.Game.Skinning
public readonly float[] ColumnLineWidth;
public readonly float[] ColumnSpacing;
public readonly float[] ColumnWidth;
+ public readonly float[] ExplosionWidth;
- public float HitPosition = 124.8f; // (480 - 402) * 1.6f
- public float LightPosition = 107.2f; // (480 - 413) * 1.6f
+ public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
+ public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
public bool ShowJudgementLine = true;
public LegacyManiaSkinConfiguration(int keys)
@@ -29,9 +40,10 @@ namespace osu.Game.Skinning
ColumnLineWidth = new float[keys + 1];
ColumnSpacing = new float[keys - 1];
ColumnWidth = new float[keys];
+ ExplosionWidth = new float[keys];
ColumnLineWidth.AsSpan().Fill(2);
- ColumnWidth.AsSpan().Fill(48);
+ ColumnWidth.AsSpan().Fill(DEFAULT_COLUMN_SIZE);
}
}
}
diff --git a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs
index 3cccb71745..853d07c060 100644
--- a/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs
+++ b/osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs
@@ -34,6 +34,8 @@ namespace osu.Game.Skinning
HoldNoteHeadImage,
HoldNoteTailImage,
HoldNoteBodyImage,
+ ExplosionImage,
+ ExplosionScale,
ColumnLineColour
}
}
diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs
index f290e705fa..3393fe09b3 100644
--- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs
+++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs
@@ -11,8 +11,6 @@ namespace osu.Game.Skinning
{
public class LegacyManiaSkinDecoder : LegacyDecoder>
{
- private const float size_scale_factor = 1.6f;
-
public LegacyManiaSkinDecoder()
: base(1)
{
@@ -94,16 +92,20 @@ namespace osu.Game.Skinning
break;
case "HitPosition":
- currentConfig.HitPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * size_scale_factor;
+ currentConfig.HitPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
break;
case "LightPosition":
- currentConfig.LightPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * size_scale_factor;
+ currentConfig.LightPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
break;
case "JudgementLine":
currentConfig.ShowJudgementLine = pair.Value == "1";
break;
+
+ case "LightingNWidth":
+ parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
+ break;
}
}
}
@@ -117,7 +119,7 @@ namespace osu.Game.Skinning
if (i >= output.Length)
break;
- output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * size_scale_factor;
+ output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
}
}
}
diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs
index a51556fa77..3d3eac97f6 100644
--- a/osu.Game/Skinning/LegacySkin.cs
+++ b/osu.Game/Skinning/LegacySkin.cs
@@ -194,6 +194,17 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookups.ShowJudgementLine:
return SkinUtils.As(new Bindable(existing.ShowJudgementLine));
+ case LegacyManiaSkinConfigurationLookups.ExplosionScale:
+ Debug.Assert(maniaLookup.TargetColumn != null);
+
+ if (GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
+ return SkinUtils.As(new Bindable(1));
+
+ if (existing.ExplosionWidth[maniaLookup.TargetColumn.Value] != 0)
+ return SkinUtils.As(new Bindable(existing.ExplosionWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
+
+ return SkinUtils.As(new Bindable(existing.ColumnWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
+
case LegacyManiaSkinConfigurationLookups.ColumnLineColour:
return SkinUtils.As(getCustomColour(existing, "ColourColumnLine"));
}
diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs
index a736174f13..ea3d180ef8 100644
--- a/osu.Game/Skinning/LegacySkinExtensions.cs
+++ b/osu.Game/Skinning/LegacySkinExtensions.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Skinning
public static class LegacySkinExtensions
{
public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-",
- bool startAtCurrentTime = false)
+ bool startAtCurrentTime = false, double? frameLength = null)
{
Texture texture;
@@ -27,7 +27,7 @@ namespace osu.Game.Skinning
{
var animation = new SkinnableTextureAnimation(startAtCurrentTime)
{
- DefaultFrameLength = getFrameLength(source, applyConfigFrameRate, textures),
+ DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
Repeat = looping,
};
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 4597d212f3..073799f08f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 27e485709b..6578aec69f 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+