1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 08:32:57 +08:00

Fix slider nodes using the wrong samples

This commit is contained in:
smoogipoo 2018-10-16 17:10:24 +09:00
parent 33d4ec876a
commit 47be95ce0b
17 changed files with 109 additions and 30 deletions

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType,
Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples,
NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false,

View File

@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Catch.Objects
set { Curve.ControlPoints = value; }
}
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public CurveType CurveType
{

View File

@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
return curveData.NodeSamples[index];
}
}
}

View File

@ -470,7 +470,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
return curveData.NodeSamples[index];
}
/// <summary>

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 700,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats),
NodeSamples = createEmptySamples(repeats),
StackHeight = 10
};
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = distance,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats),
NodeSamples = createEmptySamples(repeats),
StackHeight = stackHeight
};
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 600,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats)
NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 793.4417,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats)
NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 480,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats)
NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@ -246,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 1000,
RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats)
NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 300,
RepeatCount = repeats,
RepeatSamples = repeatSamples
NodeSamples = repeatSamples
};
addSlider(slider, 3, 1);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType,
Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples,
NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,

View File

@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary>
internal float LazyTravelDistance;
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; }
/// <summary>
@ -129,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{
StartTime = StartTime,
Position = Position,
Samples = Samples,
Samples = NodeSamples[0],
SampleControlPoint = SampleControlPoint,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboIndex = ComboIndex,
@ -209,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Position = Position + Curve.PositionAt(repeat % 2),
StackHeight = StackHeight,
Scale = Scale,
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
Samples = new List<SampleInfo>(NodeSamples[1 + repeatIndex])
});
}
}

View File

@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
List<List<SampleInfo>> allSamples = curveData != null ? curveData.RepeatSamples : new List<List<SampleInfo>>(new[] { samples });
List<List<SampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<SampleInfo>>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Skinning;
@ -312,5 +313,48 @@ namespace osu.Game.Tests.Beatmaps.Formats
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
public void TestDecodeSliderSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("slider-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
var slider1 = (ConvertSlider)hitObjects[0];
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
var slider2 = (ConvertSlider)hitObjects[1];
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
var slider3 = (ConvertSlider)hitObjects[2];
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
}
}
}
}

View File

@ -0,0 +1,23 @@
osu file format v14
[General]
SampleSet: Normal
[Difficulty]
SliderMultiplier:1.6
SliderTickRate:2
[TimingPoints]
24735,389.61038961039,4,1,1,25,1,0
[HitObjects]
// Unified: Normal, Normal, Normal
168,256,30579,6,0,B|248:320|320:248,2,160
// Unified: Normal+Clap, Normal+Clap, Normal+Clap
168,256,32137,6,8,B|248:320|320:248,2,160
// Nodal: Normal+Whistle, Normal, Normal+Clap
// Nodal sounds should override the unified clap sound
168,256,33696,6,8,B|248:320|320:248,2,160,2|0|8

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{
newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
NodeSamples = nodeSamples,
RepeatCount = repeatCount
};
}

View File

@ -169,6 +169,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples);
// The samples are played when the slider ends, which is the last node
result.Samples = nodeSamples[nodeSamples.Count - 1];
}
else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
@ -200,7 +203,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
}
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset;
result.Samples = convertSoundType(soundType, bankInfo);
if (result.Samples.Count == 0)
result.Samples = convertSoundType(soundType, bankInfo);
FirstObject = false;
@ -260,9 +265,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <param name="length">The slider length.</param>
/// <param name="curveType">The slider curve type.</param>
/// <param name="repeatCount">The slider repeat count.</param>
/// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param>
/// <param name="nodeSamples">The samples to be played when the slider nodes are hit. This includes the head and tail of the slider.</param>
/// <returns>The hit object.</returns>
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples);
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples);
/// <summary>
/// Creates a legacy Spinner-type hit object.

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
public double Distance { get; set; }
public List<List<SampleInfo>> RepeatSamples { get; set; }
public List<List<SampleInfo>> NodeSamples { get; set; }
public int RepeatCount { get; set; }
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{
return new ConvertSlider
{
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
NodeSamples = nodeSamples,
RepeatCount = repeatCount
};
}

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{
newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
ControlPoints = controlPoints,
Distance = Math.Max(0, length),
CurveType = curveType,
RepeatSamples = repeatSamples,
NodeSamples = nodeSamples,
RepeatCount = repeatCount
};
}

View File

@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
return new ConvertHit();
}
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{
return new ConvertSlider
{
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
RepeatSamples = repeatSamples,
NodeSamples = nodeSamples,
RepeatCount = repeatCount
};
}

View File

@ -17,9 +17,15 @@ namespace osu.Game.Rulesets.Objects.Types
int RepeatCount { get; }
/// <summary>
/// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc).
/// The samples to be played when each node of the <see cref="IHasRepeats"/> is hit.<br />
/// 0: The first node.<br />
/// 1: The first repeat.<br />
/// 2: The second repeat.<br />
/// ...<br />
/// n-1: The last repeat.<br />
/// n: The last node.
/// </summary>
List<List<SampleInfo>> RepeatSamples { get; }
List<List<SampleInfo>> NodeSamples { get; }
}
public static class HasRepeatsExtensions