1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-08 06:36:05 +08:00

Merge pull request #8556 from smoogipoo/mania-hitexplosion-skinning

Implement mania normal-note hit-explosion skinning
This commit is contained in:
Dean Herbert 2020-04-03 00:27:28 +09:00 committed by GitHub
commit aa74b3193e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 185 additions and 53 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="2020.331.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.402.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.402.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -3,42 +3,32 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
[TestFixture] [TestFixture]
public class TestSceneHitExplosion : OsuTestScene public class TestSceneHitExplosion : ManiaSkinnableTestScene
{ {
private ScrollingTestContainer scrolling;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(DrawableNote), typeof(DrawableNote),
typeof(DrawableManiaHitObject), 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; int runcount = 0;
AddRepeatStep("explode", () => AddRepeatStep("explode", () =>
@ -48,15 +38,29 @@ namespace osu.Game.Rulesets.Mania.Tests
if (runcount % 15 > 12) if (runcount % 15 > 12)
return; return;
scrolling.AddRange(new Drawable[] CreatedDrawables.OfType<Container>().ForEach(c =>
{ {
new HitExplosion((runcount / 15) % 2 == 0 ? new Color4(94, 0, 57, 255) : new Color4(6, 84, 0, 255), runcount % 6 != 0) 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, Anchor = Anchor.Centre,
} Origin = Anchor.Centre,
}));
}); });
}, 100); }, 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),
});
}
} }
} }

View File

@ -26,5 +26,6 @@ namespace osu.Game.Rulesets.Mania
HoldNoteHead, HoldNoteHead,
HoldNoteTail, HoldNoteTail,
HoldNoteBody, HoldNoteBody,
HitExplosion
} }
} }

View File

@ -0,0 +1,73 @@
// 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;
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<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Drawable explosion;
public LegacyHitExplosion()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
{
string imageName = GetManiaSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.ExplosionImage)?.Value
?? "lightingN";
float explosionScale = GetManiaSkinConfig<float>(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<ScrollingDirection> 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);
}
}
}

View File

@ -74,6 +74,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
case ManiaSkinComponents.HoldNoteBody: case ManiaSkinComponents.HoldNoteBody:
return new LegacyBodyPiece(); return new LegacyBodyPiece();
case ManiaSkinComponents.HitExplosion:
return new LegacyHitExplosion();
} }
break; break;

View File

@ -105,11 +105,15 @@ namespace osu.Game.Rulesets.Mania.UI
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value) if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
return; 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, RelativeSizeAxes = Axes.Both
Origin = Anchor.Centre };
});
hitObjectArea.Explosions.Add(explosion);
explosion.Delay(200).Expire(true);
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(ManiaAction action)

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{ {
public class ColumnHitObjectArea : HitObjectArea public class ColumnHitObjectArea : HitObjectArea
{ {
public readonly Container<HitExplosion> Explosions; public readonly Container Explosions;
private readonly Drawable hitTarget; private readonly Drawable hitTarget;
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer) public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
@ -25,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.UI.Components
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Depth = 1 Depth = 1
}, },
Explosions = new Container<HitExplosion> Explosions = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = -1, Depth = -1,
@ -38,15 +37,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
base.UpdateHitPosition(); base.UpdateHitPosition();
if (Direction.Value == ScrollingDirection.Up) if (Direction.Value == ScrollingDirection.Up)
{
hitTarget.Anchor = hitTarget.Origin = Anchor.TopLeft; hitTarget.Anchor = hitTarget.Origin = Anchor.TopLeft;
Explosions.Padding = new MarginPadding { Top = DefaultNotePiece.NOTE_HEIGHT / 2 };
}
else else
{
hitTarget.Anchor = hitTarget.Origin = Anchor.BottomLeft; hitTarget.Anchor = hitTarget.Origin = Anchor.BottomLeft;
Explosions.Padding = new MarginPadding { Bottom = DefaultNotePiece.NOTE_HEIGHT / 2 };
}
} }
} }
} }

View File

@ -1,26 +1,33 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // 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.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI namespace osu.Game.Rulesets.Mania.UI
{ {
public class HitExplosion : CompositeDrawable public class DefaultHitExplosion : CompositeDrawable
{ {
public override bool RemoveWhenNotAlive => true; public override bool RemoveWhenNotAlive => true;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly CircularContainer largeFaint; private readonly CircularContainer largeFaint;
private readonly CircularContainer mainGlow1; private readonly CircularContainer mainGlow1;
public HitExplosion(Color4 objectColour, bool isSmall = false) public DefaultHitExplosion(Color4 objectColour, bool isSmall = false)
{ {
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = DefaultNotePiece.NOTE_HEIGHT; 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() protected override void LoadComplete()
{ {
const double duration = 200; const double duration = 200;
@ -122,7 +136,20 @@ namespace osu.Game.Rulesets.Mania.UI
mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint); mainGlow1.ScaleTo(1.4f, duration, Easing.OutQuint);
this.FadeOut(duration, Easing.Out); this.FadeOut(duration, Easing.Out);
Expire(true); }
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
if (direction.NewValue == ScrollingDirection.Up)
{
Anchor = Anchor.TopCentre;
Y = DefaultNotePiece.NOTE_HEIGHT / 2;
}
else
{
Anchor = Anchor.BottomCentre;
Y = -DefaultNotePiece.NOTE_HEIGHT / 2;
}
} }
} }
} }

View File

