mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:42:57 +08:00
Merge pull request #738 from smoogipooo/mania-timing-sections
Mania timing sections
This commit is contained in:
commit
1112dbb113
@ -18,6 +18,7 @@ using osu.Game.Rulesets.Taiko.UI;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Desktop.VisualTests.Beatmaps;
|
using osu.Desktop.VisualTests.Beatmaps;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
|
||||||
namespace osu.Desktop.VisualTests.Tests
|
namespace osu.Desktop.VisualTests.Tests
|
||||||
{
|
{
|
||||||
@ -52,6 +53,12 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
time += RNG.Next(50, 500);
|
time += RNG.Next(50, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimingInfo timing = new TimingInfo();
|
||||||
|
timing.ControlPoints.Add(new ControlPoint
|
||||||
|
{
|
||||||
|
BeatLength = 200
|
||||||
|
});
|
||||||
|
|
||||||
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
|
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
HitObjects = objects,
|
HitObjects = objects,
|
||||||
@ -64,8 +71,9 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
Artist = @"Unknown",
|
Artist = @"Unknown",
|
||||||
Title = @"Sample Beatmap",
|
Title = @"Sample Beatmap",
|
||||||
Author = @"peppy",
|
Author = @"peppy",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
TimingInfo = timing
|
||||||
});
|
});
|
||||||
|
|
||||||
Add(new Drawable[]
|
Add(new Drawable[]
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using System.Linq;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
namespace osu.Desktop.VisualTests.Tests
|
namespace osu.Desktop.VisualTests.Tests
|
||||||
{
|
{
|
||||||
@ -21,33 +24,58 @@ namespace osu.Desktop.VisualTests.Tests
|
|||||||
{
|
{
|
||||||
base.Reset();
|
base.Reset();
|
||||||
|
|
||||||
const int max_columns = 10;
|
|
||||||
|
|
||||||
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
|
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
Add(new ManiaPlayfield(cols)
|
Add(new ManiaPlayfield(cols, new List<ControlPoint>())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
SpecialColumnPosition = pos
|
SpecialColumnPosition = pos,
|
||||||
|
Scale = new Vector2(1, -1)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 1; i <= max_columns; i++)
|
Action<int, SpecialColumnPosition> createPlayfieldWithNotes = (cols, pos) =>
|
||||||
{
|
{
|
||||||
int tempI = i;
|
Clear();
|
||||||
|
|
||||||
AddStep($"{i} column" + (i > 1 ? "s" : ""), () => createPlayfield(tempI, SpecialColumnPosition.Normal));
|
ManiaPlayfield playField;
|
||||||
|
Add(playField = new ManiaPlayfield(cols, new List<ControlPoint> { new ControlPoint { BeatLength = 200 } })
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
SpecialColumnPosition = pos,
|
||||||
|
Scale = new Vector2(1, -1)
|
||||||
|
});
|
||||||
|
|
||||||
AddStep("Trigger keys down", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyDown));
|
for (int i = 0; i < cols; i++)
|
||||||
AddStep("Trigger keys up", () => ((ManiaPlayfield)Children.First()).Columns.Children.ForEach(triggerKeyUp));
|
{
|
||||||
|
playField.Add(new DrawableNote(new Note
|
||||||
|
{
|
||||||
|
StartTime = Time.Current + 1000,
|
||||||
|
Column = i
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
AddStep("Left special style", () => createPlayfield(tempI, SpecialColumnPosition.Left));
|
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
|
||||||
AddStep("Right special style", () => createPlayfield(tempI, SpecialColumnPosition.Right));
|
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||||
}
|
AddStep("Left special style", () => createPlayfield(4, SpecialColumnPosition.Left));
|
||||||
|
AddStep("Right special style", () => createPlayfield(4, SpecialColumnPosition.Right));
|
||||||
|
AddStep("5 columns", () => createPlayfield(5, SpecialColumnPosition.Normal));
|
||||||
|
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
|
||||||
|
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
|
||||||
|
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
|
||||||
|
|
||||||
AddStep("Normal special style", () => createPlayfield(max_columns, SpecialColumnPosition.Normal));
|
AddStep("Normal special style", () => createPlayfield(4, SpecialColumnPosition.Normal));
|
||||||
|
|
||||||
|
AddStep("Notes", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Normal));
|
||||||
|
AddWaitStep(10);
|
||||||
|
AddStep("Left special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Left));
|
||||||
|
AddWaitStep(10);
|
||||||
|
AddStep("Right special style", () => createPlayfieldWithNotes(4, SpecialColumnPosition.Right));
|
||||||
|
AddWaitStep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerKeyDown(Column column)
|
private void triggerKeyDown(Column column)
|
||||||
|
@ -8,6 +8,7 @@ using System;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Beatmaps;
|
using osu.Game.Rulesets.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
@ -17,7 +18,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
yield return null;
|
int availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
|
||||||
|
|
||||||
|
var positionData = original as IHasXPosition;
|
||||||
|
|
||||||
|
float localWDivisor = 512.0f / availableColumns;
|
||||||
|
int column = MathHelper.Clamp((int)Math.Floor((positionData?.X ?? 1) / localWDivisor), 0, availableColumns - 1);
|
||||||
|
|
||||||
|
yield return new Note
|
||||||
|
{
|
||||||
|
StartTime = original.StartTime,
|
||||||
|
Column = column,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,24 +27,20 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
// This will be fixed when new designs are given or the current design is finalized.
|
// This will be fixed when new designs are given or the current design is finalized.
|
||||||
bodyPiece = new BodyPiece
|
bodyPiece = new BodyPiece
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.TopCentre,
|
||||||
},
|
},
|
||||||
headPiece = new NotePiece
|
headPiece = new NotePiece
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre
|
Origin = Anchor.TopCentre
|
||||||
},
|
},
|
||||||
tailPiece = new NotePiece
|
tailPiece = new NotePiece
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.TopCentre
|
Origin = Anchor.BottomCentre
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// The "length" of the hold note stops at the "base" of the tail piece
|
|
||||||
// but we want to contain the tail piece within our bounds
|
|
||||||
Height += (float)HitObject.Duration / headPiece.Height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
|
@ -22,9 +22,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
|
|
||||||
Anchor = Anchor.TopCentre;
|
|
||||||
Origin = Anchor.BottomCentre;
|
|
||||||
|
|
||||||
RelativePositionAxes = Axes.Y;
|
RelativePositionAxes = Axes.Y;
|
||||||
Y = (float)HitObject.StartTime;
|
Y = (float)HitObject.StartTime;
|
||||||
|
|
||||||
|
@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
public DrawableNote(Note hitObject)
|
public DrawableNote(Note hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.Both;
|
||||||
AutoSizeAxes = Axes.Y;
|
Height = 100;
|
||||||
|
|
||||||
Add(headPiece = new NotePiece
|
Add(headPiece = new NotePiece
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre
|
Origin = Anchor.TopCentre
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,6 +38,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
if (Time.Current > HitObject.StartTime)
|
||||||
|
Colour = Color4.Green;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
153
osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs
Normal file
153
osu.Game.Rulesets.Mania/Timing/ControlPointContainer.cs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Timing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A container in which added drawables are put into a relative coordinate space spanned by a length of time.
|
||||||
|
/// <para>
|
||||||
|
/// This container contains <see cref="ControlPoint"/>s which scroll inside this container.
|
||||||
|
/// Drawables added to this container are moved inside the relevant <see cref="ControlPoint"/>,
|
||||||
|
/// and as such, will scroll along with the <see cref="ControlPoint"/>s.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public class ControlPointContainer : Container<Drawable>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time which this container spans.
|
||||||
|
/// </summary>
|
||||||
|
public double TimeSpan { get; set; }
|
||||||
|
|
||||||
|
private readonly List<DrawableControlPoint> drawableControlPoints;
|
||||||
|
|
||||||
|
public ControlPointContainer(IEnumerable<ControlPoint> timingChanges)
|
||||||
|
{
|
||||||
|
drawableControlPoints = timingChanges.Select(t => new DrawableControlPoint(t)).ToList();
|
||||||
|
Children = drawableControlPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a drawable to this container. Note that the drawable added must have its Y-position be
|
||||||
|
/// an absolute unit of time that is _not_ relative to <see cref="TimeSpan"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawable">The drawable to add.</param>
|
||||||
|
public override void Add(Drawable drawable)
|
||||||
|
{
|
||||||
|
// Always add timing sections to ourselves
|
||||||
|
if (drawable is DrawableControlPoint)
|
||||||
|
{
|
||||||
|
base.Add(drawable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var controlPoint = drawableControlPoints.LastOrDefault(t => t.CanContain(drawable)) ?? drawableControlPoints.FirstOrDefault();
|
||||||
|
|
||||||
|
if (controlPoint == null)
|
||||||
|
throw new Exception("Could not find suitable timing section to add object to.");
|
||||||
|
|
||||||
|
controlPoint.Add(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A container that contains drawables within the time span of a timing section.
|
||||||
|
/// <para>
|
||||||
|
/// The content of this container will scroll relative to the current time.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
private class DrawableControlPoint : Container
|
||||||
|
{
|
||||||
|
private readonly ControlPoint timingChange;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a drawable control point. The height of this container will be proportional
|
||||||
|
/// to the beat length of the control point it is initialized with such that, e.g. a beat length
|
||||||
|
/// of 500ms results in this container being twice as high as its parent, which further means that
|
||||||
|
/// the content container will scroll at twice the normal rate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timingChange">The control point to create the drawable control point for.</param>
|
||||||
|
public DrawableControlPoint(ControlPoint timingChange)
|
||||||
|
{
|
||||||
|
this.timingChange = timingChange;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
AddInternal(content = new AutoTimeRelativeContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
RelativePositionAxes = Axes.Both,
|
||||||
|
Y = (float)timingChange.Time
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
var parent = (ControlPointContainer)Parent;
|
||||||
|
|
||||||
|
// Adjust our height to account for the speed changes
|
||||||
|
Height = (float)(1000 / timingChange.BeatLength / timingChange.SpeedMultiplier);
|
||||||
|
RelativeCoordinateSpace = new Vector2(1, (float)parent.TimeSpan);
|
||||||
|
|
||||||
|
// Scroll the content
|
||||||
|
content.Y = (float)(timingChange.Time - Time.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(Drawable drawable)
|
||||||
|
{
|
||||||
|
// The previously relatively-positioned drawable will now become relative to content, but since the drawable has no knowledge of content,
|
||||||
|
// we need to offset it back by content's position position so that it becomes correctly relatively-positioned to content
|
||||||
|
// This can be removed if hit objects were stored such that either their StartTime or their "beat offset" was relative to the timing change
|
||||||
|
// they belonged to, but this requires a radical change to the beatmap format which we're not ready to do just yet
|
||||||
|
drawable.Y -= (float)timingChange.Time;
|
||||||
|
|
||||||
|
base.Add(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this control point can contain a drawable. This control point can contain a drawable if the drawable is positioned "after" this control point.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawable">The drawable to check.</param>
|
||||||
|
public bool CanContain(Drawable drawable) => content.Y <= drawable.Y;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A container which always keeps its height and relative coordinate space "auto-sized" to its children.
|
||||||
|
/// <para>
|
||||||
|
/// This is used in the case where children are relatively positioned/sized to time values (e.g. notes/bar lines) to keep
|
||||||
|
/// such children wrapped inside a container, otherwise they would disappear due to container flattening.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
private class AutoTimeRelativeContainer : Container
|
||||||
|
{
|
||||||
|
public override void InvalidateFromChild(Invalidation invalidation)
|
||||||
|
{
|
||||||
|
// We only want to re-compute our size when a child's size or position has changed
|
||||||
|
if ((invalidation & Invalidation.Geometry) == 0)
|
||||||
|
{
|
||||||
|
base.InvalidateFromChild(invalidation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Children.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
float height = Children.Select(child => child.Y + child.Height).Max();
|
||||||
|
|
||||||
|
Height = height;
|
||||||
|
RelativeCoordinateSpace = new Vector2(1, height);
|
||||||
|
|
||||||
|
base.InvalidateFromChild(invalidation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,13 +12,17 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Timing;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class Column : Container, IHasAccentColour
|
public class Column : Container, IHasAccentColour
|
||||||
{
|
{
|
||||||
private const float key_size = 50;
|
|
||||||
|
|
||||||
private const float key_icon_size = 10;
|
private const float key_icon_size = 10;
|
||||||
private const float key_icon_corner_radius = 3;
|
private const float key_icon_corner_radius = 3;
|
||||||
private const float key_icon_border_radius = 2;
|
private const float key_icon_border_radius = 2;
|
||||||
@ -35,7 +39,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
private readonly Container hitTargetBar;
|
private readonly Container hitTargetBar;
|
||||||
private readonly Container keyIcon;
|
private readonly Container keyIcon;
|
||||||
|
|
||||||
public Column()
|
public readonly ControlPointContainer ControlPointContainer;
|
||||||
|
|
||||||
|
public Column(IEnumerable<ControlPoint> timingChanges)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = column_width;
|
Width = column_width;
|
||||||
@ -48,59 +54,16 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Alpha = 0.2f
|
Alpha = 0.2f
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Key + hit target",
|
Name = "Hit target + hit objects",
|
||||||
Anchor = Anchor.BottomCentre,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.BottomCentre,
|
Padding = new MarginPadding { Top = ManiaPlayfield.HIT_TARGET_POSITION},
|
||||||
RelativeSizeAxes = Axes.X,
|
Children = new Drawable[]
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Children = new[]
|
|
||||||
{
|
{
|
||||||
new Container
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Hit target",
|
Name = "Hit target",
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = hit_target_height,
|
Height = hit_target_height,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -114,8 +77,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
hitTargetBar = new Container
|
hitTargetBar = new Container
|
||||||
{
|
{
|
||||||
Name = "Bar",
|
Name = "Bar",
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = hit_target_bar_height,
|
Height = hit_target_bar_height,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
@ -128,6 +89,47 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ControlPointContainer = new ControlPointContainer(timingChanges)
|
||||||
|
{
|
||||||
|
Name = "Hit objects",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Key",
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = ManiaPlayfield.HIT_TARGET_POSITION,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,9 +178,17 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
|
||||||
|
{
|
||||||
|
ControlPointContainer.Add(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.Key == Key && !args.Repeat)
|
if (args.Repeat)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (args.Key == Key)
|
||||||
{
|
{
|
||||||
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
|
background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint);
|
||||||
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
|
keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint);
|
||||||
@ -198,5 +208,4 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using OpenTK;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Beatmaps;
|
using osu.Game.Rulesets.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
@ -15,20 +22,67 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
public class ManiaHitRenderer : HitRenderer<ManiaHitObject, ManiaJudgement>
|
public class ManiaHitRenderer : HitRenderer<ManiaHitObject, ManiaJudgement>
|
||||||
{
|
{
|
||||||
private readonly int columns;
|
public int? Columns;
|
||||||
|
|
||||||
public ManiaHitRenderer(WorkingBeatmap beatmap, int columns = 5)
|
public ManiaHitRenderer(WorkingBeatmap beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
this.columns = columns;
|
}
|
||||||
|
|
||||||
|
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield()
|
||||||
|
{
|
||||||
|
ControlPoint firstTimingChange = Beatmap.TimingInfo.ControlPoints.FirstOrDefault(t => t.TimingChange);
|
||||||
|
|
||||||
|
if (firstTimingChange == null)
|
||||||
|
throw new Exception("The Beatmap contains no timing points!");
|
||||||
|
|
||||||
|
// Generate the timing points, making non-timing changes use the previous timing change
|
||||||
|
var timingChanges = Beatmap.TimingInfo.ControlPoints.Select(c =>
|
||||||
|
{
|
||||||
|
ControlPoint t = c.Clone();
|
||||||
|
|
||||||
|
if (c.TimingChange)
|
||||||
|
firstTimingChange = c;
|
||||||
|
else
|
||||||
|
t.BeatLength = firstTimingChange.BeatLength;
|
||||||
|
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
|
double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime;
|
||||||
|
|
||||||
|
// Perform some post processing of the timing changes
|
||||||
|
timingChanges = timingChanges
|
||||||
|
// Collapse sections after the last hit object
|
||||||
|
.Where(s => s.Time <= lastObjectTime)
|
||||||
|
// Collapse sections with the same start time
|
||||||
|
.GroupBy(s => s.Time).Select(g => g.Last()).OrderBy(s => s.Time)
|
||||||
|
// Collapse sections with the same beat length
|
||||||
|
.GroupBy(s => s.BeatLength * s.SpeedMultiplier).Select(g => g.First())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize), timingChanges)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
// Invert by default for now (should be moved to config/skin later)
|
||||||
|
Scale = new Vector2(1, -1)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||||
|
|
||||||
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(columns);
|
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
||||||
|
{
|
||||||
|
var note = h as Note;
|
||||||
|
if (note != null)
|
||||||
|
return new DrawableNote(note);
|
||||||
|
|
||||||
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h) => null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,25 @@ using osu.Framework.Allocation;
|
|||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Timing;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class ManiaPlayfield : Playfield<ManiaHitObject, ManiaJudgement>
|
public class ManiaPlayfield : Playfield<ManiaHitObject, ManiaJudgement>
|
||||||
{
|
{
|
||||||
|
public const float HIT_TARGET_POSITION = 50;
|
||||||
|
|
||||||
|
private const float time_span_default = 5000;
|
||||||
|
private const float time_span_min = 10;
|
||||||
|
private const float time_span_max = 50000;
|
||||||
|
private const float time_span_step = 200;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default column keys, expanding outwards from the middle as more column are added.
|
/// 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...
|
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
|
||||||
@ -43,12 +57,14 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public readonly FlowContainer<Column> Columns;
|
public readonly FlowContainer<Column> Columns;
|
||||||
|
|
||||||
|
private readonly ControlPointContainer barlineContainer;
|
||||||
|
|
||||||
private List<Color4> normalColumnColours = new List<Color4>();
|
private List<Color4> normalColumnColours = new List<Color4>();
|
||||||
private Color4 specialColumnColour;
|
private Color4 specialColumnColour;
|
||||||
|
|
||||||
private readonly int columnCount;
|
private readonly int columnCount;
|
||||||
|
|
||||||
public ManiaPlayfield(int columnCount)
|
public ManiaPlayfield(int columnCount, IEnumerable<ControlPoint> timingChanges)
|
||||||
{
|
{
|
||||||
this.columnCount = columnCount;
|
this.columnCount = columnCount;
|
||||||
|
|
||||||
@ -59,10 +75,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
|
Masking = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
@ -72,18 +89,34 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
},
|
},
|
||||||
Columns = new FillFlowContainer<Column>
|
Columns = new FillFlowContainer<Column>
|
||||||
{
|
{
|
||||||
|
Name = "Columns",
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Padding = new MarginPadding { Left = 1, Right = 1 },
|
Padding = new MarginPadding { Left = 1, Right = 1 },
|
||||||
Spacing = new Vector2(1, 0)
|
Spacing = new Vector2(1, 0)
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
barlineContainer = new ControlPointContainer(timingChanges)
|
||||||
|
{
|
||||||
|
Name = "Bar lines",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < columnCount; i++)
|
for (int i = 0; i < columnCount; i++)
|
||||||
Columns.Add(new Column());
|
Columns.Add(new Column(timingChanges));
|
||||||
|
|
||||||
|
TimeSpan = time_span_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -155,5 +188,73 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return column == columnCount - 1;
|
return column == columnCount - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.Children.ElementAt(h.HitObject.Column).Add(h);
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
|
{
|
||||||
|
if (state.Keyboard.ControlPressed)
|
||||||
|
{
|
||||||
|
switch (args.Key)
|
||||||
|
{
|
||||||
|
case Key.Minus:
|
||||||
|
transformTimeSpanTo(TimeSpan + time_span_step, 200, EasingTypes.OutQuint);
|
||||||
|
break;
|
||||||
|
case Key.Plus:
|
||||||
|
transformTimeSpanTo(TimeSpan - time_span_step, 200, EasingTypes.OutQuint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double timeSpan;
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time which the length of the playfield spans.
|
||||||
|
/// </summary>
|
||||||
|
public double TimeSpan
|
||||||
|
{
|
||||||
|
get { return timeSpan; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (timeSpan == value)
|
||||||
|
return;
|
||||||
|
timeSpan = value;
|
||||||
|
|
||||||
|
timeSpan = MathHelper.Clamp(timeSpan, time_span_min, time_span_max);
|
||||||
|
|
||||||
|
barlineContainer.TimeSpan = value;
|
||||||
|
Columns.Children.ForEach(c => c.ControlPointContainer.TimeSpan = value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transformTimeSpanTo(double newTimeSpan, double duration = 0, EasingTypes easing = EasingTypes.None)
|
||||||
|
{
|
||||||
|
TransformTo(() => TimeSpan, newTimeSpan, duration, easing, new TransformTimeSpan());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TransformTimeSpan : Transform<double>
|
||||||
|
{
|
||||||
|
public override double CurrentValue
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
double time = Time?.Current ?? 0;
|
||||||
|
if (time < StartTime) return StartValue;
|
||||||
|
if (time >= EndTime) return EndValue;
|
||||||
|
|
||||||
|
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Apply(Drawable d)
|
||||||
|
{
|
||||||
|
base.Apply(d);
|
||||||
|
|
||||||
|
var p = (ManiaPlayfield)d;
|
||||||
|
p.TimeSpan = CurrentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
<Compile Include="ManiaRuleset.cs" />
|
<Compile Include="ManiaRuleset.cs" />
|
||||||
<Compile Include="Mods\ManiaMod.cs" />
|
<Compile Include="Mods\ManiaMod.cs" />
|
||||||
<Compile Include="UI\SpecialColumnPosition.cs" />
|
<Compile Include="UI\SpecialColumnPosition.cs" />
|
||||||
|
<Compile Include="Timing\ControlPointContainer.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">
|
||||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Beatmaps.Timing
|
|||||||
{
|
{
|
||||||
public string SampleBank;
|
public string SampleBank;
|
||||||
public int SampleVolume;
|
public int SampleVolume;
|
||||||
public TimeSignatures TimeSignature;
|
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
|
||||||
public double Time;
|
public double Time;
|
||||||
public double BeatLength = 500;
|
public double BeatLength = 500;
|
||||||
public double SpeedMultiplier = 1;
|
public double SpeedMultiplier = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user