1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 10:18:22 +08:00

Merge pull request #25190 from frenzibyte/match-skin-element-animation-support

Match capability of animating legacy skin elements with osu!(stable)
This commit is contained in:
Dean Herbert 2023-10-30 18:25:06 +09:00 committed by GitHub
commit fbba3787d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 65 additions and 43 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -4,6 +4,7 @@ Version: 2.5
[Mania]
Keys: 4
ColumnLineWidth: 3,1,3,1,1
LightFramePerSecond: 15
// some skins found in the wild had configuration keys where the @2x suffix was included in the values.
// the expected compatibility behaviour is that the presence of the @2x suffix shouldn't change anything
// if @2x assets are present.
@ -15,5 +16,6 @@ Hit300: mania/hit300@2x
Hit300g: mania/hit300g@2x
StageLeft: mania/stage-left
StageRight: mania/stage-right
StageLight: mania/stage-light
NoteImage0L: LongNoteTailWang
NoteImage1L: LongNoteTailWang

View File

@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
direction.BindTo(scrollingInfo.Direction);
isHitting.BindTo(holdNote.IsHitting);
bodySprite = skin.GetAnimation(imageName, wrapMode, wrapMode, true, true).With(d =>
bodySprite = skin.GetAnimation(imageName, wrapMode, wrapMode, true, true, frameLength: 30).With(d =>
{
if (d == null)
return;

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI.Scrolling;
@ -20,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Container lightContainer = null!;
private Sprite light = null!;
private Drawable light = null!;
public LegacyColumnBackground()
{
@ -39,6 +38,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
Color4 lightColour = GetColumnSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLightColour)?.Value
?? Color4.White;
int lightFramePerSecond = skin.GetManiaSkinConfig<int>(LegacyManiaSkinConfigurationLookups.LightFramePerSecond)?.Value ?? 60;
InternalChildren = new[]
{
lightContainer = new Container
@ -46,16 +47,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Bottom = lightPosition },
Child = light = new Sprite
Child = light = skin.GetAnimation(lightImage, true, true, frameLength: 1000d / lightFramePerSecond)?.With(l =>
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Colour = LegacyColourCompatibility.DisallowZeroAlpha(lightColour),
Texture = skin.GetTexture(lightImage),
RelativeSizeAxes = Axes.X,
Width = 1,
Alpha = 0
}
l.Anchor = Anchor.BottomCentre;
l.Origin = Anchor.BottomCentre;
l.Colour = LegacyColourCompatibility.DisallowZeroAlpha(lightColour);
l.RelativeSizeAxes = Axes.X;
l.Width = 1;
l.Alpha = 0;
}) ?? Empty(),
}
};

View File

@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
string filename = this.GetManiaSkinConfig<string>(hit_result_mapping[result])?.Value
?? default_hit_result_skin_filenames[result];
var animation = this.GetAnimation(filename, true, true);
var animation = this.GetAnimation(filename, true, true, frameLength: 1000 / 20d);
return animation == null ? null : new LegacyManiaJudgementPiece(result, animation);
}

View File

@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Child = piece = new TestLegacyMainCirclePiece(priorityLookup),
};
var sprites = this.ChildrenOfType<Sprite>().Where(s => !string.IsNullOrEmpty(s.Texture.AssetName)).DistinctBy(s => s.Texture.AssetName).ToArray();
var sprites = this.ChildrenOfType<Sprite>().Where(s => !string.IsNullOrEmpty(s.Texture?.AssetName)).DistinctBy(s => s.Texture.AssetName).ToArray();
Debug.Assert(sprites.Length <= 2);
});
@ -103,8 +103,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private partial class TestLegacyMainCirclePiece : LegacyMainCirclePiece
{
public new Sprite? CircleSprite => base.CircleSprite.ChildrenOfType<Sprite>().DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
public new Sprite? OverlaySprite => base.OverlaySprite.ChildrenOfType<Sprite>().DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
public new Sprite? CircleSprite => base.CircleSprite.ChildrenOfType<Sprite>().Where(s => !string.IsNullOrEmpty(s.Texture?.AssetName)).DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
public new Sprite? OverlaySprite => base.OverlaySprite.ChildrenOfType<Sprite>().Where(s => !string.IsNullOrEmpty(s.Texture?.AssetName)).DistinctBy(s => s.Texture.AssetName).SingleOrDefault();
public TestLegacyMainCirclePiece(string? priorityLookupPrefix)
: base(priorityLookupPrefix, false)

View File

@ -14,6 +14,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
@ -62,12 +63,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
// otherwise fall back to the default prefix "hitcircle".
string circleName = (priorityLookupPrefix != null && skin.GetTexture(priorityLookupPrefix) != null) ? priorityLookupPrefix : @"hitcircle";
Vector2 maxSize = OsuHitObject.OBJECT_DIMENSIONS * 2;
// at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it.
// the conditional above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png.
InternalChildren = new[]
{
CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName)?.WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2) })
CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName)?.WithMaximumSize(maxSize) })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -76,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => skin.GetAnimation(@$"{circleName}overlay", true, true, frameLength: 1000 / 2d, maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2))
Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(@$"{circleName}overlay")?.WithMaximumSize(maxSize) })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
@ -41,11 +42,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null);
InternalChild = arrow = (skin?.GetAnimation(lookupName, true, true, maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2) ?? Empty()).With(d =>
InternalChild = arrow = new Sprite
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
});
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = skin?.GetTexture(lookupName)?.WithMaximumSize(maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2),
};
textureIsDefaultSkin = skin is ISkinTransformer transformer && transformer.Skin is DefaultLegacySkin;

