1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 03:07:26 +08:00
osu-lazer/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

347 lines
14 KiB
C#
Raw Normal View History

// 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.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2017-09-18 21:32:49 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
2019-10-30 16:05:15 +08:00
using System.Linq;
2018-03-02 14:34:31 +08:00
using NUnit.Framework;
2017-09-18 21:32:49 +08:00
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
2021-07-15 13:36:37 +08:00
using osu.Framework.Utils;
2017-09-18 21:32:49 +08:00
using osu.Game.Beatmaps.ControlPoints;
2019-03-25 00:02:36 +08:00
using osu.Game.Graphics;
2017-09-18 21:32:49 +08:00
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Play;
2018-11-20 15:51:59 +08:00
using osuTK.Graphics;
2018-04-13 17:19:50 +08:00
2019-03-25 00:02:36 +08:00
namespace osu.Game.Tests.Visual.UserInterface
2017-09-18 21:32:49 +08:00
{
2018-03-02 14:34:31 +08:00
[TestFixture]
public partial class TestSceneBeatSyncedContainer : OsuTestScene
2017-09-18 21:32:49 +08:00
{
private TestBeatSyncedContainer beatContainer;
private MasterGameplayClockContainer gameplayClockContainer;
[SetUpSteps]
public void SetUpSteps()
2017-09-18 21:32:49 +08:00
{
AddStep("Set beatmap", () =>
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
});
2018-04-13 17:19:50 +08:00
AddStep("Create beat sync container", () =>
2017-09-18 21:32:49 +08:00
{
Children = new Drawable[]
{
gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, 0)
{
Child = beatContainer = new TestBeatSyncedContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
}
};
2017-09-18 21:32:49 +08:00
});
AddStep("Start playback", () => gameplayClockContainer.Start());
2017-09-18 21:32:49 +08:00
}
2018-04-13 17:19:50 +08:00
[TestCase(false)]
[TestCase(true)]
public void TestDisallowMistimedEventFiring(bool allowMistimed)
{
int? lastBeatIndex = null;
double? lastActuationTime = null;
TimingControlPoint lastTimingPoint = null;
AddStep($"set mistimed to {(allowMistimed ? "allowed" : "disallowed")}", () => beatContainer.AllowMistimedEventFiring = allowMistimed);
AddStep("Set time before zero", () =>
{
2022-06-24 20:25:23 +08:00
beatContainer.NewBeat = (i, timingControlPoint, _, _) =>
{
lastActuationTime = gameplayClockContainer.CurrentTime;
lastTimingPoint = timingControlPoint;
lastBeatIndex = i;
beatContainer.NewBeat = null;
};
gameplayClockContainer.Seek(-1000);
});
AddUntilStep("wait for trigger", () => lastBeatIndex != null);
if (!allowMistimed)
{
AddAssert("trigger is near beat length",
() => lastActuationTime != null && lastBeatIndex != null && Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength, lastActuationTime.Value,
BeatSyncedContainer.MISTIMED_ALLOWANCE));
}
else
{
AddAssert("trigger is not near beat length",
() => lastActuationTime != null && lastBeatIndex != null && !Precision.AlmostEquals(lastTimingPoint.Time + lastBeatIndex.Value * lastTimingPoint.BeatLength,
lastActuationTime.Value, BeatSyncedContainer.MISTIMED_ALLOWANCE));
}
}
[Test]
public void TestNegativeBeatsStillUsingBeatmapTiming()
2017-09-18 21:32:49 +08:00
{
2021-07-15 13:36:37 +08:00
int? lastBeatIndex = null;
double? lastBpm = null;
AddStep("Set time before zero", () =>
{
2022-06-24 20:25:23 +08:00
beatContainer.NewBeat = (i, timingControlPoint, _, _) =>
2021-07-15 13:36:37 +08:00
{
lastBeatIndex = i;
lastBpm = timingControlPoint.BPM;
};
2021-07-15 13:36:37 +08:00
gameplayClockContainer.Seek(-1000);
});
2021-07-15 13:36:37 +08:00
AddUntilStep("wait for trigger", () => lastBpm != null);
2021-07-15 13:59:57 +08:00
AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128));
2021-07-15 13:36:37 +08:00
AddAssert("beat index is less than zero", () => lastBeatIndex < 0);
2017-09-18 21:32:49 +08:00
}
2018-04-13 17:19:50 +08:00
2021-07-15 13:59:57 +08:00
[Test]
public void TestIdleBeatOnPausedClock()
{
double? lastBpm = null;
AddStep("bind event", () =>
{
2022-06-24 20:25:23 +08:00
beatContainer.NewBeat = (_, timingControlPoint, _, _) => lastBpm = timingControlPoint.BPM;
2021-07-15 13:59:57 +08:00
});
AddUntilStep("wait for trigger", () => lastBpm != null);
AddAssert("bpm is from beatmap", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 128));
AddStep("pause gameplay clock", () =>
{
lastBpm = null;
gameplayClockContainer.Stop();
});
AddUntilStep("wait for trigger", () => lastBpm != null);
2021-07-17 13:29:18 +08:00
AddAssert("bpm is default", () => lastBpm != null && Precision.AlmostEquals(lastBpm.Value, 60));
2021-07-15 13:59:57 +08:00
}
2021-12-05 17:53:36 +08:00
[TestCase(true)]
[TestCase(false)]
public void TestEarlyActivationEffectPoint(bool earlyActivating)
{
double earlyActivationMilliseconds = earlyActivating ? 100 : 0;
ControlPoint actualEffectPoint = null;
AddStep($"set early activation to {earlyActivationMilliseconds}", () => beatContainer.EarlyActivationMilliseconds = earlyActivationMilliseconds);
AddStep("seek before kiai effect point", () =>
{
ControlPoint expectedEffectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPoints.First(ep => ep.KiaiMode);
actualEffectPoint = null;
beatContainer.AllowMistimedEventFiring = false;
2022-06-24 20:25:23 +08:00
beatContainer.NewBeat = (_, _, effectControlPoint, _) =>
2021-12-05 17:53:36 +08:00
{
if (Precision.AlmostEquals(gameplayClockContainer.CurrentTime + earlyActivationMilliseconds, expectedEffectPoint.Time, BeatSyncedContainer.MISTIMED_ALLOWANCE))
actualEffectPoint = effectControlPoint;
};
gameplayClockContainer.Seek(expectedEffectPoint.Time - earlyActivationMilliseconds);
});
AddUntilStep("wait for effect point", () => actualEffectPoint != null);
AddAssert("effect has kiai", () => actualEffectPoint != null && ((EffectControlPoint)actualEffectPoint).KiaiMode);
}
private partial class TestBeatSyncedContainer : BeatSyncedContainer
2017-09-18 21:32:49 +08:00
{
private const int flash_layer_height = 150;
2018-04-13 17:19:50 +08:00
public new bool AllowMistimedEventFiring
{
get => base.AllowMistimedEventFiring;
set => base.AllowMistimedEventFiring = value;
}
2021-12-05 17:53:36 +08:00
public new double EarlyActivationMilliseconds
{
get => base.EarlyActivationMilliseconds;
set => base.EarlyActivationMilliseconds = value;
}
2017-09-18 21:32:49 +08:00
private readonly InfoString timingPointCount;
private readonly InfoString currentTimingPoint;
private readonly InfoString beatCount;
private readonly InfoString currentBeat;
private readonly InfoString beatsPerMinute;
private readonly InfoString adjustedBeatLength;
private readonly InfoString timeUntilNextBeat;
private readonly InfoString timeSinceLastBeat;
private readonly InfoString currentTime;
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
private readonly Box flashLayer;
2018-04-13 17:19:50 +08:00
public TestBeatSyncedContainer()
2017-09-18 21:32:49 +08:00
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Container
{
Name = @"Info Layer",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding { Bottom = flash_layer_height },
2017-09-18 21:32:49 +08:00
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(150),
},
new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
currentTime = new InfoString(@"Current time"),
2017-09-18 21:32:49 +08:00
timingPointCount = new InfoString(@"Timing points amount"),
currentTimingPoint = new InfoString(@"Current timing point"),
beatCount = new InfoString(@"Beats amount (in the current timing point)"),
currentBeat = new InfoString(@"Current beat"),
beatsPerMinute = new InfoString(@"BPM"),
adjustedBeatLength = new InfoString(@"Adjusted beat length"),
timeUntilNextBeat = new InfoString(@"Time until next beat"),
timeSinceLastBeat = new InfoString(@"Time since last beat"),
}
}
}
},
new Container
{
Name = @"Color indicator",
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = flash_layer_height,
2017-09-18 21:32:49 +08:00
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
flashLayer = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
Alpha = 0,
}
}
}
};
}
2018-04-13 17:19:50 +08:00
private List<TimingControlPoint> timingPoints => BeatSyncSource.ControlPoints?.TimingPoints.ToList();
2019-02-28 12:31:40 +08:00
2017-09-18 21:32:49 +08:00
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
{
2022-06-20 15:53:03 +08:00
if (ReferenceEquals(timingPoints[^1], current))
2017-09-18 21:32:49 +08:00
return current;
2018-04-13 17:19:50 +08:00
int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat"
return index == -1 ? current : timingPoints[index + 1];
2017-09-18 21:32:49 +08:00
}
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
private int calculateBeatCount(TimingControlPoint current)
{
if (timingPoints.Count == 0) return 0;
2018-04-13 17:19:50 +08:00
2022-06-20 15:53:03 +08:00
if (ReferenceEquals(timingPoints[^1], current))
{
Debug.Assert(BeatSyncSource.Clock != null);
return (int)Math.Ceiling((BeatSyncSource.Clock.CurrentTime - current.Time) / current.BeatLength);
}
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);
}
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
protected override void Update()
{
base.Update();
Debug.Assert(BeatSyncSource.Clock != null);
2017-09-18 21:32:49 +08:00
timeUntilNextBeat.Value = TimeUntilNextBeat;
timeSinceLastBeat.Value = TimeSinceLastBeat;
currentTime.Value = BeatSyncSource.Clock.CurrentTime;
2017-09-18 21:32:49 +08:00
}
2018-04-13 17:19:50 +08:00
public Action<int, TimingControlPoint, EffectControlPoint, ChannelAmplitudes> NewBeat;
2020-06-23 12:49:18 +08:00
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
2017-09-18 21:32:49 +08:00
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
timingPointCount.Value = timingPoints.Count;
currentTimingPoint.Value = timingPoints.IndexOf(timingPoint);
beatCount.Value = calculateBeatCount(timingPoint);
currentBeat.Value = beatIndex;
beatsPerMinute.Value = 60000 / timingPoint.BeatLength;
adjustedBeatLength.Value = timingPoint.BeatLength;
2018-04-13 17:19:50 +08:00
flashLayer.FadeOutFromOne(timingPoint.BeatLength / 4);
NewBeat?.Invoke(beatIndex, timingPoint, effectPoint, amplitudes);
2017-09-18 21:32:49 +08:00
}
}
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
private partial class InfoString : FillFlowContainer
{
private const int text_size = 20;
private const int margin = 7;
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
private readonly OsuSpriteText valueText;
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
public double Value
{
set => valueText.Text = $"{value:0.##}";
2017-09-18 21:32:49 +08:00
}
2018-04-13 17:19:50 +08:00
2017-09-18 21:32:49 +08:00
public InfoString(string header)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Add(new OsuSpriteText { Text = header + @": ", Font = OsuFont.GetFont(size: text_size) });
Add(valueText = new OsuSpriteText { Font = OsuFont.GetFont(size: text_size) });
2017-09-18 21:32:49 +08:00
Margin = new MarginPadding(margin);
}
}
}
}