From 49f893d5e42b3712a7801ea329128bb2701538dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 May 2018 12:00:11 +0900 Subject: [PATCH 01/15] Couple the timeline to the audio --- .../Visual/TestCaseEditorComposeTimeline.cs | 13 ++- .../Edit/Screens/Compose/Timeline/Timeline.cs | 80 +++++++++++++++++++ 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index 0776bc123e..aff7abe03d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -14,14 +14,15 @@ using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseEditorComposeTimeline : OsuTestCase + public class TestCaseEditorComposeTimeline : EditorClockTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(TimelineArea), typeof(Timeline), typeof(BeatmapWaveformGraph), typeof(TimelineButton) }; - private readonly TimelineArea timelineArea; - - public TestCaseEditorComposeTimeline() + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) { + TimelineArea timelineArea; + Children = new Drawable[] { new MusicController @@ -38,11 +39,7 @@ namespace osu.Game.Tests.Visual Size = new Vector2(0.8f, 100) } }; - } - [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) - { timelineArea.Beatmap.BindTo(osuGame.Beatmap); } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index 9801ba12a1..e8d34b466c 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -13,6 +16,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline public readonly Bindable WaveformVisible = new Bindable(); public readonly Bindable Beatmap = new Bindable(); + private IAdjustableClock adjustableClock; + public Timeline() { ZoomDuration = 200; @@ -32,12 +37,87 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint); } + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + } + + private bool handlingUserInput; + private bool trackWasPlaying; + protected override void Update() { base.Update(); // We want time = 0 to be at the centre of the container when scrolled to the start Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 }; + + if (!handlingUserInput) + ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + else + adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + if (base.OnMouseDown(state, args)) + { + beginUserInput(); + return true; + } + + return false; + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + endUserInput(); + return base.OnMouseUp(state, args); + } + + private void beginUserInput() + { + handlingUserInput = true; + trackWasPlaying = adjustableClock.IsRunning; + adjustableClock.Stop(); + } + + private void endUserInput() + { + handlingUserInput = false; + if (trackWasPlaying) + adjustableClock.Start(); + } + + protected override ScrollbarContainer CreateScrollbar(Direction direction) => new TimelineScrollbar(this, direction); + + private class TimelineScrollbar : ScrollbarContainer + { + private readonly Timeline timeline; + + public TimelineScrollbar(Timeline timeline, Direction scrollDir) + : base(scrollDir) + { + this.timeline = timeline; + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + if (base.OnMouseDown(state, args)) + { + timeline.beginUserInput(); + return true; + } + + return false; + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + timeline.endUserInput(); + return base.OnMouseUp(state, args); + } } } } From f18677382c59446d4cdc307ed5969c5a4c2b71f7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 May 2018 12:00:23 +0900 Subject: [PATCH 02/15] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 8f007de13e..907265b750 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 8f007de13e4a1aed46cb35e5018448f809bd5e88 +Subproject commit 907265b7501964a09eb2cd89ec2257941183c65c From 6b0eeb343651bd50764199b3413c59dd946ae2df Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 May 2018 14:14:32 +0900 Subject: [PATCH 03/15] Rework seeking interactions to support smooth scrolling --- .../Edit/Screens/Compose/Timeline/Timeline.cs | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index e8d34b466c..9dbaa1586d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -43,20 +43,59 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline this.adjustableClock = adjustableClock; } - private bool handlingUserInput; + /// + /// The track's time in the previous frame. + /// + private double lastTrackTime; + + /// + /// Whether the user is currently dragging the timeline. + /// + private bool handlingDragInput; + + /// + /// Whether the track was playing before a user drag event. + /// private bool trackWasPlaying; protected override void Update() { base.Update(); - // We want time = 0 to be at the centre of the container when scrolled to the start + // The extrema of track time should be positioned at the centre of the container when scrolled to the start or end Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 }; - if (!handlingUserInput) - ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); - else + if (handlingDragInput) + { + // The user is dragging - the track should always follow the timeline adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + } + else if (adjustableClock.IsRunning) + { + // If the user hasn't provided mouse input but the track is running, always follow the track + ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + } + else + { + // The track isn't playing, so we want to smooth-scroll once more, and re-enable wheel scrolling + // There are two cases we have to be wary of: + // 1) The user scrolls on this timeline: We want the track to follow us + // 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time + + // The simplest way to cover both cases is by checking that inter-frame track times are identical + if (adjustableClock.CurrentTime == lastTrackTime) + { + // The track hasn't been seeked externally + adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + } + else + { + // The track has been seeked externally + ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + } + } + + lastTrackTime = adjustableClock.CurrentTime; } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) @@ -78,14 +117,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline private void beginUserInput() { - handlingUserInput = true; + handlingDragInput = true; trackWasPlaying = adjustableClock.IsRunning; adjustableClock.Stop(); } private void endUserInput() { - handlingUserInput = false; + handlingDragInput = false; if (trackWasPlaying) adjustableClock.Start(); } From a30f72b477d6353d0e785d94cd66aac6fefe50de Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 23 May 2018 14:32:00 +0900 Subject: [PATCH 04/15] Add helper methods for seeking/scrolling --- .../Screens/Edit/Screens/Compose/Timeline/Timeline.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index 9dbaa1586d..df615ad035 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -68,12 +68,12 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline if (handlingDragInput) { // The user is dragging - the track should always follow the timeline - adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + seekTrackToCurrent(); } else if (adjustableClock.IsRunning) { // If the user hasn't provided mouse input but the track is running, always follow the track - ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + scrollToTrackTime(); } else { @@ -86,16 +86,19 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline if (adjustableClock.CurrentTime == lastTrackTime) { // The track hasn't been seeked externally - adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + seekTrackToCurrent(); } else { // The track has been seeked externally - ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + scrollToTrackTime(); } } lastTrackTime = adjustableClock.CurrentTime; + + void seekTrackToCurrent() => adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + void scrollToTrackTime() => ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) From d4f68e0b673c9e258180d5290e016a3fb0e7d105 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 May 2018 14:26:53 +0900 Subject: [PATCH 05/15] Fix possible NaN values due to trackvirtual infinite length --- .../Edit/Screens/Compose/Timeline/Timeline.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index df615ad035..3fdde1fc1d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Audio.Track; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; @@ -97,8 +98,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline lastTrackTime = adjustableClock.CurrentTime; - void seekTrackToCurrent() => adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); - void scrollToTrackTime() => ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + void seekTrackToCurrent() + { + if (!(Beatmap.Value.Track is TrackVirtual)) + adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + } + + void scrollToTrackTime() + { + if (!(Beatmap.Value.Track is TrackVirtual)) + ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + } } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) From b649025e861b14506754794e6416235b1e43d205 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 24 May 2018 14:36:48 +0900 Subject: [PATCH 06/15] beginUserDrag/endUserDrag --- .../Edit/Screens/Compose/Timeline/Timeline.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index 3fdde1fc1d..0ae6cd6da2 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline { if (base.OnMouseDown(state, args)) { - beginUserInput(); + beginUserDrag(); return true; } @@ -124,18 +124,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - endUserInput(); + endUserDrag(); return base.OnMouseUp(state, args); } - private void beginUserInput() + private void beginUserDrag() { handlingDragInput = true; trackWasPlaying = adjustableClock.IsRunning; adjustableClock.Stop(); } - private void endUserInput() + private void endUserDrag() { handlingDragInput = false; if (trackWasPlaying) @@ -158,7 +158,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline { if (base.OnMouseDown(state, args)) { - timeline.beginUserInput(); + timeline.beginUserDrag(); return true; } @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) { - timeline.endUserInput(); + timeline.endUserDrag(); return base.OnMouseUp(state, args); } } From 22dfe46572b2f73a0fbb790688d41a8a78d873d5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 11 Jun 2018 21:45:19 +0900 Subject: [PATCH 07/15] Make taiko hitobject properly unproxy when rewound --- .../Objects/Drawables/DrawableHit.cs | 4 ++ .../Objects/Drawables/DrawableSwell.cs | 20 +++---- .../Drawables/DrawableTaikoHitObject.cs | 60 +++++++++++++++++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 20 +------ 4 files changed, 70 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index d923b2dcdf..cca2753524 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (State.Value) { case ArmedState.Idle: + UnproxyContent(); this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); break; case ArmedState.Miss: @@ -93,6 +94,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables .Expire(); break; case ArmedState.Hit: + if (X >= -0.05f) + ProxyContent(); + var flash = circlePiece?.FlashBox; if (flash != null) { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 33cc29bccf..1f571544d8 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -20,12 +20,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public class DrawableSwell : DrawableTaikoHitObject { - /// - /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. - /// This is only ever invoked once. - /// - public event Action OnStart; - private const float target_ring_thick_border = 1.4f; private const float target_ring_thin_border = 1f; private const float target_ring_scale = 5f; @@ -40,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private int userHits; - private bool hasStarted; + private bool hasProxied; private readonly SwellSymbolPiece symbol; public DrawableSwell(Swell swell) @@ -48,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { FillMode = FillMode.Fit; - AddInternal(bodyContainer = new Container + Content.Add(bodyContainer = new Container { RelativeSizeAxes = Axes.Both, Depth = 1, @@ -177,6 +171,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (state) { + case ArmedState.Idle: + hasProxied = false; + UnproxyContent(); + break; case ArmedState.Hit: bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time); break; @@ -195,10 +193,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables X = Math.Max(0, X); double t = Math.Min(HitObject.StartTime, Time.Current); - if (t == HitObject.StartTime && !hasStarted) + if (t == HitObject.StartTime && !hasProxied) { - OnStart?.Invoke(); - hasStarted = true; + ProxyContent(); + hasProxied = true; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index 971fd8854d..c07ee3a0fd 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -9,10 +9,62 @@ using OpenTK; using System.Linq; using osu.Game.Audio; using System.Collections.Generic; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { - public abstract class DrawableTaikoHitObject : DrawableHitObject, IKeyBindingHandler + public abstract class DrawableTaikoHitObject : DrawableHitObject, IKeyBindingHandler + { + protected readonly Container Content; + public readonly Container ProxiedContent; + + private readonly Container nonProxiedContent; + + protected DrawableTaikoHitObject(TaikoHitObject hitObject) + : base(hitObject) + { + InternalChildren = new[] + { + nonProxiedContent = new Container + { + RelativeSizeAxes = Axes.Both, + Child = Content = new Container { RelativeSizeAxes = Axes.Both } + }, + ProxiedContent = new Container { RelativeSizeAxes = Axes.Both } + }; + } + + /// + /// is proxied into an upper layer. We don't want to get masked away otherwise would too. + /// + protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + + /// + /// Moves to a layer proxied above the playfield. + /// + protected void ProxyContent() + { + nonProxiedContent.Remove(Content); + ProxiedContent.Remove(Content); + ProxiedContent.Add(Content); + } + + /// + /// Moves to the normal hitobject layer. + /// + protected void UnproxyContent() + { + ProxiedContent.Remove(Content); + nonProxiedContent.Remove(Content); + nonProxiedContent.Add(Content); + } + + public abstract bool OnPressed(TaikoAction action); + public virtual bool OnReleased(TaikoAction action) => false; + } + + public abstract class DrawableTaikoHitObject : DrawableTaikoHitObject where TaikoHitType : TaikoHitObject { public override Vector2 OriginPosition => new Vector2(DrawHeight / 2); @@ -34,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Both; Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE); - InternalChild = MainPiece = CreateMainPiece(); + Content.Add(MainPiece = CreateMainPiece()); MainPiece.KiaiMode = HitObject.Kiai; } @@ -44,9 +96,5 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected override string SampleNamespace => "Taiko"; protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(); - - public abstract bool OnPressed(TaikoAction action); - - public virtual bool OnReleased(TaikoAction action) => false; } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 417a7c2581..c638f807fe 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -216,10 +216,9 @@ namespace osu.Game.Rulesets.Taiko.UI if (barline != null) barlineContainer.Add(barline.CreateProxy()); - // Swells should be moved at the very top of the playfield when they reach the hit target - var swell = h as DrawableSwell; - if (swell != null) - swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); + var taikoObject = h as DrawableTaikoHitObject; + if (taikoObject != null) + topLevelHitContainer.Add(taikoObject.ProxiedContent.CreateProxy()); } internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) @@ -244,19 +243,6 @@ namespace osu.Game.Rulesets.Taiko.UI hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit(); else { - if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) - { - // If we're far enough away from the left stage, we should bring outselves in front of it - // Todo: The following try-catch is temporary for replay rewinding support - try - { - topLevelHitContainer.Add(judgedObject.CreateProxy()); - } - catch - { - } - } - hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); if (judgedObject.HitObject.Kiai) From 1b12820de9e8529df794cc3d1396ba9e4d954c29 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Mon, 11 Jun 2018 21:54:11 +0900 Subject: [PATCH 08/15] Restore a removed comment --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index cca2753524..519b56a3ed 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -94,6 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables .Expire(); break; case ArmedState.Hit: + // If we're far enough away from the left stage, we should bring outselves in front of it if (X >= -0.05f) ProxyContent(); From b90cdfbfd166aeb8fc26ad1bf8f51ff8d1c9b3d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jun 2018 14:33:33 +0900 Subject: [PATCH 09/15] Remove unused variable --- osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index 0a2e549509..c0557982fb 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -19,8 +19,6 @@ namespace osu.Game.Tests.Visual public TestCaseEditorComposeTimeline() { - TimelineArea timelineArea; - Children = new Drawable[] { new MusicController From ca3186f34c7b2507fef6e0900e61e5c7e9746c82 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 12 Jun 2018 19:51:35 +0900 Subject: [PATCH 10/15] Create dependencies before children are loaded --- osu.Game/Tests/Visual/EditorClockTestCase.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 08dc6a3bbd..521b51529e 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -25,13 +25,20 @@ namespace osu.Game.Tests.Visual Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false }; } + protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + { + var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + + dependencies.Cache(BeatDivisor); + dependencies.CacheAs(Clock); + dependencies.CacheAs(Clock); + + return dependencies; + } + [BackgroundDependencyLoader] private void load() { - Dependencies.Cache(BeatDivisor); - Dependencies.CacheAs(Clock); - Dependencies.CacheAs(Clock); - Beatmap.BindValueChanged(beatmapChanged, true); } From 457531910294897c15cdfbc5337672d138c0ef1d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sun, 17 Jun 2018 17:56:46 +0900 Subject: [PATCH 11/15] Privatise the proxied content --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 17 +++++++++++------ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index c07ee3a0fd..e712d62a26 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public abstract class DrawableTaikoHitObject : DrawableHitObject, IKeyBindingHandler { protected readonly Container Content; - public readonly Container ProxiedContent; + private readonly Container proxiedContent; private readonly Container nonProxiedContent; @@ -31,12 +31,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables RelativeSizeAxes = Axes.Both, Child = Content = new Container { RelativeSizeAxes = Axes.Both } }, - ProxiedContent = new Container { RelativeSizeAxes = Axes.Both } + proxiedContent = new Container { RelativeSizeAxes = Axes.Both } }; } /// - /// is proxied into an upper layer. We don't want to get masked away otherwise would too. + /// is proxied into an upper layer. We don't want to get masked away otherwise would too. /// protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; @@ -46,8 +46,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables protected void ProxyContent() { nonProxiedContent.Remove(Content); - ProxiedContent.Remove(Content); - ProxiedContent.Add(Content); + proxiedContent.Remove(Content); + proxiedContent.Add(Content); } /// @@ -55,11 +55,16 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// protected void UnproxyContent() { - ProxiedContent.Remove(Content); + proxiedContent.Remove(Content); nonProxiedContent.Remove(Content); nonProxiedContent.Add(Content); } + /// + /// Creates a proxy for the content of this . + /// + public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); + public abstract bool OnPressed(TaikoAction action); public virtual bool OnReleased(TaikoAction action) => false; } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index c638f807fe..7fdd3cd1e2 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -218,7 +218,7 @@ namespace osu.Game.Rulesets.Taiko.UI var taikoObject = h as DrawableTaikoHitObject; if (taikoObject != null) - topLevelHitContainer.Add(taikoObject.ProxiedContent.CreateProxy()); + topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); } internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) From 6c100caba763b333cd099e37033c70e02cac224b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Jun 2018 15:09:03 +0900 Subject: [PATCH 12/15] Use existing instance of beatmap rather than adding more resources --- .../Beatmaps/IO/ImportBeatmapTest.cs | 4 +- osu.Game.Tests/Visual/TestCaseWaveform.cs | 16 +----- osu.Game.Tests/WaveformTestBeatmap.cs | 55 +++++++++++++++++++ 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 osu.Game.Tests/WaveformTestBeatmap.cs diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 1c9696901c..616ba132fd 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestFixture] public class ImportBeatmapTest { - private const string osz_path = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; + public const string TEST_OSZ_PATH = @"../../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; [Test] public void TestImportWhenClosed() @@ -265,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO private string createTemporaryBeatmap() { var temp = Path.GetTempFileName() + ".osz"; - File.Copy(osz_path, temp, true); + File.Copy(TEST_OSZ_PATH, temp, true); Assert.IsTrue(File.Exists(temp)); return temp; } diff --git a/osu.Game.Tests/Visual/TestCaseWaveform.cs b/osu.Game.Tests/Visual/TestCaseWaveform.cs index 983b98016e..46d46863ad 100644 --- a/osu.Game.Tests/Visual/TestCaseWaveform.cs +++ b/osu.Game.Tests/Visual/TestCaseWaveform.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; -using osu.Game.Overlays; namespace osu.Game.Tests.Visual { @@ -20,22 +19,14 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { + Beatmap.Value = new WaveformTestBeatmap(); + FillFlowContainer flow; Child = flow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - new MusicController - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Y = 100, - State = Visibility.Visible - }, - } }; for (int i = 1; i <= 16; i *= 2) @@ -44,10 +35,9 @@ namespace osu.Game.Tests.Visual { RelativeSizeAxes = Axes.Both, Resolution = 1f / i, + Waveform = Beatmap.Value.Waveform, }; - Beatmap.ValueChanged += b => newDisplay.Waveform = b.Waveform; - flow.Add(new Container { RelativeSizeAxes = Axes.X, diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs new file mode 100644 index 0000000000..17aa7db14d --- /dev/null +++ b/osu.Game.Tests/WaveformTestBeatmap.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using System.Linq; +using osu.Framework.Audio.Track; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; +using osu.Game.IO.Archives; +using osu.Game.Tests.Beatmaps.IO; + +namespace osu.Game.Tests +{ + /// + /// A that is used for testcases that include waveforms. + /// + public class WaveformTestBeatmap : WorkingBeatmap + { + private readonly ZipArchiveReader reader; + private readonly FileStream stream; + + public WaveformTestBeatmap() + : base(new BeatmapInfo()) + { + stream = File.OpenRead(ImportBeatmapTest.TEST_OSZ_PATH); + reader = new ZipArchiveReader(stream); + } + + public override void Dispose() + { + base.Dispose(); + stream?.Dispose(); + reader?.Dispose(); + } + + protected override IBeatmap GetBeatmap() => createTestBeatmap(); + + protected override Texture GetBackground() => null; + + protected override Waveform GetWaveform() => new Waveform(getAudioStream()); + + protected override Track GetTrack() => new TrackBass(getAudioStream()); + + private Stream getAudioStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".mp3"))); + private Stream getBeatmapStream() => reader.GetStream(reader.Filenames.First(f => f.EndsWith(".osu"))); + + private Beatmap createTestBeatmap() + { + using (var beatmapStream = getBeatmapStream()) + using (var beatmapReader = new StreamReader(beatmapStream)) + return Decoder.GetDecoder(beatmapReader).Decode(beatmapReader); + } + } +} From 0695615c6a21ff53e11c6e53ef52147841304b66 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 13 Jun 2018 15:10:08 +0900 Subject: [PATCH 13/15] Remove MusicController from compose timeline testcase --- .../Visual/TestCaseEditorComposeTimeline.cs | 104 +++++++++++++++++- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index c0557982fb..bb0e6156f6 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -4,11 +4,17 @@ using System; using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Overlays; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; +using OpenTK.Graphics; namespace osu.Game.Tests.Visual { @@ -17,15 +23,23 @@ namespace osu.Game.Tests.Visual { public override IReadOnlyList RequiredTypes => new[] { typeof(TimelineArea), typeof(Timeline), typeof(TimelineButton) }; - public TestCaseEditorComposeTimeline() + [BackgroundDependencyLoader] + private void load() { + Beatmap.Value = new WaveformTestBeatmap(); + Children = new Drawable[] { - new MusicController + new FillFlowContainer { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - State = Visibility.Visible + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + new StartStopButton(), + new AudioVisualiser(), + } }, new TimelineArea { @@ -36,5 +50,83 @@ namespace osu.Game.Tests.Visual } }; } + + private class AudioVisualiser : CompositeDrawable + { + private readonly Drawable marker; + + private readonly IBindable beatmap = new Bindable(); + private IAdjustableClock adjustableClock; + + public AudioVisualiser() + { + Size = new Vector2(250, 25); + + InternalChildren = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.25f, + }, + marker = new Box + { + RelativePositionAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Width = 2, + } + }; + } + + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap) + { + this.adjustableClock = adjustableClock; + this.beatmap.BindTo(beatmap); + } + + protected override void Update() + { + base.Update(); + marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length); + } + } + + private class StartStopButton : Button + { + private IAdjustableClock adjustableClock; + private bool started; + + public StartStopButton() + { + BackgroundColour = Color4.SlateGray; + Size = new Vector2(100, 50); + Text = "Start"; + + Action = onClick; + } + + [BackgroundDependencyLoader] + private void load(IAdjustableClock adjustableClock) + { + this.adjustableClock = adjustableClock; + } + + private void onClick() + { + if (started) + { + adjustableClock.Stop(); + Text = "Start"; + } + else + { + adjustableClock.Start(); + Text = "Stop"; + } + + started = !started; + } + } } } From 08f172ba982f8c4880a516dc565e373b3f7d0ab0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 15 Jun 2018 15:38:19 +0900 Subject: [PATCH 14/15] Make testcase work on appveyor (no BASS) --- osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs index bb0e6156f6..5aa17bca7d 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs @@ -88,7 +88,9 @@ namespace osu.Game.Tests.Visual protected override void Update() { base.Update(); - marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length); + + if (beatmap.Value.Track.IsLoaded) + marker.X = (float)(adjustableClock.CurrentTime / beatmap.Value.Track.Length); } } From 5b344525e1889e6c4fbd8a5cedccc812827a314e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Jun 2018 00:27:18 +0900 Subject: [PATCH 15/15] Move proxy state check to base class --- .../Objects/Drawables/DrawableSwell.cs | 7 +------ .../Objects/Drawables/DrawableTaikoHitObject.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index 1f571544d8..df36a475d6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// private int userHits; - private bool hasProxied; private readonly SwellSymbolPiece symbol; public DrawableSwell(Swell swell) @@ -172,7 +171,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables switch (state) { case ArmedState.Idle: - hasProxied = false; UnproxyContent(); break; case ArmedState.Hit: @@ -193,11 +191,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables X = Math.Max(0, X); double t = Math.Min(HitObject.StartTime, Time.Current); - if (t == HitObject.StartTime && !hasProxied) - { + if (t == HitObject.StartTime) ProxyContent(); - hasProxied = true; - } } private bool? lastWasCentre; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index e712d62a26..a6d61f1a5a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -40,23 +40,31 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables /// protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false; + private bool isProxied; + /// /// Moves to a layer proxied above the playfield. + /// Does nothing is content is already proxied. /// protected void ProxyContent() { + if (isProxied) return; + isProxied = true; + nonProxiedContent.Remove(Content); - proxiedContent.Remove(Content); proxiedContent.Add(Content); } /// /// Moves to the normal hitobject layer. + /// Does nothing is content is not currently proxied. /// protected void UnproxyContent() { + if (!isProxied) return; + isProxied = false; + proxiedContent.Remove(Content); - nonProxiedContent.Remove(Content); nonProxiedContent.Add(Content); }