1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 22:07:25 +08:00

Full rewrite of timing section code, partially working for now.

This commit is contained in:
smoogipooo 2017-05-11 12:33:19 +09:00
parent 185e98aa14
commit b10df74079
7 changed files with 219 additions and 130 deletions

View File

@ -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<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols)
Add(new ManiaPlayfield(cols, new List<TimingSection>())
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,

View File

@ -1,19 +1,27 @@
// 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 OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Mania.Timing
{
/// <summary>
/// A container that contains hit objects within the time span of a timing section.
/// </summary>
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);
}
}
}

View File

@ -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<DrawableTimingSection> timingSectionContainer;
public Column()
public readonly TimingSectionContainer TimingSectionContainer;
public Column(IEnumerable<TimingSection> 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<DrawableTimingSection>
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<ManiaHitObject, ManiaJudgement> 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;
}
/// <summary>
/// The amount of time which the length of this column spans.
/// </summary>
private double timeSpan
{
get { return timingSectionContainer.RelativeCoordinateSpace.Y; }
set { timingSectionContainer.RelativeCoordinateSpace = new Vector2(1, (float)MathHelper.Clamp(value, time_span_min, time_span_max)); }
}
}
}

View File

@ -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<ManiaHitObject, ManiaJudgement> CreatePlayfield()
{
var maniaPlayfield = Playfield as ManiaPlayfield;
if (maniaPlayfield == null)
return;
var sections = new List<TimingSection>();
List<TimingSection> timingSections = new List<TimingSection>();
// 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<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(Columns ?? (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize));
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;
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote);
return null;
}
}
}

View File

@ -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<ManiaHitObject, ManiaJudgement>
{
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;
/// <summary>
/// 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<Column> Columns;
private readonly TimingSectionContainer barlineContainer;
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
public ManiaPlayfield(int columnCount, IEnumerable<TimingSection> timingSections)
{
this.columnCount = columnCount;
@ -77,18 +87,29 @@ namespace osu.Game.Rulesets.Mania.UI
},
Columns = new FillFlowContainer<Column>
{
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<ManiaHitObject, ManiaJudgement> 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;
/// <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;
barlineContainer.TimeSpan = value;
Columns.Children.ForEach(c => c.TimingSectionContainer.TimeSpan = value);
}
}
}
}

View File

@ -0,0 +1,39 @@
// 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.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<DrawableTimingSection>
{
/// <summary>
/// The amount of time which the length of this container spans.
/// </summary>
public double TimeSpan
{
get { return RelativeCoordinateSpace.Y; }
set { RelativeCoordinateSpace = new Vector2(1, (float)value); }
}
public TimingSectionContainer(IEnumerable<TimingSection> 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);
}
}
}

View File

@ -70,6 +70,7 @@
<Compile Include="ManiaRuleset.cs" />
<Compile Include="Mods\ManiaMod.cs" />
<Compile Include="UI\SpecialColumnPosition.cs" />
<Compile Include="UI\TimingSectionContainer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">