1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-19 12:22:57 +08:00

Merge pull request #11166 from bdach/taiko-bar-line-pooling

Implement taiko bar line pooling
This commit is contained in:
Dan Balasescu 2020-12-14 16:21:25 +09:00 committed by GitHub
commit c9ba1144ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 267 additions and 96 deletions

View File

@ -0,0 +1,52 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
public abstract class HitObjectApplicationTestScene : OsuTestScene
{
[Cached(typeof(IScrollingInfo))]
private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo
{
Direction = { Value = ScrollingDirection.Left },
TimeRange = { Value = 1000 },
};
private ScrollingHitObjectContainer hitObjectContainer;
[SetUpSteps]
public void SetUp()
=> AddStep("create SHOC", () => Child = hitObjectContainer = new ScrollingHitObjectContainer
{
RelativeSizeAxes = Axes.X,
Height = 200,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Clock = new FramedClock(new StopwatchClock())
});
protected void AddHitObject(DrawableHitObject hitObject)
=> AddStep("add to SHOC", () => hitObjectContainer.Add(hitObject));
protected void RemoveHitObject(DrawableHitObject hitObject)
=> AddStep("remove from SHOC", () => hitObjectContainer.Remove(hitObject));
protected TObject PrepareObject<TObject>(TObject hitObject)
where TObject : TaikoHitObject
{
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return hitObject;
}
}
}

View File

@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
} }
}; };
hoc.Add(new DrawableBarLineMajor(createBarLineAtCurrentTime(true)) hoc.Add(new DrawableBarLine(createBarLineAtCurrentTime(true))
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -0,0 +1,32 @@
// 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 NUnit.Framework;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneBarLineApplication : HitObjectApplicationTestScene
{
[Test]
public void TestApplyNewBarLine()
{
DrawableBarLine barLine = new DrawableBarLine(PrepareObject(new BarLine
{
StartTime = 400,
Major = true
}));
AddHitObject(barLine);
RemoveHitObject(barLine);
AddStep("apply new bar line", () => barLine.Apply(PrepareObject(new BarLine
{
StartTime = 200,
Major = false
}), null));
AddHitObject(barLine);
}
}
}

View File

@ -145,9 +145,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
private void addBarLine(bool major, double delay = scroll_time) private void addBarLine(bool major, double delay = scroll_time)
{ {
BarLine bl = new BarLine { StartTime = DrawableRuleset.Playfield.Time.Current + delay }; BarLine bl = new BarLine
{
StartTime = DrawableRuleset.Playfield.Time.Current + delay,
Major = major
};
DrawableRuleset.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl)); DrawableRuleset.Playfield.Add(bl);
} }
private void addSwell(double duration = default_duration) private void addSwell(double duration = default_duration)

View File

@ -1,6 +1,7 @@
// 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.Bindables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -8,7 +9,13 @@ namespace osu.Game.Rulesets.Taiko.Objects
{ {
public class BarLine : TaikoHitObject, IBarLine public class BarLine : TaikoHitObject, IBarLine
{ {
public bool Major { get; set; } public bool Major
{
get => MajorBindable.Value;
set => MajorBindable.Value = value;
}
public readonly Bindable<bool> MajorBindable = new BindableBool();
public override Judgement CreateJudgement() => new IgnoreJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
} }

View File

@ -1,7 +1,11 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osuTK; using osuTK;
@ -15,49 +19,123 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary> /// </summary>
public class DrawableBarLine : DrawableHitObject<HitObject> public class DrawableBarLine : DrawableHitObject<HitObject>
{ {
public new BarLine HitObject => (BarLine)base.HitObject;
/// <summary> /// <summary>
/// The width of the line tracker. /// The width of the line tracker.
/// </summary> /// </summary>
private const float tracker_width = 2f; private const float tracker_width = 2f;
/// <summary> /// <summary>
/// Fade out time calibrated to a pre-empt of 1000ms. /// The vertical offset of the triangles from the line tracker.
/// </summary> /// </summary>
private const float base_fadeout_time = 100f; private const float triangle_offset = 10f;
/// <summary>
/// The size of the triangles.
/// </summary>
private const float triangle_size = 20f;
/// <summary> /// <summary>
/// The visual line tracker. /// The visual line tracker.
/// </summary> /// </summary>
protected SkinnableDrawable Line; private SkinnableDrawable line;
/// <summary> /// <summary>
/// The bar line. /// Container with triangles. Only visible for major lines.
/// </summary> /// </summary>
protected readonly BarLine BarLine; private Container triangleContainer;
public DrawableBarLine(BarLine barLine) private readonly Bindable<bool> major = new Bindable<bool>();
public DrawableBarLine()
: this(null)
{
}
public DrawableBarLine([CanBeNull] BarLine barLine)
: base(barLine) : base(barLine)
{ {
BarLine = barLine; }
[BackgroundDependencyLoader]
private void load()
{
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;
Width = tracker_width; Width = tracker_width;
AddInternal(Line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box AddRangeInternal(new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box
EdgeSmoothness = new Vector2(0.5f, 0), {
}) RelativeSizeAxes = Axes.Both,
{ EdgeSmoothness = new Vector2(0.5f, 0),
Anchor = Anchor.Centre, })
Origin = Anchor.Centre, {
Alpha = 0.75f, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
triangleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new EquilateralTriangle
{
Name = "Top",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, -triangle_offset),
Size = new Vector2(-triangle_size),
EdgeSmoothness = new Vector2(1),
},
new EquilateralTriangle
{
Name = "Bottom",
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, triangle_offset),
Size = new Vector2(triangle_size),
EdgeSmoothness = new Vector2(1),
}
}
}
}); });
} }
protected override void UpdateHitStateTransforms(ArmedState state) => this.FadeOut(150); protected override void LoadComplete()
{
base.LoadComplete();
major.BindValueChanged(updateMajor);
}
private void updateMajor(ValueChangedEvent<bool> 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 UpdateHitStateTransforms(ArmedState state)
{
using (BeginAbsoluteSequence(HitObject.StartTime))
this.FadeOutFromOne(150).Expire();
}
} }
} }

