diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index f0127e30ec..ca124e9214 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -6,33 +6,29 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { public ApproachCircle ApproachCircle; - private readonly CirclePiece circle; - private readonly RingPiece ring; - private readonly FlashPiece flash; - private readonly ExplodePiece explode; - private readonly NumberPiece number; - private readonly GlowPiece glow; private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); - public OsuAction? HitAction => circle.HitAction; - - private readonly Container explodeContainer; + public OsuAction? HitAction => hitArea.HitAction; private readonly Container scaleContainer; + private readonly HitArea hitArea; + public DrawableHitCircle(HitCircle h) : base(h) { @@ -47,44 +43,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both, Origin = Anchor.Centre, Anchor = Anchor.Centre, - Child = explodeContainer = new Container + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Children = new Drawable[] + hitArea = new HitArea { - glow = new GlowPiece(), - circle = new CirclePiece + Hit = () => { - Hit = () => - { - if (AllJudged) - return false; + if (AllJudged) + return false; - UpdateResult(true); - return true; - }, + UpdateResult(true); + return true; }, - number = new NumberPiece - { - Text = (HitObject.IndexInCurrentCombo + 1).ToString(), - }, - ring = new RingPiece(), - flash = new FlashPiece(), - explode = new ExplodePiece(), - ApproachCircle = new ApproachCircle - { - Alpha = 0, - Scale = new Vector2(4), - } + }, + new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)), + ApproachCircle = new ApproachCircle + { + Alpha = 0, + Scale = new Vector2(4), } } }, }; - //may not be so correct - Size = circle.DrawSize; + Size = hitArea.DrawSize; } [BackgroundDependencyLoader] @@ -98,13 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables stackHeightBindable.BindTo(HitObject.StackHeightBindable); scaleBindable.BindTo(HitObject.ScaleBindable); - AccentColour.BindValueChanged(colour => - { - explode.Colour = colour.NewValue; - glow.Colour = colour.NewValue; - circle.Colour = colour.NewValue; - ApproachCircle.Colour = colour.NewValue; - }, true); + AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true); } protected override void CheckForResult(bool userTriggered, double timeOffset) @@ -139,8 +115,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void UpdateStateTransforms(ArmedState state) { - glow.FadeOut(400); - switch (state) { case ArmedState.Idle: @@ -148,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Expire(true); - circle.HitAction = null; + hitArea.HitAction = null; // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); @@ -163,29 +137,50 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Hit: ApproachCircle.FadeOut(50); - const double flash_in = 40; - flash.FadeTo(0.8f, flash_in) - .Then() - .FadeOut(100); - - explode.FadeIn(flash_in); - explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad); - - using (BeginDelayedSequence(flash_in, true)) - { - //after the flash, we can hide some elements that were behind it - ring.FadeOut(); - circle.FadeOut(); - number.FadeOut(); - - this.FadeOut(800); - } - - Expire(); + // todo: temporary / arbitrary + this.Delay(800).Expire(); break; } } public Drawable ProxiedLayer => ApproachCircle; + + private class HitArea : Drawable, IKeyBindingHandler + { + // IsHovered is used + public override bool HandlePositionalInput => true; + + public Func Hit; + + public OsuAction? HitAction; + + public HitArea() + { + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + public bool OnPressed(OsuAction action) + { + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + if (IsHovered && (Hit?.Invoke() ?? false)) + { + HitAction = action; + return true; + } + + break; + } + + return false; + } + + public bool OnReleased(OsuAction action) => false; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 579f16e0d4..a89fb8b682 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Objects.Drawables; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index dc0b149140..c92937ef09 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -1,24 +1,17 @@ // 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.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Bindings; -using osu.Game.Skinning; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class CirclePiece : Container, IKeyBindingHandler + public class CirclePiece : CompositeDrawable { - // IsHovered is used - public override bool HandlePositionalInput => true; - - public Func Hit; - - public OsuAction? HitAction; - public CirclePiece() { Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); @@ -27,28 +20,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - - InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); } - public bool OnPressed(OsuAction action) + [BackgroundDependencyLoader] + private void load(TextureStore textures) { - switch (action) + InternalChildren = new Drawable[] { - case OsuAction.LeftButton: - case OsuAction.RightButton: - if (IsHovered && (Hit?.Invoke() ?? false)) - { - HitAction = action; - return true; - } - - break; - } - - return false; + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(@"Play/osu/disc"), + }, + new TrianglesPiece + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + } + }; } - - public bool OnReleased(OsuAction action) => false; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs deleted file mode 100644 index 047ff943ff..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class DefaultCirclePiece : Container - { - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - RelativeSizeAxes = Axes.Both; - Children = new Drawable[] - { - new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = textures.Get(@"Play/osu/disc"), - }, - new TrianglesPiece - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Alpha = 0.5f, - } - }; - } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs new file mode 100644 index 0000000000..944c93bb6d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs @@ -0,0 +1,94 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class MainCirclePiece : CompositeDrawable + { + private readonly CirclePiece circle; + private readonly RingPiece ring; + private readonly FlashPiece flash; + private readonly ExplodePiece explode; + private readonly NumberPiece number; + private readonly GlowPiece glow; + + public MainCirclePiece(int index) + { + Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece(), + number = new NumberPiece + { + Text = (index + 1).ToString(), + }, + ring = new RingPiece(), + flash = new FlashPiece(), + explode = new ExplodePiece(), + }; + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject) + { + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => + { + explode.Colour = colour.NewValue; + glow.Colour = colour.NewValue; + circle.Colour = colour.NewValue; + }, true); + } + + private void updateState(ValueChangedEvent state) + { + glow.FadeOut(400); + + switch (state.NewValue) + { + case ArmedState.Hit: + const double flash_in = 40; + const double flash_out = 100; + + flash.FadeTo(0.8f, flash_in) + .Then() + .FadeOut(flash_out); + + explode.FadeIn(flash_in); + this.ScaleTo(1.5f, 400, Easing.OutQuad); + + using (BeginDelayedSequence(flash_in, true)) + { + //after the flash, we can hide some elements that were behind it + ring.FadeOut(); + circle.FadeOut(); + number.FadeOut(); + + this.FadeOut(800); + } + + break; + } + } + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index af9a24df42..c381c6293b 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -6,15 +6,22 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Database; +using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osuTK; +using osuTK.Graphics; namespace osu.Game.Skinning { @@ -36,6 +43,8 @@ namespace osu.Game.Skinning { } + private readonly bool hasHitCircle; + protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin) { @@ -49,8 +58,6 @@ namespace osu.Game.Skinning Samples = audioManager.GetSampleStore(storage); Textures = new TextureStore(new TextureLoaderStore(storage)); - bool hasHitCircle = false; - using (var testStream = storage.GetStream("hitcircle")) hasHitCircle |= testStream != null; @@ -71,6 +78,12 @@ namespace osu.Game.Skinning { switch (componentName) { + case "Play/osu/hitcircle": + if (!hasHitCircle) + return null; + + return new LegacyMainCirclePiece(); + case "Play/Miss": componentName = "hit0"; break; @@ -243,5 +256,70 @@ namespace osu.Game.Skinning return texture; } } + + public class LegacyMainCirclePiece : CompositeDrawable + { + public LegacyMainCirclePiece() + { + Size = new Vector2(128); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + private readonly IBindable state = new Bindable(); + + private readonly Bindable accentColour = new Bindable(); + + [BackgroundDependencyLoader] + private void load(DrawableHitObject drawableObject, ISkinSource skin) + { + Sprite hitCircleSprite; + + InternalChildren = new Drawable[] + { + hitCircleSprite = new Sprite + { + Texture = skin.GetTexture("hitcircle"), + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText + { + Font = OsuFont.Numeric.With(size: 40), + UseFullGlyphHeight = false, + }, confineMode: ConfineMode.NoScaling) + { + Text = (((IHasComboInformation)drawableObject.HitObject).IndexInCurrentCombo + 1).ToString() + }, + new Sprite + { + Texture = skin.GetTexture("hitcircleoverlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + + state.BindTo(drawableObject.State); + state.BindValueChanged(updateState, true); + + accentColour.BindTo(drawableObject.AccentColour); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); + } + + private void updateState(ValueChangedEvent state) + { + const double legacy_fade_duration = 240; + + switch (state.NewValue) + { + case ArmedState.Hit: + this.FadeOut(legacy_fade_duration, Easing.Out); + this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + break; + } + } + } } }