mirror of
https://github.com/osukey/osukey.git
synced 2025-08-07 00:23:59 +09:00
Merge remote-tracking branch 'upstream/master' into update-scrollcontainer
This commit is contained in:
109
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
Normal file
109
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneGameplayRewinding : PlayerTestScene
|
||||||
|
{
|
||||||
|
private RulesetExposingPlayer player => (RulesetExposingPlayer)Player;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private AudioManager audioManager { get; set; }
|
||||||
|
|
||||||
|
public TestSceneGameplayRewinding()
|
||||||
|
: base(new OsuRuleset())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track track;
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var working = new ClockBackedTestWorkingBeatmap(beatmap, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||||
|
track = working.Track;
|
||||||
|
return working;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoJudgementsOnRewind()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||||
|
addSeekStep(3000);
|
||||||
|
AddAssert("all judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
||||||
|
AddStep("clear results", () => player.AppliedResults.Clear());
|
||||||
|
addSeekStep(0);
|
||||||
|
AddAssert("none judged", () => player.DrawableRuleset.Playfield.AllHitObjects.All(h => !h.Judged));
|
||||||
|
AddAssert("no results triggered", () => player.AppliedResults.Count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSeekStep(double time)
|
||||||
|
{
|
||||||
|
AddStep($"seek to {time}", () => track.Seek(time));
|
||||||
|
|
||||||
|
// Allow a few frames of lenience
|
||||||
|
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
|
{
|
||||||
|
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
||||||
|
return new RulesetExposingPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = { BaseDifficulty = { ApproachRate = 9 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
beatmap.HitObjects.Add(new HitCircle
|
||||||
|
{
|
||||||
|
Position = new Vector2(256, 192),
|
||||||
|
StartTime = 1000 + 30 * i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return beatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RulesetExposingPlayer : Player
|
||||||
|
{
|
||||||
|
public readonly List<JudgementResult> AppliedResults = new List<JudgementResult>();
|
||||||
|
|
||||||
|
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||||
|
|
||||||
|
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
|
||||||
|
|
||||||
|
public RulesetExposingPlayer()
|
||||||
|
: base(false, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
ScoreProcessor.NewJudgement += r => AppliedResults.Add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Taiko;
|
using osu.Game.Rulesets.Taiko;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
@ -100,8 +101,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public virtual void SetUp() =>
|
public virtual void SetUp() => Schedule(() =>
|
||||||
Schedule(() => { manager?.Delete(manager.GetAllUsableBeatmapSets()); });
|
{
|
||||||
|
Ruleset.Value = new OsuRuleset().RulesetInfo;
|
||||||
|
manager?.Delete(manager.GetAllUsableBeatmapSets());
|
||||||
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDummy()
|
public void TestDummy()
|
||||||
@ -185,7 +189,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("empty mods", () => !Mods.Value.Any());
|
AddAssert("empty mods", () => !Mods.Value.Any());
|
||||||
|
|
||||||
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
|
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
|
||||||
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--;
|
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -26,8 +26,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
new NamedIconButton("No change", new IconButton()),
|
new NamedIconButton("No change", new IconButton()),
|
||||||
new NamedIconButton("Background colours", new ColouredIconButton()),
|
new NamedIconButton("Background colours", new ColouredIconButton()),
|
||||||
new NamedIconButton("Full-width", new IconButton { ButtonSize = new Vector2(200, 30) }),
|
new NamedIconButton("Full-width", new IconButton { Size = new Vector2(200, 30) }),
|
||||||
new NamedIconButton("Unchanging size", new IconButton(), false),
|
|
||||||
new NamedIconButton("Icon colours", new IconButton
|
new NamedIconButton("Icon colours", new IconButton
|
||||||
{
|
{
|
||||||
IconColour = Color4.Green,
|
IconColour = Color4.Green,
|
||||||
@ -48,7 +47,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private class NamedIconButton : Container
|
private class NamedIconButton : Container
|
||||||
{
|
{
|
||||||
public NamedIconButton(string name, IconButton button, bool allowSizeChange = true)
|
public NamedIconButton(string name, IconButton button)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Width = 200;
|
Width = 200;
|
||||||
@ -101,13 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (allowSizeChange)
|
iconContainer.AutoSizeAxes = Axes.Both;
|
||||||
iconContainer.AutoSizeAxes = Axes.Both;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
iconContainer.RelativeSizeAxes = Axes.X;
|
|
||||||
iconContainer.Height = 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.Anchor = Anchor.Centre;
|
button.Anchor = Anchor.Centre;
|
||||||
button.Origin = Anchor.Centre;
|
button.Origin = Anchor.Centre;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
|
||||||
@ -23,9 +22,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
};
|
};
|
||||||
Add(mc);
|
Add(mc);
|
||||||
|
|
||||||
AddToggleStep(@"toggle visibility", state => mc.State.Value = state ? Visibility.Visible : Visibility.Hidden);
|
|
||||||
AddStep(@"show", () => mc.Show());
|
AddStep(@"show", () => mc.Show());
|
||||||
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
|
AddToggleStep(@"toggle beatmap lock", state => Beatmap.Disabled = state);
|
||||||
|
AddStep(@"show", () => mc.Hide());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
public class IconButton : OsuAnimatedButton
|
public class IconButton : OsuAnimatedButton
|
||||||
{
|
{
|
||||||
public const float BUTTON_SIZE = 30;
|
public const float DEFAULT_BUTTON_SIZE = 30;
|
||||||
|
|
||||||
private Color4? iconColour;
|
private Color4? iconColour;
|
||||||
|
|
||||||
@ -57,26 +57,11 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
set => icon.Scale = value;
|
set => icon.Scale = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The size of the <see cref="IconButton"/> while it is not being pressed.
|
|
||||||
/// </summary>
|
|
||||||
public Vector2 ButtonSize
|
|
||||||
{
|
|
||||||
get => Content.Size;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Content.RelativeSizeAxes = Axes.None;
|
|
||||||
Content.AutoSizeAxes = Axes.None;
|
|
||||||
Content.Size = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
|
|
||||||
public IconButton()
|
public IconButton()
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
Size = new Vector2(DEFAULT_BUTTON_SIZE);
|
||||||
ButtonSize = new Vector2(BUTTON_SIZE);
|
|
||||||
|
|
||||||
Add(icon = new SpriteIcon
|
Add(icon = new SpriteIcon
|
||||||
{
|
{
|
||||||
|
@ -464,12 +464,26 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private class MusicIconButton : IconButton
|
private class MusicIconButton : IconButton
|
||||||
{
|
{
|
||||||
|
public MusicIconButton()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
HoverColour = colours.YellowDark.Opacity(0.6f);
|
HoverColour = colours.YellowDark.Opacity(0.6f);
|
||||||
FlashColour = colours.Yellow;
|
FlashColour = colours.Yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// works with AutoSizeAxes above to make buttons autosize with the scale animation.
|
||||||
|
Content.AutoSizeAxes = Axes.None;
|
||||||
|
Content.Size = new Vector2(DEFAULT_BUTTON_SIZE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Background : BufferedContainer
|
private class Background : BufferedContainer
|
||||||
|
@ -244,6 +244,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// <returns>Whether a scoring result has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>.</returns>
|
/// <returns>Whether a scoring result has occurred from this <see cref="DrawableHitObject"/> or any nested <see cref="DrawableHitObject"/>.</returns>
|
||||||
protected bool UpdateResult(bool userTriggered)
|
protected bool UpdateResult(bool userTriggered)
|
||||||
{
|
{
|
||||||
|
// It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed
|
||||||
|
if (Time.Elapsed < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
judgementOccurred = false;
|
judgementOccurred = false;
|
||||||
|
|
||||||
if (AllJudged)
|
if (AllJudged)
|
||||||
|
@ -171,7 +171,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
// Small offset to look a bit better centered along with the divisor text
|
// Small offset to look a bit better centered along with the divisor text
|
||||||
Y = 1;
|
Y = 1;
|
||||||
|
|
||||||
ButtonSize = new Vector2(20);
|
Size = new Vector2(20);
|
||||||
IconScale = new Vector2(0.6f);
|
IconScale = new Vector2(0.6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,14 +31,14 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
|||||||
InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() };
|
InternalChild = button = new TimelineIconButton { Action = () => Action?.Invoke() };
|
||||||
|
|
||||||
button.Enabled.BindTo(Enabled);
|
button.Enabled.BindTo(Enabled);
|
||||||
Width = button.ButtonSize.X;
|
Width = button.Width;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
button.ButtonSize = new Vector2(button.ButtonSize.X, DrawHeight);
|
button.Size = new Vector2(button.Width, DrawHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TimelineIconButton : IconButton
|
private class TimelineIconButton : IconButton
|
||||||
|
Reference in New Issue
Block a user