View File

@ -48,40 +48,45 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
[BackgroundDependencyLoader]
private void load(ISkinSource skin, DrawableHitObject drawableHitObject, IBeatSyncProvider? beatSyncProvider)
{
Drawable? getDrawableFor(string lookup)
Drawable? getDrawableFor(string lookup, bool animatable)
{
const string normal_hit = "taikohit";
const string big_hit = "taikobig";
string prefix = ((drawableHitObject.HitObject as TaikoStrongableHitObject)?.IsStrong ?? false) ? big_hit : normal_hit;
return skin.GetAnimation($"{prefix}{lookup}", true, false, maxSize: max_circle_sprite_size) ??
return skin.GetAnimation($"{prefix}{lookup}", animatable, false, maxSize: max_circle_sprite_size) ??
// fallback to regular size if "big" version doesn't exist.
skin.GetAnimation($"{normal_hit}{lookup}", true, false, maxSize: max_circle_sprite_size);
skin.GetAnimation($"{normal_hit}{lookup}", animatable, false, maxSize: max_circle_sprite_size);
}
// backgroundLayer is guaranteed to exist due to the pre-check in TaikoLegacySkinTransformer.
AddInternal(backgroundLayer = new LegacyKiaiFlashingDrawable(() => getDrawableFor("circle")));
AddInternal(backgroundLayer = new LegacyKiaiFlashingDrawable(() => getDrawableFor("circle", false))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
});
foregroundLayer = getDrawableFor("circleoverlay", true);
foregroundLayer = getDrawableFor("circleoverlay");
if (foregroundLayer != null)
{
foregroundLayer.Anchor = Anchor.Centre;
foregroundLayer.Origin = Anchor.Centre;
// Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat).
// For now just stop at first frame for sanity.
if (foregroundLayer is IFramedAnimation animatedForegroundLayer)
animatedForegroundLayer.Stop();
AddInternal(foregroundLayer);
}
drawableHitObject.StartTimeBindable.BindValueChanged(startTime =>
{
timingPoint = beatSyncProvider?.ControlPoints?.TimingPointAt(startTime.NewValue) ?? TimingControlPoint.DEFAULT;
}, true);
// Animations in taiko skins are used in a custom way (>150 combo and animating in time with beat).
// For now just stop at first frame for sanity.
foreach (var c in InternalChildren)
{
(c as IFramedAnimation)?.Stop();
c.Anchor = Anchor.Centre;
c.Origin = Anchor.Centre;
}
if (gameplayState != null)
currentCombo.BindTo(gameplayState.ScoreProcessor.Combo);
}
@ -101,11 +106,11 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
foreach (var c in InternalChildren)
c.Scale = new Vector2(DrawHeight / circle_piece_size.Y);
if (foregroundLayer is IFramedAnimation animatableForegroundLayer)
animateForegroundLayer(animatableForegroundLayer);
if (foregroundLayer is IFramedAnimation animatedForegroundLayer)
animateForegroundLayer(animatedForegroundLayer);
}
private void animateForegroundLayer(IFramedAnimation animatableForegroundLayer)
private void animateForegroundLayer(IFramedAnimation animation)
{
int multiplier;
@ -119,12 +124,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
}
else
{
animatableForegroundLayer.GotoFrame(0);
animation.GotoFrame(0);
return;
}
animationFrame = Math.Abs(Time.Current - timingPoint.Time) % ((timingPoint.BeatLength * 2) / multiplier) >= timingPoint.BeatLength / multiplier ? 0 : 1;
animatableForegroundLayer.GotoFrame(animationFrame);
animation.GotoFrame(animationFrame);
}
private Color4 accentColour;

View File

@ -40,6 +40,7 @@ namespace osu.Game.Skinning
public float ScorePosition = 300 * POSITION_SCALE_FACTOR;
public bool ShowJudgementLine = true;
public bool KeysUnderNotes;
public int LightFramePerSecond = 60;
public LegacyNoteBodyStyle? NoteBodyStyle;

View File

@ -74,6 +74,7 @@ namespace osu.Game.Skinning
Hit50,
Hit0,
KeysUnderNotes,
NoteBodyStyle
NoteBodyStyle,
LightFramePerSecond
}
}

View File

@ -123,6 +123,11 @@ namespace osu.Game.Skinning
currentConfig.WidthForNoteHeightScale = (float.Parse(pair.Value, CultureInfo.InvariantCulture)) * LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR;
break;
case "LightFramePerSecond":
int lightFramePerSecond = int.Parse(pair.Value, CultureInfo.InvariantCulture);
currentConfig.LightFramePerSecond = lightFramePerSecond > 0 ? lightFramePerSecond : 24;
break;
case string when pair.Key.StartsWith("Colour", StringComparison.Ordinal):
HandleColours(currentConfig, line, true);
break;

View File

@ -273,6 +273,9 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookups.KeysUnderNotes:
return SkinUtils.As<TValue>(new Bindable<bool>(existing.KeysUnderNotes));
case LegacyManiaSkinConfigurationLookups.LightFramePerSecond:
return SkinUtils.As<TValue>(new Bindable<int>(existing.LightFramePerSecond));
}
return null;