View File

@ -1,67 +0,0 @@
// 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 osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osuTK;
using osu.Framework.Graphics.Shapes;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableBarLineMajor : DrawableBarLine
{
/// <summary>
/// The vertical offset of the triangles from the line tracker.
/// </summary>
private const float triangle_offfset = 10f;
/// <summary>
/// The size of the triangles.
/// </summary>
private const float triangle_size = 20f;
private readonly Container triangleContainer;
public DrawableBarLineMajor(BarLine barLine)
: base(barLine)
{
AddInternal(triangleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new EquilateralTriangle
{
Name = "Top",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, -triangle_offfset),
Size = new Vector2(-triangle_size),
EdgeSmoothness = new Vector2(1),
},
new EquilateralTriangle
{
Name = "Bottom",
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, triangle_offfset),
Size = new Vector2(triangle_size),
EdgeSmoothness = new Vector2(1),
}
}
});
Line.Alpha = 1f;
}
protected override void LoadComplete()
{
base.LoadComplete();
using (triangleContainer.BeginAbsoluteSequence(HitObject.StartTime))
triangleContainer.FadeOut(150);
}
}
}

View File

@ -0,0 +1,19 @@
// 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 osu.Framework.Allocation;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI
{
public class BarLinePlayfield : ScrollingPlayfield
{
[BackgroundDependencyLoader]
private void load()
{
RegisterPool<BarLine, DrawableBarLine>(15);
}
}
}

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar))); new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar));
FrameStableComponents.Add(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty()) FrameStableComponents.Add(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
{ {

View File

@ -10,6 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
@ -38,10 +39,15 @@ namespace osu.Game.Rulesets.Taiko.UI
private SkinnableDrawable mascot; private SkinnableDrawable mascot;
private ProxyContainer topLevelHitContainer; private ProxyContainer topLevelHitContainer;
private ScrollingHitObjectContainer barlineContainer;
private Container rightArea; private Container rightArea;
private Container leftArea; private Container leftArea;
/// <remarks>
/// <see cref="Playfield.AddNested"/> is purposefully not called on this to prevent i.e. being able to interact
/// with bar lines in the editor.
/// </remarks>
private BarLinePlayfield barLinePlayfield;
private Container hitTargetOffsetContent; private Container hitTargetOffsetContent;
public TaikoPlayfield(ControlPointInfo controlPoints) public TaikoPlayfield(ControlPointInfo controlPoints)
@ -84,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
barlineContainer = new ScrollingHitObjectContainer(), barLinePlayfield = new BarLinePlayfield(),
new Container new Container
{ {
Name = "Hit objects", Name = "Hit objects",
@ -155,12 +161,50 @@ namespace osu.Game.Rulesets.Taiko.UI
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT); mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
} }
#region Pooling support
public override void Add(HitObject h)
{
switch (h)
{
case BarLine barLine:
barLinePlayfield.Add(barLine);
break;
case TaikoHitObject taikoHitObject:
base.Add(taikoHitObject);
break;
default:
throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}");
}
}
public override bool Remove(HitObject h)
{
switch (h)
{
case BarLine barLine:
return barLinePlayfield.Remove(barLine);
case TaikoHitObject taikoHitObject:
return base.Remove(taikoHitObject);
default:
throw new ArgumentException($"Unsupported {nameof(HitObject)} type: {h.GetType()}");
}
}
#endregion
#region Non-pooling support
public override void Add(DrawableHitObject h) public override void Add(DrawableHitObject h)
{ {
switch (h) switch (h)
{ {
case DrawableBarLine barline: case DrawableBarLine barLine:
barlineContainer.Add(barline); barLinePlayfield.Add(barLine);
break; break;
case DrawableTaikoHitObject taikoObject: case DrawableTaikoHitObject taikoObject:
@ -170,7 +214,7 @@ namespace osu.Game.Rulesets.Taiko.UI
break; break;
default: default:
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type"); throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type: {h.GetType()}");
} }
} }
@ -178,8 +222,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
switch (h) switch (h)
{ {
case DrawableBarLine barline: case DrawableBarLine barLine:
return barlineContainer.Remove(barline); return barLinePlayfield.Remove(barLine);
case DrawableTaikoHitObject _: case DrawableTaikoHitObject _:
h.OnNewResult -= OnNewResult; h.OnNewResult -= OnNewResult;
@ -187,10 +231,12 @@ namespace osu.Game.Rulesets.Taiko.UI
return base.Remove(h); return base.Remove(h);
default: default:
throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type"); throw new ArgumentException($"Unsupported {nameof(DrawableHitObject)} type: {h.GetType()}");
} }
} }
#endregion
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result) internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{ {
if (!DisplayJudgements.Value) if (!DisplayJudgements.Value)