mirror of
https://github.com/ppy/osu.git
synced 2025-01-12 18:23:04 +08:00
Add SpinFramesGenerator
class to simplify creating spinner tests
This commit is contained in:
parent
159b24acf7
commit
cfa4adb24d
111
osu.Game.Rulesets.Osu.Tests/SpinFramesGenerator.cs
Normal file
111
osu.Game.Rulesets.Osu.Tests/SpinFramesGenerator.cs
Normal file
@ -0,0 +1,111 @@
|
||||
// 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 osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class SpinFramesGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// A small amount to spin beyond a given angle to mitigate floating-point precision errors.
|
||||
/// </summary>
|
||||
public const float SPIN_ERROR = MathF.PI / 8;
|
||||
|
||||
/// <summary>
|
||||
/// The offset from the centre of the spinner at which to spin.
|
||||
/// </summary>
|
||||
private const float centre_spin_offset = 50;
|
||||
|
||||
private readonly double startTime;
|
||||
private readonly float startAngle;
|
||||
private readonly List<(float deltaAngle, double duration)> sequences = new List<(float deltaAngle, double duration)>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpinFramesGenerator"/> that can be used to generate spinner spin frames.
|
||||
/// </summary>
|
||||
/// <param name="startTime">The time at which to start spinning.</param>
|
||||
/// <param name="startAngle">The angle, in radians, at which to start spinning from. Defaults to the positive-y-axis.</param>
|
||||
public SpinFramesGenerator(double startTime, float startAngle = -MathF.PI / 2f)
|
||||
{
|
||||
this.startTime = startTime;
|
||||
this.startAngle = startAngle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a single spin.
|
||||
/// </summary>
|
||||
/// <param name="delta">The amount, relative to a full circle, to spin.</param>
|
||||
/// <param name="duration">The time to spend to perform the spin.</param>
|
||||
/// <returns>This <see cref="SpinFramesGenerator"/>.</returns>
|
||||
public SpinFramesGenerator Spin(float delta, double duration)
|
||||
{
|
||||
sequences.Add((delta * 2 * MathF.PI, duration));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the replay frames.
|
||||
/// </summary>
|
||||
/// <returns>The replay frames.</returns>
|
||||
public List<ReplayFrame> Build()
|
||||
{
|
||||
List<ReplayFrame> frames = new List<ReplayFrame>();
|
||||
|
||||
double lastTime = startTime;
|
||||
float lastAngle = startAngle;
|
||||
int lastDirection = 0;
|
||||
|
||||
for (int i = 0; i < sequences.Count; i++)
|
||||
{
|
||||
var seq = sequences[i];
|
||||
|
||||
int seqDirection = Math.Sign(seq.deltaAngle);
|
||||
float seqError = SPIN_ERROR * seqDirection;
|
||||
|
||||
if (seqDirection == lastDirection)
|
||||
{
|
||||
// Spinning in the same direction, but the error was already added in the last rotation.
|
||||
seqError = 0;
|
||||
}
|
||||
else if (lastDirection != 0)
|
||||
{
|
||||
// Spinning in a different direction, we need to account for the error of the start angle, so double it.
|
||||
seqError *= 2;
|
||||
}
|
||||
|
||||
double seqStartTime = lastTime;
|
||||
double seqEndTime = lastTime + seq.duration;
|
||||
float seqStartAngle = lastAngle;
|
||||
float seqEndAngle = seqStartAngle + seq.deltaAngle + seqError;
|
||||
|
||||
// Intermediate spin frames.
|
||||
for (; lastTime < seqEndTime; lastTime += 10)
|
||||
frames.Add(new OsuReplayFrame(lastTime, calcOffsetAt((lastTime - seqStartTime) / (seqEndTime - seqStartTime), seqStartAngle, seqEndAngle), OsuAction.LeftButton));
|
||||
|
||||
// Final frame at the end of the current spin.
|
||||
frames.Add(new OsuReplayFrame(lastTime, calcOffsetAt(1, seqStartAngle, seqEndAngle), OsuAction.LeftButton));
|
||||
|
||||
lastTime = seqEndTime;
|
||||
lastAngle = seqEndAngle;
|
||||
lastDirection = seqDirection;
|
||||
}
|
||||
|
||||
// Key release frame.
|
||||
if (frames.Count > 0)
|
||||
frames.Add(new OsuReplayFrame(frames[^1].Time, ((OsuReplayFrame)frames[^1]).Position));
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
private static Vector2 calcOffsetAt(double p, float startAngle, float endAngle)
|
||||
{
|
||||
float angle = startAngle + (endAngle - startAngle) * (float)p;
|
||||
return new Vector2(256, 192) + centre_spin_offset * new Vector2(MathF.Cos(angle), MathF.Sin(angle));
|
||||
}
|
||||
}
|
||||
}
|
@ -356,15 +356,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
};
|
||||
|
||||
performTest(hitObjects, new List<ReplayFrame>
|
||||
List<ReplayFrame> frames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame { Time = time_spinner - 90, Position = positionCircle, Actions = { OsuAction.LeftButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 10, Position = new Vector2(236, 192), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 20, Position = new Vector2(256, 172), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 30, Position = new Vector2(276, 192), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 40, Position = new Vector2(256, 212), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 50, Position = new Vector2(236, 192), Actions = { OsuAction.RightButton } },
|
||||
});
|
||||
};
|
||||
|
||||
frames.AddRange(new SpinFramesGenerator(time_spinner + 10)
|
||||
.Spin(1, 500)
|
||||
.Build());
|
||||
|
||||
performTest(hitObjects, frames);
|
||||
|
||||
addJudgementAssert(hitObjects[0], HitResult.Great);
|
||||
addJudgementAssert(hitObjects[1], HitResult.Meh);
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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 System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -11,14 +10,12 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
@ -59,26 +56,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
AddAssert("all max judgements", () => judgementResults.All(result => result.Type == result.Judgement.MaxResult));
|
||||
}
|
||||
|
||||
private static List<ReplayFrame> generateReplay(int spins)
|
||||
{
|
||||
var replayFrames = new List<ReplayFrame>();
|
||||
|
||||
const int frames_per_spin = 30;
|
||||
|
||||
for (int i = 0; i < spins * frames_per_spin; ++i)
|
||||
{
|
||||
float totalProgress = i / (float)(spins * frames_per_spin);
|
||||
float spinProgress = (i % frames_per_spin) / (float)frames_per_spin;
|
||||
double time = time_spinner_start + (time_spinner_end - time_spinner_start) * totalProgress;
|
||||
float posX = MathF.Cos(2 * MathF.PI * spinProgress);
|
||||
float posY = MathF.Sin(2 * MathF.PI * spinProgress);
|
||||
Vector2 finalPos = OsuPlayfield.BASE_SIZE / 2 + new Vector2(posX, posY) * 50;
|
||||
|
||||
replayFrames.Add(new OsuReplayFrame(time, finalPos, OsuAction.LeftButton));
|
||||
}
|
||||
|
||||
return replayFrames;
|
||||
}
|
||||
private static List<ReplayFrame> generateReplay(int spins) => new SpinFramesGenerator(time_spinner_start)
|
||||
.Spin(spins, time_spinner_end - time_spinner_start)
|
||||
.Build();
|
||||
|
||||
private void performTest(List<ReplayFrame> frames)
|
||||
{
|
||||
|
@ -284,15 +284,16 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
};
|
||||
|
||||
performTest(hitObjects, new List<ReplayFrame>
|
||||
List<ReplayFrame> frames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame { Time = time_spinner - 100, Position = positionCircle, Actions = { OsuAction.LeftButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 10, Position = new Vector2(236, 192), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 20, Position = new Vector2(256, 172), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 30, Position = new Vector2(276, 192), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 40, Position = new Vector2(256, 212), Actions = { OsuAction.RightButton } },
|
||||
new OsuReplayFrame { Time = time_spinner + 50, Position = new Vector2(236, 192), Actions = { OsuAction.RightButton } },
|
||||
});
|
||||
};
|
||||
|
||||
frames.AddRange(new SpinFramesGenerator(time_spinner + 10)
|
||||
.Spin(1, 500)
|
||||
.Build());
|
||||
|
||||
performTest(hitObjects, frames);
|
||||
|
||||
addJudgementAssert(hitObjects[0], HitResult.Great);
|
||||
addJudgementAssert(hitObjects[1], HitResult.Great);
|
||||
|
Loading…
Reference in New Issue
Block a user