diff --git a/osu.Game.Rulesets.Karaoke/Edit/DrawableKaraokeEditRuleset.cs b/osu.Game.Rulesets.Karaoke/Edit/DrawableKaraokeEditRuleset.cs index 5582697a2..7f6c13e05 100644 --- a/osu.Game.Rulesets.Karaoke/Edit/DrawableKaraokeEditRuleset.cs +++ b/osu.Game.Rulesets.Karaoke/Edit/DrawableKaraokeEditRuleset.cs @@ -30,19 +30,7 @@ public DrawableKaraokeEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyLi { } - public override DrawableHitObject CreateDrawableRepresentation(KaraokeHitObject h) - { - switch (h) - { - case Lyric lyric: - return new DrawableLyric(lyric); - - case Note note: - return new DrawableNote(note); - } - - return null; - } + public override DrawableHitObject CreateDrawableRepresentation(KaraokeHitObject h) => null; protected override bool OnKeyDown(KeyDownEvent e) { diff --git a/osu.Game.Rulesets.Karaoke/Objects/BarLine.cs b/osu.Game.Rulesets.Karaoke/Objects/BarLine.cs index 988c52b5e..d2e2068f6 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/BarLine.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/BarLine.cs @@ -1,12 +1,22 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Bindables; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Karaoke.Objects { public class BarLine : KaraokeHitObject, IBarLine { - public bool Major { get; set; } + public bool Major + { + get => MajorBindable.Value; + set => MajorBindable.Value = value; + } + + public readonly Bindable MajorBindable = new BindableBool(); + + public override Judgement CreateJudgement() => new IgnoreJudgement(); } } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableBarLine.cs index df6776aae..ad1b619b5 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableBarLine.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableBarLine.cs @@ -1,7 +1,11 @@ // Copyright (c) andy840119 . Licensed under the GPL Licence. // See the LICENCE file in the repository root for full licence text. +using JetBrains.Annotations; +using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osuTK; using osuTK.Graphics; @@ -24,46 +28,96 @@ public class DrawableBarLine : DrawableKaraokeScrollingHitObject /// private const float triangle_offset = 9; - public DrawableBarLine(BarLine barLine) + /// + /// The visual line tracker. + /// + private Box line; + + /// + /// Container with triangles. Only visible for major lines. + /// + private Container triangleContainer; + + private readonly Bindable major = new Bindable(); + + public DrawableBarLine() + : this(null) + { + } + + public DrawableBarLine([CanBeNull] BarLine barLine) : base(barLine) + { + } + + [BackgroundDependencyLoader] + private void load() { RelativeSizeAxes = Axes.Y; Width = 2f; - AddInternal(new Box - { - Name = "Bar line", - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - Colour = new Color4(255, 204, 33, 255), - }); - - if (barLine.Major) + AddRangeInternal(new Drawable[] { - AddInternal(new EquilateralTriangle + line = new Box { - Name = "Up triangle", - Anchor = Anchor.TopCentre, - Origin = Anchor.Centre, - Size = new Vector2(triangle_width), - Y = -triangle_offset, - Rotation = 180 - }); - - AddInternal(new EquilateralTriangle + Name = "Bar line", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Colour = new Color4(255, 204, 33, 255), + }, + triangleContainer = new Container { - Name = "Down triangle", - Anchor = Anchor.BottomCentre, + Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(triangle_width), - Y = triangle_offset, - Rotation = 0 - }); - } - - if (!barLine.Major) - Alpha = 0.2f; + RelativeSizeAxes = Axes.Both, + Children = new [] + { + new EquilateralTriangle + { + Name = "Up triangle", + Anchor = Anchor.TopCentre, + Origin = Anchor.Centre, + Size = new Vector2(triangle_width), + Y = -triangle_offset, + Rotation = 180 + }, + new EquilateralTriangle + { + Name = "Down triangle", + Anchor = Anchor.BottomCentre, + Origin = Anchor.Centre, + Size = new Vector2(triangle_width), + Y = triangle_offset, + Rotation = 0 + } + } + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + major.BindValueChanged(updateMajor, true); + } + + private void updateMajor(ValueChangedEvent major) + { + line.Alpha = major.NewValue ? 1f : 0.75f; + triangleContainer.Alpha = major.NewValue ? 1 : 0; + } + + protected override void OnApply() + { + base.OnApply(); + major.BindTo(HitObject.MajorBindable); + } + + protected override void OnFree() + { + base.OnFree(); + major.UnbindFrom(HitObject.MajorBindable); } protected override void UpdateInitialTransforms() diff --git a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableKaraokeScrollingHitObject.cs b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableKaraokeScrollingHitObject.cs index 0e95123f8..d15381d5b 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableKaraokeScrollingHitObject.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableKaraokeScrollingHitObject.cs @@ -59,12 +59,11 @@ protected override void UpdateInitialTransforms() public abstract class DrawableKaraokeScrollingHitObject : DrawableKaraokeScrollingHitObject where TObject : KaraokeHitObject { - public new readonly TObject HitObject; + public new TObject HitObject => base.HitObject as TObject; protected DrawableKaraokeScrollingHitObject(TObject hitObject) : base(hitObject) { - HitObject = hitObject; } } } diff --git a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableLyric.cs b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableLyric.cs index 83f514c23..0c442ed4c 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableLyric.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableLyric.cs @@ -59,18 +59,14 @@ private void load() { Scale = new Vector2(2f); AutoSizeAxes = Axes.Both; - InternalChildren = new Drawable[] - { - KaraokeText = new KarakeSpriteText(), - translateText = new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - } - }; - - LifetimeEnd = HitObject.EndTime + 1000; + AddInternal(KaraokeText = new KarakeSpriteText()); + AddInternal(translateText = new OsuSpriteText + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + }); + TextBindable.BindValueChanged(text => { KaraokeText.Text = text.NewValue; }); TimeTagsBindable.BindValueChanged(timeTags => { KaraokeText.TimeTags = TimeTagsUtils.ToDictionary(timeTags.NewValue); }); RubyTagsBindable.BindValueChanged(rubyTags => { ApplyRuby(); }); @@ -106,18 +102,6 @@ protected override void OnFree() TranslateTextBindable.UnbindFrom(HitObject.TranslateTextBindable); } - protected override void UpdateInitialTransforms() - { - base.UpdateInitialTransforms(); - - // Manually set to reduce the number of future alive objects to a bare minimum. - LifetimeStart = HitObject.StartTime - HitObject.TimePreempt; - } - - protected override void ClearInternal(bool disposeChildren = true) - { - } - protected virtual void ApplyRuby() { KaraokeText.Rubies = DisplayRuby ? HitObject.RubyTags?.Select(x => new PositionText(x.Text, x.StartIndex, x.EndIndex)).ToArray() : null; @@ -148,6 +132,9 @@ protected override void ApplySkin(ISkinSource skin, bool allowFallback) if (CurrentSkin == null) return; + if (HitObject == null) + return; + skin.GetConfig(new KaraokeSkinLookup(KaraokeSkinConfiguration.LyricStyle, HitObject.Singers))?.BindValueChanged(karaokeFont => { if (karaokeFont.NewValue != null) @@ -254,28 +241,6 @@ protected override void UpdateStartTimeStateTransforms() } } - public override double LifetimeStart - { - get => base.LifetimeStart; - set - { - base.LifetimeStart = value; - KaraokeText.LifetimeStart = value; - translateText.LifetimeStart = value; - } - } - - public override double LifetimeEnd - { - get => base.LifetimeEnd; - set - { - base.LifetimeEnd = value; - KaraokeText.LifetimeEnd = value; - translateText.LifetimeEnd = value; - } - } - private bool displayRuby; public bool DisplayRuby diff --git a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableNote.cs index 9913ce984..61ed645f7 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Drawables/DrawableNote.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System.Diagnostics; +using JetBrains.Annotations; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; @@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Karaoke.Objects.Drawables /// public class DrawableNote : DrawableKaraokeScrollingHitObject, IKeyBindingHandler { - private readonly OsuSpriteText textPiece; + private OsuSpriteText textPiece; /// /// Time at which the user started holding this hold note. Null if the user is not holding this hold note. @@ -33,12 +35,19 @@ public class DrawableNote : DrawableKaraokeScrollingHitObject, IKeyBinding private readonly Bindable isHitting = new Bindable(); - public IBindable Display => HitObject.DisplayBindable; + public readonly IBindable TextBindable = new Bindable(); + public readonly IBindable AlternativeTextBindable = new Bindable(); + public readonly IBindable SingersBindable = new Bindable(); + public readonly IBindable DisplayBindable = new Bindable(); + public readonly IBindable ToneBindable = new Bindable(); - public IBindable Singers => HitObject.SingersBindable; + public DrawableNote() + : this(null) + { + } - public DrawableNote(Note note) - : base(note) + public DrawableNote([CanBeNull] Note hitObject) + : base(hitObject) { Height = DefaultColumnBackground.COLUMN_HEIGHT; @@ -55,14 +64,37 @@ public DrawableNote(Note note) bodyPiece.AccentColour = colour.NewValue; }, true); */ + } - Display.BindValueChanged(e => { (Result.Judgement as KaraokeNoteJudgement).Saitenable = e.NewValue; }); + [BackgroundDependencyLoader] + private void load() + { + TextBindable.BindValueChanged(_ => { changeText(HitObject); }); + AlternativeTextBindable.BindValueChanged(_ => { changeText(HitObject); }); + SingersBindable.BindValueChanged(index => { ApplySkin(CurrentSkin, false); }); + DisplayBindable.BindValueChanged(e => { (Result.Judgement as KaraokeNoteJudgement).Saitenable = e.NewValue; }); + } - note.TextBindable.BindValueChanged(_ => { changeText(note); }, true); + protected override void OnApply() + { + base.OnApply(); - note.AlternativeTextBindable.BindValueChanged(_ => { changeText(note); }, true); + TextBindable.BindTo(HitObject.TextBindable); + AlternativeTextBindable.BindTo(HitObject.AlternativeTextBindable); + SingersBindable.BindTo(HitObject.SingersBindable); + DisplayBindable.BindTo(HitObject.DisplayBindable); + ToneBindable.BindTo(HitObject.ToneBindable); + } + + protected override void OnFree() + { + base.OnFree(); - note.SingersBindable.BindValueChanged(index => { ApplySkin(CurrentSkin, false); }, true); + TextBindable.UnbindFrom(HitObject.TextBindable); + AlternativeTextBindable.UnbindFrom(HitObject.AlternativeTextBindable); + SingersBindable.UnbindFrom(HitObject.SingersBindable); + DisplayBindable.UnbindFrom(HitObject.DisplayBindable); + ToneBindable.UnbindFrom(HitObject.ToneBindable); } protected override void ApplySkin(ISkinSource skin, bool allowFallback) @@ -72,6 +104,9 @@ protected override void ApplySkin(ISkinSource skin, bool allowFallback) if (CurrentSkin == null) return; + if (HitObject == null) + return; + var noteSkin = skin.GetConfig(new KaraokeSkinLookup(KaraokeSkinConfiguration.NoteStyle, HitObject.Singers))?.Value; if (noteSkin == null) return; diff --git a/osu.Game.Rulesets.Karaoke/Objects/Drawables/Pieces/DefaultBodyPiece.cs b/osu.Game.Rulesets.Karaoke/Objects/Drawables/Pieces/DefaultBodyPiece.cs index 707960512..11f04d9d1 100644 --- a/osu.Game.Rulesets.Karaoke/Objects/Drawables/Pieces/DefaultBodyPiece.cs +++ b/osu.Game.Rulesets.Karaoke/Objects/Drawables/Pieces/DefaultBodyPiece.cs @@ -60,8 +60,8 @@ private void load([CanBeNull] DrawableHitObject drawableObject, ISkinSource skin var holdNote = (DrawableNote)drawableObject; isHitting.BindTo(holdNote.IsHitting); - display.BindTo(holdNote.Display); - singer.BindTo(holdNote.Singers); + display.BindTo(holdNote.DisplayBindable); + singer.BindTo(holdNote.SingersBindable); } AccentColour.BindValueChanged(onAccentChanged); diff --git a/osu.Game.Rulesets.Karaoke/Skinning/LegacyNotePiece.cs b/osu.Game.Rulesets.Karaoke/Skinning/LegacyNotePiece.cs index 2ea3bb9aa..2db6f196a 100644 --- a/osu.Game.Rulesets.Karaoke/Skinning/LegacyNotePiece.cs +++ b/osu.Game.Rulesets.Karaoke/Skinning/LegacyNotePiece.cs @@ -65,8 +65,8 @@ private void load(DrawableHitObject drawableObject, ISkinSource skin, IScrolling var holdNote = (DrawableNote)drawableObject; isHitting.BindTo(holdNote.IsHitting); - display.BindTo(holdNote.Display); - singer.BindTo(holdNote.Singers); + display.BindTo(holdNote.DisplayBindable); + singer.BindTo(holdNote.SingersBindable); } AccentColour.BindValueChanged(onAccentChanged); diff --git a/osu.Game.Rulesets.Karaoke/UI/DrawableKaraokeRuleset.cs b/osu.Game.Rulesets.Karaoke/UI/DrawableKaraokeRuleset.cs index 41b61bf09..f4e8d64de 100644 --- a/osu.Game.Rulesets.Karaoke/UI/DrawableKaraokeRuleset.cs +++ b/osu.Game.Rulesets.Karaoke/UI/DrawableKaraokeRuleset.cs @@ -93,20 +93,7 @@ private void load() Playfield.NotePlayfield.Hide(); } - public override DrawableHitObject CreateDrawableRepresentation(KaraokeHitObject h) - { - switch (h) - { - case Lyric lyric: - return new DrawableLyric(lyric); - - case Note note: - return new DrawableNote(note); - - default: - throw new NotSupportedException(); - } - } + public override DrawableHitObject CreateDrawableRepresentation(KaraokeHitObject h) => null; protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new KaraokeFramedReplayInputHandler(replay); diff --git a/osu.Game.Rulesets.Karaoke/UI/KaraokePlayfield.cs b/osu.Game.Rulesets.Karaoke/UI/KaraokePlayfield.cs index 7bf300eab..f118655c0 100644 --- a/osu.Game.Rulesets.Karaoke/UI/KaraokePlayfield.cs +++ b/osu.Game.Rulesets.Karaoke/UI/KaraokePlayfield.cs @@ -1,6 +1,7 @@ // Copyright (c) andy840119 . Licensed under the GPL 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; @@ -9,6 +10,7 @@ using osu.Game.Rulesets.Karaoke.Configuration; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Objects.Drawables; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -74,6 +76,48 @@ public KaraokePlayfield() }); } + #region Pooling support + + public override void Add(HitObject h) + { + switch (h) + { + case Lyric _: + LyricPlayfield.Add(h); + break; + + case Note _: + case BarLine _: + // hidden the note playfield until receive any note. + NotePlayfield.Alpha = 1; + NotePlayfield.Add(h); + break; + + default: + throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}"); + } + } + + public override bool Remove(HitObject h) + { + switch (h) + { + case Lyric _: + return LyricPlayfield.Remove(h); + + case Note _: + case BarLine _: + return NotePlayfield.Remove(h); + + default: + throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}"); + } + } + + #endregion + + #region Non-pooling support + public override void Add(DrawableHitObject h) { switch (h) @@ -109,10 +153,7 @@ public override bool Remove(DrawableHitObject h) } } - public void Add(BarLine barLine) - { - NotePlayfield.Add(barLine); - } + #endregion [BackgroundDependencyLoader] private void load(KaraokeRulesetConfigManager rulesetConfig, KaraokeSessionStatics session) diff --git a/osu.Game.Rulesets.Karaoke/UI/LyricPlayfield.cs b/osu.Game.Rulesets.Karaoke/UI/LyricPlayfield.cs index 91121cf29..1cf09dc19 100644 --- a/osu.Game.Rulesets.Karaoke/UI/LyricPlayfield.cs +++ b/osu.Game.Rulesets.Karaoke/UI/LyricPlayfield.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Karaoke.Judgements; using osu.Game.Rulesets.Karaoke.Objects; using osu.Game.Rulesets.Karaoke.Objects.Drawables; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; @@ -64,6 +65,13 @@ public LyricPlayfield() seekCache.Validate(); } + protected override void LoadComplete() + { + base.LoadComplete(); + + NewResult += OnNewResult; + } + private void updateLyricTranslate() { var isTranslate = translate.Value; @@ -77,12 +85,12 @@ public override void Add(DrawableHitObject h) { if (h is DrawableLyric drawableLyric) { + // todo : not really sure should cancel binding action in here? drawableLyric.OnLyricStart += OnNewResult; drawableLyric.DisplayRuby = displayRuby.Value; drawableLyric.DisplayRomaji = displayRomaji.Value; } - h.OnNewResult += OnNewResult; base.Add(h); } @@ -91,7 +99,6 @@ public override bool Remove(DrawableHitObject h) if (h is DrawableLyric drawableLyric) drawableLyric.OnLyricStart -= OnNewResult; - h.OnNewResult -= OnNewResult; return base.Remove(h); } @@ -121,6 +128,25 @@ private void load(KaraokeRulesetConfigManager rulesetConfig, KaraokeSessionStati // Practice rulesetConfig.BindWith(KaraokeRulesetSetting.PracticePreemptTime, preemptTime); session.BindWith(KaraokeRulesetSession.NowLyric, nowLyric); + + RegisterPool(50); + } + + protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new LyricHitObjectLifetimeEntry(hitObject); + + private class LyricHitObjectLifetimeEntry : HitObjectLifetimeEntry + { + public LyricHitObjectLifetimeEntry(HitObject hitObject) + : base(hitObject) + { + // Manually set to reduce the number of future alive objects to a bare minimum. + LifetimeEnd = Lyric.EndTime; + LifetimeStart = HitObject.StartTime - Lyric.TimePreempt; + } + + protected Lyric Lyric => (Lyric)HitObject; + + protected override double InitialLifetimeOffset => Lyric.TimePreempt; } } } diff --git a/osu.Game.Rulesets.Karaoke/UI/NotePlayfield.cs b/osu.Game.Rulesets.Karaoke/UI/NotePlayfield.cs index b45a7f969..9f9c7083d 100644 --- a/osu.Game.Rulesets.Karaoke/UI/NotePlayfield.cs +++ b/osu.Game.Rulesets.Karaoke/UI/NotePlayfield.cs @@ -220,6 +220,8 @@ protected override void LoadComplete() { base.LoadComplete(); + NewResult += OnNewResult; + saitenPitch.BindValueChanged(value => { var newValue = value.NewValue; @@ -241,26 +243,14 @@ public void AddColumn(DefaultColumnBackground c) public override void Add(DrawableHitObject h) { - var note = (Note)h.HitObject; - - note.ToneBindable.BindValueChanged(tone => { h.Y = calculator.YPositionAt(tone.NewValue); }, true); - - h.OnNewResult += OnNewResult; + if (h is DrawableNote drawableNote) + { + drawableNote.ToneBindable.BindValueChanged(tone => { h.Y = calculator.YPositionAt(tone.NewValue); }, true); + } base.Add(h); } - public override bool Remove(DrawableHitObject h) - { - if (!base.Remove(h)) - return false; - - h.OnNewResult -= OnNewResult; - return true; - } - - public void Add(BarLine barLine) => base.Add(new DrawableBarLine(barLine)); - public void AddReplay(KaraokeReplayFrame frame) { replaySaitenVisualization.Add(frame); @@ -310,6 +300,9 @@ private void load(OsuColour colours, KaraokeSessionStatics session) session.BindWith(KaraokeRulesetSession.SaitenPitch, saitenPitch); session.GetBindable(KaraokeRulesetSession.SaitenStatus).BindValueChanged(e => { saitenStatus.SaitenStatusMode = e.NewValue; }); + + RegisterPool(50); + RegisterPool(15); } public bool OnPressed(KaraokeSaitenAction action)