mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 19:32:55 +08:00
Extract slider tick creation so it can be shared with osu!catch
This commit is contained in:
parent
c4cc72cad9
commit
551380dd42
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -12,6 +11,7 @@ using osu.Framework.Caching;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
|
|
||||||
@ -155,116 +155,73 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
|
|
||||||
createSliderEnds();
|
foreach (var e in
|
||||||
createTicks();
|
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
||||||
createRepeatPoints();
|
|
||||||
|
|
||||||
if (LegacyLastTickOffset != null)
|
|
||||||
TailCircle.StartTime = Math.Max(StartTime + Duration / 2, TailCircle.StartTime - LegacyLastTickOffset.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createSliderEnds()
|
|
||||||
{
|
|
||||||
HeadCircle = new SliderCircle
|
|
||||||
{
|
{
|
||||||
StartTime = StartTime,
|
var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
|
||||||
Position = Position,
|
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||||
Samples = getNodeSamples(0),
|
var sampleList = new List<SampleInfo>();
|
||||||
SampleControlPoint = SampleControlPoint,
|
|
||||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
|
||||||
ComboIndex = ComboIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
TailCircle = new SliderTailCircle(this)
|
if (firstSample != null)
|
||||||
{
|
sampleList.Add(new SampleInfo
|
||||||
StartTime = EndTime,
|
|
||||||
Position = EndPosition,
|
|
||||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
|
||||||
ComboIndex = ComboIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
AddNested(HeadCircle);
|
|
||||||
AddNested(TailCircle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createTicks()
|
|
||||||
{
|
|
||||||
// A very lenient maximum length of a slider for ticks to be generated.
|
|
||||||
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
|
|
||||||
const double max_length = 100000;
|
|
||||||
|
|
||||||
var length = Math.Min(max_length, Path.Distance);
|
|
||||||
var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
|
|
||||||
|
|
||||||
if (tickDistance == 0) return;
|
|
||||||
|
|
||||||
var minDistanceFromEnd = Velocity * 10;
|
|
||||||
|
|
||||||
var spanCount = this.SpanCount();
|
|
||||||
|
|
||||||
for (var span = 0; span < spanCount; span++)
|
|
||||||
{
|
|
||||||
var spanStartTime = StartTime + span * SpanDuration;
|
|
||||||
var reversed = span % 2 == 1;
|
|
||||||
|
|
||||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
|
||||||
{
|
|
||||||
if (d > length - minDistanceFromEnd)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var distanceProgress = d / length;
|
|
||||||
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
|
|
||||||
|
|
||||||
var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
|
|
||||||
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
|
||||||
var sampleList = new List<SampleInfo>();
|
|
||||||
|
|
||||||
if (firstSample != null)
|
|
||||||
sampleList.Add(new SampleInfo
|
|
||||||
{
|
|
||||||
Bank = firstSample.Bank,
|
|
||||||
Volume = firstSample.Volume,
|
|
||||||
Name = @"slidertick",
|
|
||||||
});
|
|
||||||
|
|
||||||
AddNested(new SliderTick
|
|
||||||
{
|
{
|
||||||
SpanIndex = span,
|
Bank = firstSample.Bank,
|
||||||
SpanStartTime = spanStartTime,
|
Volume = firstSample.Volume,
|
||||||
StartTime = spanStartTime + timeProgress * SpanDuration,
|
Name = @"slidertick",
|
||||||
Position = Position + Path.PositionAt(distanceProgress),
|
|
||||||
StackHeight = StackHeight,
|
|
||||||
Scale = Scale,
|
|
||||||
Samples = sampleList
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
switch (e.Type)
|
||||||
|
{
|
||||||
|
case SliderEventType.Tick:
|
||||||
|
AddNested(new SliderTick
|
||||||
|
{
|
||||||
|
SpanIndex = e.SpanIndex,
|
||||||
|
SpanStartTime = e.SpanStartTime,
|
||||||
|
StartTime = e.StartTime,
|
||||||
|
Position = Position + Path.PositionAt(e.PathProgress),
|
||||||
|
StackHeight = StackHeight,
|
||||||
|
Scale = Scale,
|
||||||
|
Samples = sampleList
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case SliderEventType.Head:
|
||||||
|
AddNested(HeadCircle = new SliderCircle
|
||||||
|
{
|
||||||
|
StartTime = e.StartTime,
|
||||||
|
Position = Position,
|
||||||
|
Samples = getNodeSamples(0),
|
||||||
|
SampleControlPoint = SampleControlPoint,
|
||||||
|
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||||
|
ComboIndex = ComboIndex,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case SliderEventType.Tail:
|
||||||
|
AddNested(TailCircle = new SliderTailCircle(this)
|
||||||
|
{
|
||||||
|
StartTime = e.StartTime,
|
||||||
|
Position = EndPosition,
|
||||||
|
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||||
|
ComboIndex = ComboIndex,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case SliderEventType.Repeat:
|
||||||
|
AddNested(new RepeatPoint
|
||||||
|
{
|
||||||
|
RepeatIndex = e.SpanIndex,
|
||||||
|
SpanDuration = SpanDuration,
|
||||||
|
StartTime = StartTime + (e.SpanIndex + 1) * SpanDuration,
|
||||||
|
Position = Position + Path.PositionAt((e.SpanIndex + 1) % 2),
|
||||||
|
StackHeight = StackHeight,
|
||||||
|
Scale = Scale,
|
||||||
|
Samples = getNodeSamples(1 + e.SpanIndex)
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRepeatPoints()
|
private List<SampleInfo> getNodeSamples(int nodeIndex) =>
|
||||||
{
|
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
|
||||||
for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++)
|
|
||||||
{
|
|
||||||
AddNested(new RepeatPoint
|
|
||||||
{
|
|
||||||
RepeatIndex = repeatIndex,
|
|
||||||
SpanDuration = SpanDuration,
|
|
||||||
StartTime = StartTime + repeat * SpanDuration,
|
|
||||||
Position = Position + Path.PositionAt(repeat % 2),
|
|
||||||
StackHeight = StackHeight,
|
|
||||||
Scale = Scale,
|
|
||||||
Samples = getNodeSamples(1 + repeatIndex)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<SampleInfo> getNodeSamples(int nodeIndex)
|
|
||||||
{
|
|
||||||
if (nodeIndex < NodeSamples.Count)
|
|
||||||
return NodeSamples[nodeIndex];
|
|
||||||
|
|
||||||
return Samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuJudgement();
|
public override Judgement CreateJudgement() => new OsuJudgement();
|
||||||
}
|
}
|
||||||
|
115
osu.Game/Beatmaps/Formats/SliderEventGenerator.cs
Normal file
115
osu.Game/Beatmaps/Formats/SliderEventGenerator.cs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
public static class SliderEventGenerator
|
||||||
|
{
|
||||||
|
public static IEnumerable<SliderEventDescriptor> Generate(double startTime, double spanDuration, double velocity, double tickDistance, double totalDistance, int spanCount, double? legacyLastTickOffset)
|
||||||
|
{
|
||||||
|
List<SliderEventDescriptor> events = new List<SliderEventDescriptor>();
|
||||||
|
|
||||||
|
// A very lenient maximum length of a slider for ticks to be generated.
|
||||||
|
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
|
||||||
|
const double max_length = 100000;
|
||||||
|
|
||||||
|
var length = Math.Min(max_length, totalDistance);
|
||||||
|
tickDistance = MathHelper.Clamp(tickDistance, 0, length);
|
||||||
|
|
||||||
|
{
|
||||||
|
var minDistanceFromEnd = velocity * 10;
|
||||||
|
|
||||||
|
events.Add(new SliderEventDescriptor
|
||||||
|
{
|
||||||
|
Type = SliderEventType.Head,
|
||||||
|
SpanIndex = 0,
|
||||||
|
SpanStartTime = startTime,
|
||||||
|
StartTime = startTime,
|
||||||
|
PathProgress = 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tickDistance != 0)
|
||||||
|
{
|
||||||
|
for (var span = 0; span < spanCount; span++)
|
||||||
|
{
|
||||||
|
var spanStartTime = startTime + span * spanDuration;
|
||||||
|
var reversed = span % 2 == 1;
|
||||||
|
|
||||||
|
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||||
|
{
|
||||||
|
if (d > length - minDistanceFromEnd)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var pathProgress = d / length;
|
||||||
|
var timeProgress = reversed ? 1 - pathProgress : pathProgress;
|
||||||
|
|
||||||
|
events.Add(new SliderEventDescriptor
|
||||||
|
{
|
||||||
|
Type = SliderEventType.Tick,
|
||||||
|
SpanIndex = span,
|
||||||
|
SpanStartTime = spanStartTime,
|
||||||
|
StartTime = spanStartTime + timeProgress * spanDuration,
|
||||||
|
PathProgress = pathProgress,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (span < spanCount - 1)
|
||||||
|
{
|
||||||
|
events.Add(new SliderEventDescriptor
|
||||||
|
{
|
||||||
|
Type = SliderEventType.Repeat,
|
||||||
|
SpanIndex = span,
|
||||||
|
SpanStartTime = startTime + span * spanDuration,
|
||||||
|
StartTime = spanStartTime + (span + 1) * spanDuration,
|
||||||
|
PathProgress = 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalDuration = spanCount * spanDuration;
|
||||||
|
|
||||||
|
var tail = new SliderEventDescriptor
|
||||||
|
{
|
||||||
|
Type = SliderEventType.Tail,
|
||||||
|
SpanIndex = spanCount - 1,
|
||||||
|
SpanStartTime = startTime + (spanCount - 1) * spanDuration,
|
||||||
|
StartTime = startTime + totalDuration,
|
||||||
|
PathProgress = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (legacyLastTickOffset != null)
|
||||||
|
tail.StartTime = Math.Max(startTime + totalDuration / 2, tail.StartTime - legacyLastTickOffset.Value);
|
||||||
|
|
||||||
|
events.Add(tail);
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SliderEventDescriptor
|
||||||
|
{
|
||||||
|
public SliderEventType Type;
|
||||||
|
|
||||||
|
public int SpanIndex;
|
||||||
|
|
||||||
|
public double SpanStartTime;
|
||||||
|
|
||||||
|
public double StartTime;
|
||||||
|
|
||||||
|
public double PathProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SliderEventType
|
||||||
|
{
|
||||||
|
Tick,
|
||||||
|
Head,
|
||||||
|
Tail,
|
||||||
|
Repeat
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user