@ -10,6 +10,16 @@ namespace osu.Game.Skinning
{ {
public class LegacyManiaSkinConfiguration : IHasCustomColours public class LegacyManiaSkinConfiguration : IHasCustomColours
{ {
/// <summary>
/// Conversion factor from converting legacy positioning values (based in x480 dimensions) to x768.
/// </summary>
public const float POSITION_SCALE_FACTOR = 1.6f;
/// <summary>
/// Size of a legacy column in the default skin, used for determining relative scale factors.
/// </summary>
public const float DEFAULT_COLUMN_SIZE = 30 * POSITION_SCALE_FACTOR;
public readonly int Keys; public readonly int Keys;
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>(); public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
@ -17,9 +27,10 @@ namespace osu.Game.Skinning
public readonly float[] ColumnLineWidth; public readonly float[] ColumnLineWidth;
public readonly float[] ColumnSpacing; public readonly float[] ColumnSpacing;
public readonly float[] ColumnWidth; public readonly float[] ColumnWidth;
public readonly float[] ExplosionWidth;
public float HitPosition = 124.8f; // (480 - 402) * 1.6f public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
public float LightPosition = 107.2f; // (480 - 413) * 1.6f public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
public bool ShowJudgementLine = true; public bool ShowJudgementLine = true;
public LegacyManiaSkinConfiguration(int keys) public LegacyManiaSkinConfiguration(int keys)
@ -29,9 +40,10 @@ namespace osu.Game.Skinning
ColumnLineWidth = new float[keys + 1]; ColumnLineWidth = new float[keys + 1];
ColumnSpacing = new float[keys - 1]; ColumnSpacing = new float[keys - 1];
ColumnWidth = new float[keys]; ColumnWidth = new float[keys];
ExplosionWidth = new float[keys];
ColumnLineWidth.AsSpan().Fill(2); ColumnLineWidth.AsSpan().Fill(2);
ColumnWidth.AsSpan().Fill(48); ColumnWidth.AsSpan().Fill(DEFAULT_COLUMN_SIZE);
} }
} }
} }

View File

@ -34,6 +34,8 @@ namespace osu.Game.Skinning
HoldNoteHeadImage, HoldNoteHeadImage,
HoldNoteTailImage, HoldNoteTailImage,
HoldNoteBodyImage, HoldNoteBodyImage,
ExplosionImage,
ExplosionScale,
ColumnLineColour ColumnLineColour
} }
} }

View File

@ -11,8 +11,6 @@ namespace osu.Game.Skinning
{ {
public class LegacyManiaSkinDecoder : LegacyDecoder<List<LegacyManiaSkinConfiguration>> public class LegacyManiaSkinDecoder : LegacyDecoder<List<LegacyManiaSkinConfiguration>>
{ {
private const float size_scale_factor = 1.6f;
public LegacyManiaSkinDecoder() public LegacyManiaSkinDecoder()
: base(1) : base(1)
{ {
@ -94,16 +92,20 @@ namespace osu.Game.Skinning
break; break;
case "HitPosition": 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; break;
case "LightPosition": 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; break;
case "JudgementLine": case "JudgementLine":
currentConfig.ShowJudgementLine = pair.Value == "1"; currentConfig.ShowJudgementLine = pair.Value == "1";
break; break;
case "LightingNWidth":
parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
break;
} }
} }
} }
@ -117,7 +119,7 @@ namespace osu.Game.Skinning
if (i >= output.Length) if (i >= output.Length)
break; break;
output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * size_scale_factor; output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
} }
} }
} }

View File

@ -194,6 +194,17 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookups.ShowJudgementLine: case LegacyManiaSkinConfigurationLookups.ShowJudgementLine:
return SkinUtils.As<TValue>(new Bindable<bool>(existing.ShowJudgementLine)); return SkinUtils.As<TValue>(new Bindable<bool>(existing.ShowJudgementLine));
case LegacyManiaSkinConfigurationLookups.ExplosionScale:
Debug.Assert(maniaLookup.TargetColumn != null);
if (GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
return SkinUtils.As<TValue>(new Bindable<float>(1));
if (existing.ExplosionWidth[maniaLookup.TargetColumn.Value] != 0)
return SkinUtils.As<TValue>(new Bindable<float>(existing.ExplosionWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnWidth[maniaLookup.TargetColumn.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
case LegacyManiaSkinConfigurationLookups.ColumnLineColour: case LegacyManiaSkinConfigurationLookups.ColumnLineColour:
return SkinUtils.As<TValue>(getCustomColour(existing, "ColourColumnLine")); return SkinUtils.As<TValue>(getCustomColour(existing, "ColourColumnLine"));
} }

View File

@ -15,7 +15,7 @@ namespace osu.Game.Skinning
public static class LegacySkinExtensions public static class LegacySkinExtensions
{ {
public static Drawable GetAnimation(this ISkin source, string componentName, bool animatable, bool looping, bool applyConfigFrameRate = false, string animationSeparator = "-", 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; Texture texture;
@ -27,7 +27,7 @@ namespace osu.Game.Skinning
{ {
var animation = new SkinnableTextureAnimation(startAtCurrentTime) var animation = new SkinnableTextureAnimation(startAtCurrentTime)
{ {
DefaultFrameLength = getFrameLength(source, applyConfigFrameRate, textures), DefaultFrameLength = frameLength ?? getFrameLength(source, applyConfigFrameRate, textures),
Repeat = looping, Repeat = looping,
}; };

View File

@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.331.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
<PackageReference Include="ppy.osu.Framework" Version="2020.402.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.402.0" />
<PackageReference Include="Sentry" Version="2.1.1" /> <PackageReference Include="Sentry" Version="2.1.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.331.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.402.1" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.402.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2020.402.0" />
</ItemGroup> </ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. --> <!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->