diff --git a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs index 2d4414d19f..459f4589b5 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseManiaPlayfield.cs @@ -8,6 +8,8 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.UI; using System.Linq; using System; +using System.Collections.Generic; +using osu.Game.Rulesets.Mania.Timing; namespace osu.Desktop.VisualTests.Tests { @@ -26,7 +28,7 @@ namespace osu.Desktop.VisualTests.Tests Action createPlayfield = (cols, pos) => { Clear(); - Add(new ManiaPlayfield(cols) + Add(new ManiaPlayfield(cols, new List()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Mania/Timing/DrawableTimingSection.cs b/osu.Game.Rulesets.Mania/Timing/DrawableTimingSection.cs index 8dcb39ae0d..b5943c272a 100644 --- a/osu.Game.Rulesets.Mania/Timing/DrawableTimingSection.cs +++ b/osu.Game.Rulesets.Mania/Timing/DrawableTimingSection.cs @@ -1,19 +1,27 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; + namespace osu.Game.Rulesets.Mania.Timing { + /// + /// A container that contains hit objects within the time span of a timing section. + /// public class DrawableTimingSection : Container { - private readonly TimingSection section; + public readonly TimingSection TimingSection; public DrawableTimingSection(TimingSection section) { - this.section = section; + TimingSection = section; + + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; RelativePositionAxes = Axes.Y; Y = -(float)section.StartTime; @@ -23,5 +31,10 @@ namespace osu.Game.Rulesets.Mania.Timing RelativeCoordinateSpace = new Vector2(1, Height); } + + protected override void Update() + { + Y = (float)(Time.Current - TimingSection.StartTime); + } } } \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 681c5a5ef2..a140fa1984 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -12,14 +12,18 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Colour; using osu.Framework.Input; using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Timing; +using osu.Game.Rulesets.Mania.Timing; +using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Judgements; namespace osu.Game.Rulesets.Mania.UI { public class Column : Container, IHasAccentColour { - private const float key_size = 50; - private const float key_icon_size = 10; private const float key_icon_corner_radius = 3; private const float key_icon_border_radius = 2; @@ -30,24 +34,20 @@ namespace osu.Game.Rulesets.Mania.UI private const float column_width = 45; private const float special_column_width = 70; - private const double time_span_default = 2000; - private const double time_span_min = 10; - private const double time_span_max = 20000; - private const double time_span_step = 100; - public Key Key; private readonly Box background; private readonly Container hitTargetBar; private readonly Container keyIcon; - private readonly Container timingSectionContainer; - public Column() + public readonly TimingSectionContainer TimingSectionContainer; + + public Column(IEnumerable timingSections) { RelativeSizeAxes = Axes.Y; Width = column_width; - Children = new Drawable[] + InternalChildren = new Drawable[] { background = new Box { @@ -55,96 +55,85 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both, Alpha = 0.2f }, - new FillFlowContainer + new Container { - Name = "Key + hit target", + Name = "Key", Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Children = new[] + Height = ManiaPlayfield.HIT_TARGET_POSITION, + Children = new Drawable[] { - new Container + new Box { - Name = "Key", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = key_size, - Children = new Drawable[] - { - new Box - { - Name = "Key gradient", - RelativeSizeAxes = Axes.Both, - ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)), - Alpha = 0.5f - }, - keyIcon = new Container - { - Name = "Key icon", - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(key_icon_size), - Masking = true, - CornerRadius = key_icon_corner_radius, - BorderThickness = 2, - BorderColour = Color4.White, // Not true - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true - } - } - } - } + Name = "Key gradient", + RelativeSizeAxes = Axes.Both, + ColourInfo = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)), + Alpha = 0.5f }, - new Container + keyIcon = new Container { - Name = "Hit target", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = hit_target_height, - Children = new Drawable[] + Name = "Key icon", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(key_icon_size), + Masking = true, + CornerRadius = key_icon_corner_radius, + BorderThickness = 2, + BorderColour = Color4.White, // Not true + Children = new[] { new Box { - Name = "Background", RelativeSizeAxes = Axes.Both, - Colour = Color4.Black - }, - hitTargetBar = new Container - { - Name = "Bar", - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = hit_target_bar_height, - Masking = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both - } - } + Alpha = 0, + AlwaysPresent = true } } } } }, - timingSectionContainer = new Container + TimingSectionContainer = new TimingSectionContainer(timingSections) { + Name = "Hit objects", + RelativeSizeAxes = Axes.Both, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.Both, - Y = -hit_target_bar_height, - RelativeCoordinateSpace = new Vector2(1, (float)time_span_default) + Y = -ManiaPlayfield.HIT_TARGET_POSITION + }, + new Container + { + Name = "Hit target", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = hit_target_height, + Y = -ManiaPlayfield.HIT_TARGET_POSITION, + Children = new Drawable[] + { + new Box + { + Name = "Background", + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black + }, + hitTargetBar = new Container + { + Name = "Bar", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = hit_target_bar_height, + Masking = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both + } + } + } + } } }; } @@ -191,7 +180,10 @@ namespace osu.Game.Rulesets.Mania.UI } } - public void AddTimingSection(TimingSection timingSection) => timingSectionContainer.Add(new DrawableTimingSection(timingSection)); + public void Add(DrawableHitObject hitObject) + { + TimingSectionContainer.Add(hitObject); + } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { @@ -204,19 +196,6 @@ namespace osu.Game.Rulesets.Mania.UI keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint); } - if (state.Keyboard.ControlPressed) - { - switch (args.Key) - { - case Key.Minus: - timeSpan += time_span_step; - break; - case Key.Plus: - timeSpan -= time_span_step; - break; - } - } - return false; } @@ -230,15 +209,5 @@ namespace osu.Game.Rulesets.Mania.UI return false; } - - /// - /// The amount of time which the length of this column spans. - /// - private double timeSpan - { - get { return timingSectionContainer.RelativeCoordinateSpace.Y; } - set { timingSectionContainer.RelativeCoordinateSpace = new Vector2(1, (float)MathHelper.Clamp(value, time_span_min, time_span_max)); } - } } - } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index b34442b1e1..20a7d9983e 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -12,11 +12,12 @@ using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.Taiko.Timing; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI @@ -28,17 +29,11 @@ namespace osu.Game.Rulesets.Mania.UI public ManiaHitRenderer(WorkingBeatmap beatmap) : base(beatmap) { - // Has to be done before drawable hit objects are generated in load() - loadTimingSections(); } - private void loadTimingSections() + protected override Playfield CreatePlayfield() { - var maniaPlayfield = Playfield as ManiaPlayfield; - if (maniaPlayfield == null) - return; - - var sections = new List(); + List timingSections = new List(); // Construct all the relevant timing sections ControlPoint lastTimingChange = null; @@ -47,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.UI if (point.TimingChange) lastTimingChange = point; - sections.Add(new TimingSection + timingSections.Add(new TimingSection { StartTime = point.Time, // Todo: Should this be dividing by beatlength? @@ -59,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.UI double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime; // Perform some post processing of the timing sections - sections = sections + timingSections = timingSections // Collapse sections after the last hit object .Where(s => s.StartTime <= lastObjectTime) // Collapse sections with the same start time @@ -69,27 +64,36 @@ namespace osu.Game.Rulesets.Mania.UI .ToList(); // Determine duration of timing sections - for (int i = 0; i < sections.Count; i++) + for (int i = 0; i < timingSections.Count; i++) { - if (i < sections.Count - 1) - sections[i].Duration = sections[i + 1].StartTime - sections[i].StartTime; + if (i < timingSections.Count - 1) + timingSections[i].Duration = timingSections[i + 1].StartTime - timingSections[i].StartTime; else { // Extra length added for the last timing section to extend past the last hitobject - double extraLength = sections[i].BeatLength * (int)sections[i].TimeSignature; - sections[i].Duration = lastObjectTime + extraLength - sections[i].StartTime; + double extraLength = timingSections[i].BeatLength * (int)timingSections[i].TimeSignature; + timingSections[i].Duration = lastObjectTime + extraLength - timingSections[i].StartTime; } } - sections.ForEach(s => maniaPlayfield.Columns.Children.ForEach(c => c.AddTimingSection(s))); + return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingSections); } public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(); - protected override Playfield CreatePlayfield() => new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)); + protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) + { + var note = h as Note; + if (note != null) + return new DrawableNote(note); - protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h) => null; + var holdNote = h as HoldNote; + if (holdNote != null) + return new DrawableHoldNote(holdNote); + + return null; + } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index e468d6f533..4572239b55 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -16,15 +16,23 @@ using osu.Framework.Allocation; using OpenTK.Input; using System.Linq; using System.Collections.Generic; -using osu.Game.Rulesets.Taiko.Timing; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Timing; +using osu.Framework.Input; namespace osu.Game.Rulesets.Mania.UI { public class ManiaPlayfield : Playfield { + public const float HIT_TARGET_POSITION = 50; + + private const float time_span_default = 500; + private const float time_span_min = 10; + private const float time_span_max = 20000; + private const float time_span_step = 100; + /// /// Default column keys, expanding outwards from the middle as more column are added. /// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc... @@ -48,12 +56,14 @@ namespace osu.Game.Rulesets.Mania.UI public readonly FlowContainer Columns; + private readonly TimingSectionContainer barlineContainer; + private List normalColumnColours = new List(); private Color4 specialColumnColour; private readonly int columnCount; - public ManiaPlayfield(int columnCount) + public ManiaPlayfield(int columnCount, IEnumerable timingSections) { this.columnCount = columnCount; @@ -77,18 +87,29 @@ namespace osu.Game.Rulesets.Mania.UI }, Columns = new FillFlowContainer { + Name = "Columns", RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, Direction = FillDirection.Horizontal, Padding = new MarginPadding { Left = 1, Right = 1 }, Spacing = new Vector2(1, 0) + }, + barlineContainer = new TimingSectionContainer(timingSections) + { + Name = "Barlines", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Bottom = HIT_TARGET_POSITION } } } } }; for (int i = 0; i < columnCount; i++) - Columns.Add(new Column()); + Columns.Add(new Column(timingSections)); + + TimeSpan = time_span_default; } [BackgroundDependencyLoader] @@ -162,5 +183,45 @@ namespace osu.Game.Rulesets.Mania.UI } public override void Add(DrawableHitObject h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h); + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) + return false; + + if (state.Keyboard.ControlPressed) + { + switch (args.Key) + { + case Key.Minus: + TimeSpan += time_span_step; + break; + case Key.Plus: + TimeSpan -= time_span_step; + break; + } + } + + return false; + } + + private double timeSpan; + /// + /// The amount of time which the length of the playfield spans. + /// + public double TimeSpan + { + get { return timeSpan; } + set + { + if (timeSpan == value) + return; + timeSpan = value; + + barlineContainer.TimeSpan = value; + Columns.Children.ForEach(c => c.TimingSectionContainer.TimeSpan = value); + } + } + } } diff --git a/osu.Game.Rulesets.Mania/UI/TimingSectionContainer.cs b/osu.Game.Rulesets.Mania/UI/TimingSectionContainer.cs new file mode 100644 index 0000000000..378d4279e5 --- /dev/null +++ b/osu.Game.Rulesets.Mania/UI/TimingSectionContainer.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Timing; +using osu.Game.Rulesets.Objects.Drawables; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class TimingSectionContainer : Container + { + /// + /// The amount of time which the length of this container spans. + /// + public double TimeSpan + { + get { return RelativeCoordinateSpace.Y; } + set { RelativeCoordinateSpace = new Vector2(1, (float)value); } + } + + public TimingSectionContainer(IEnumerable timingSections) + { + Children = timingSections.Select(t => new DrawableTimingSection(t)); + } + + public void Add(Drawable drawable) + { + var section = Children.LastOrDefault(t => t.TimingSection.StartTime <= drawable.Y) ?? Children.First(); + drawable.Y -= (float)section.TimingSection.StartTime; + section.Add(drawable); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index f745845f6e..e1d033c214 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -70,6 +70,7 @@ +