// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE using System; using System.Collections.Generic; using osu.Framework.Configuration; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Lists; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Timing; using osu.Game.Rulesets.UI; using OpenTK.Graphics; namespace osu.Game.Tests.Visual { public class TestCaseScrollingHitObjects : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(Playfield) }; private readonly List playfields = new List(); public TestCaseScrollingHitObjects() { playfields.Add(new TestPlayfield(Direction.Vertical)); playfields.Add(new TestPlayfield(Direction.Horizontal)); playfields.ForEach(p => p.HitObjects.ControlPoints.Add(new MultiplierControlPoint(double.MinValue))); Add(new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, Size = new Vector2(0.85f), Masking = true, BorderColour = Color4.White, BorderThickness = 2, MaskingSmoothness = 1, Children = new Drawable[] { new Box { Name = "Background", RelativeSizeAxes = Axes.Both, Alpha = 0.35f, }, playfields[0], playfields[1] } }); AddSliderStep("Time range", 100, 10000, 5000, v => playfields.ForEach(p => p.TimeRange.Value = v)); AddStep("Add control point", () => addControlPoint(Time.Current + 5000)); } protected override void LoadComplete() { base.LoadComplete(); for (int i = 0; i <= 5000; i += 1000) addHitObject(Time.Current + i); Scheduler.AddDelayed(() => addHitObject(Time.Current + 5000), 1000, true); } private void addHitObject(double time) { playfields.ForEach(p => { p.Add(new TestDrawableHitObject(time) { Anchor = p.ScrollingDirection == Direction.Horizontal ? Anchor.CentreRight : Anchor.BottomCentre }); }); } private void addControlPoint(double time) { playfields.ForEach(p => { p.HitObjects.ControlPoints.AddRange(new[] { new MultiplierControlPoint(time) { DifficultyPoint = { SpeedMultiplier = 3 } }, new MultiplierControlPoint(time + 2000) { DifficultyPoint = { SpeedMultiplier = 2 } }, new MultiplierControlPoint(time + 3000) { DifficultyPoint = { SpeedMultiplier = 1 } }, }); TestDrawableControlPoint createDrawablePoint(double t) => new TestDrawableControlPoint(t) { Anchor = p.ScrollingDirection == Direction.Horizontal ? Anchor.CentreRight : Anchor.BottomCentre }; p.Add(createDrawablePoint(time)); p.Add(createDrawablePoint(time + 2000)); p.Add(createDrawablePoint(time + 3000)); }); } private class ScrollingHitObjectContainer : Playfield.HitObjectContainer { public readonly BindableDouble TimeRange = new BindableDouble { MinValue = 0, MaxValue = double.MaxValue }; public readonly SortedList ControlPoints = new SortedList(); private readonly Direction scrollingDirection; public ScrollingHitObjectContainer(Direction scrollingDirection) { this.scrollingDirection = scrollingDirection; RelativeSizeAxes = Axes.Both; } protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); var currentMultiplier = controlPointAt(Time.Current); foreach (var obj in AliveObjects) { var relativePosition = (Time.Current - obj.HitObject.StartTime) / (TimeRange / currentMultiplier.Multiplier); // Todo: We may need to consider scale here var finalPosition = (float)relativePosition * DrawSize; switch (scrollingDirection) { case Direction.Horizontal: obj.X = finalPosition.X; break; case Direction.Vertical: obj.Y = finalPosition.Y; break; } } } private readonly MultiplierControlPoint searchingPoint = new MultiplierControlPoint(); private MultiplierControlPoint controlPointAt(double time) { if (ControlPoints.Count == 0) return new MultiplierControlPoint(double.MinValue); if (time < ControlPoints[0].StartTime) return ControlPoints[0]; searchingPoint.StartTime = time; int index = ControlPoints.BinarySearch(searchingPoint); if (index < 0) index = ~index - 1; return ControlPoints[index]; } } private class TestPlayfield : Playfield { public readonly BindableDouble TimeRange = new BindableDouble(5000); public readonly Direction ScrollingDirection; public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects; public TestPlayfield(Direction scrollingDirection) { ScrollingDirection = scrollingDirection; base.HitObjects = new ScrollingHitObjectContainer(scrollingDirection); HitObjects.TimeRange.BindTo(TimeRange); } } private class TestDrawableControlPoint : DrawableHitObject { private readonly Box box; public TestDrawableControlPoint(double time) : base(new HitObject { StartTime = time }) { Origin = Anchor.Centre; Add(box = new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, }); } protected override void Update() { base.Update(); RelativeSizeAxes = (Anchor & Anchor.x2) > 0 ? Axes.Y : Axes.X; Size = new Vector2(1); box.Size = DrawSize; } protected override void UpdateState(ArmedState state) { } } private class TestDrawableHitObject : DrawableHitObject { public TestDrawableHitObject(double time) : base(new HitObject { StartTime = time }) { Origin = Anchor.Centre; AutoSizeAxes = Axes.Both; Add(new Box { Size = new Vector2(75) }); } protected override void UpdateState(ArmedState state) { } } } }