1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 21:27:24 +08:00

Move analysis container implementation completely local to osu! ruleset

This commit is contained in:
Dean Herbert 2024-09-04 18:35:27 +09:00
parent 0e16508fd6
commit a417fec234
No known key found for this signature in database
10 changed files with 93 additions and 180 deletions

View File

@ -1,13 +1,12 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Replays;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Replays;
@ -21,51 +20,80 @@ namespace osu.Game.Rulesets.Osu.Tests
{
public partial class TestSceneOsuAnalysisContainer : OsuTestScene
{
private TestOsuAnalysisContainer analysisContainer;
private TestOsuAnalysisContainer analysisContainer = null!;
[BackgroundDependencyLoader]
private void load()
[SetUpSteps]
public void SetUpSteps()
{
Child = analysisContainer = createAnalysisContainer();
AddStep("create analysis container", () =>
{
DrawableOsuRuleset drawableRuleset = new DrawableOsuRuleset(new OsuRuleset(), new OsuBeatmap());
Children = new Drawable[]
{
drawableRuleset,
analysisContainer = new TestOsuAnalysisContainer(fabricateReplay(), drawableRuleset),
};
});
}
[Test]
public void TestHitMarkers()
{
AddStep("enable hit markers", () => analysisContainer.AnalysisSettings.HitMarkersEnabled.Value = true);
AddStep("enable hit markers", () => analysisContainer.Settings.HitMarkersEnabled.Value = true);
AddAssert("hit markers visible", () => analysisContainer.HitMarkersVisible);
AddStep("disable hit markers", () => analysisContainer.AnalysisSettings.HitMarkersEnabled.Value = false);
AddStep("disable hit markers", () => analysisContainer.Settings.HitMarkersEnabled.Value = false);
AddAssert("hit markers not visible", () => !analysisContainer.HitMarkersVisible);
}
[Test]
public void TestAimMarker()
{
AddStep("enable aim markers", () => analysisContainer.AnalysisSettings.AimMarkersEnabled.Value = true);
AddStep("enable aim markers", () => analysisContainer.Settings.AimMarkersEnabled.Value = true);
AddAssert("aim markers visible", () => analysisContainer.AimMarkersVisible);
AddStep("disable aim markers", () => analysisContainer.AnalysisSettings.AimMarkersEnabled.Value = false);
AddStep("disable aim markers", () => analysisContainer.Settings.AimMarkersEnabled.Value = false);
AddAssert("aim markers not visible", () => !analysisContainer.AimMarkersVisible);
}
[Test]
public void TestAimLines()
{
AddStep("enable aim lines", () => analysisContainer.AnalysisSettings.AimLinesEnabled.Value = true);
AddStep("enable aim lines", () => analysisContainer.Settings.AimLinesEnabled.Value = true);
AddAssert("aim lines visible", () => analysisContainer.AimLinesVisible);
AddStep("disable aim lines", () => analysisContainer.AnalysisSettings.AimLinesEnabled.Value = false);
AddStep("disable aim lines", () => analysisContainer.Settings.AimLinesEnabled.Value = false);
AddAssert("aim lines not visible", () => !analysisContainer.AimLinesVisible);
}
private TestOsuAnalysisContainer createAnalysisContainer()
private Replay fabricateReplay()
{
var replay = new Replay();
var ruleset = new OsuRuleset();
var beatmap = new OsuBeatmap();
var drawableRuleset = new DrawableOsuRuleset(ruleset, beatmap);
// Load playfield cursor to avoid errors
Add(drawableRuleset);
var frames = new List<ReplayFrame>();
var random = new Random();
int posX = 250;
int posY = 250;
bool leftOrRight = false;
return new TestOsuAnalysisContainer(replay, drawableRuleset);
for (int i = 0; i < 1000; i++)
{
posX = Math.Clamp(posX + random.Next(-10, 11), 0, 500);
posY = Math.Clamp(posY + random.Next(-10, 11), 0, 500);
var actions = new List<OsuAction>();
if (i % 20 == 0)
{
actions.Add(leftOrRight ? OsuAction.LeftButton : OsuAction.RightButton);
leftOrRight = !leftOrRight;
}
frames.Add(new OsuReplayFrame
{
Time = Time.Current + i * 15,
Position = new Vector2(posX, posY),
Actions = actions
});
}
return new Replay { Frames = frames };
}
private partial class TestOsuAnalysisContainer : OsuAnalysisContainer
@ -75,68 +103,9 @@ namespace osu.Game.Rulesets.Osu.Tests
{
}
[BackgroundDependencyLoader]
private void load()
{
Replay = fabricateReplay();
LoadReplay();
makeReplayLoop();
}
private void makeReplayLoop()
{
Scheduler.AddDelayed(() =>
{
Replay = fabricateReplay();
HitMarkers.Clear();
AimMarkers.Clear();
AimLines.Clear();
LoadReplay();
makeReplayLoop();
}, 15000);
}
public bool HitMarkersVisible => HitMarkers.Alpha > 0 && HitMarkers.Entries.Any();
public bool AimMarkersVisible => AimMarkers.Alpha > 0 && AimMarkers.Entries.Any();
public bool AimLinesVisible => AimLines.Alpha > 0 && AimLines.Vertices.Count > 1;
private Replay fabricateReplay()
{
var frames = new List<ReplayFrame>();
var random = new Random();
int posX = 250;
int posY = 250;
bool leftOrRight = false;
for (int i = 0; i < 1000; i++)
{
posX = Math.Clamp(posX + random.Next(-10, 11), 0, 500);
posY = Math.Clamp(posY + random.Next(-10, 11), 0, 500);
var actions = new List<OsuAction>();
if (i % 20 == 0)
{
actions.Add(leftOrRight ? OsuAction.LeftButton : OsuAction.RightButton);
leftOrRight = !leftOrRight;
}
frames.Add(new OsuReplayFrame
{
Time = Time.Current + i * 15,
Position = new Vector2(posX, posY),
Actions = actions
});
}
return new Replay { Frames = frames };
}
}
}
}

View File

@ -36,6 +36,14 @@ namespace osu.Game.Rulesets.Osu.UI
{
}
protected override void LoadComplete()
{
if (HasReplayLoaded.Value)
LoadComponentAsync(new OsuAnalysisContainer(ReplayScore.Replay, this), PlayfieldAdjustmentContainer.Add);
base.LoadComplete();
}
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h) => null;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
@ -68,7 +76,5 @@ namespace osu.Game.Rulesets.Osu.UI
return 0;
}
}
public override AnalysisContainer CreateAnalysisContainer(Replay replay) => new OsuAnalysisContainer(replay, this);
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Performance;
using osu.Framework.Graphics.Pooling;
@ -18,62 +19,62 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.UI
{
public partial class OsuAnalysisContainer : AnalysisContainer
public partial class OsuAnalysisContainer : CompositeDrawable
{
public new OsuAnalysisSettings AnalysisSettings => (OsuAnalysisSettings)base.AnalysisSettings;
protected readonly HitMarkersContainer HitMarkers;
protected readonly AimMarkersContainer AimMarkers;
protected readonly AimLinesContainer AimLines;
protected new DrawableOsuRuleset DrawableRuleset => (DrawableOsuRuleset)base.DrawableRuleset;
public OsuAnalysisSettings Settings = null!;
protected HitMarkersContainer HitMarkers;
protected AimMarkersContainer AimMarkers;
protected AimLinesContainer AimLines;
private readonly Replay replay;
private readonly DrawableRuleset drawableRuleset;
public OsuAnalysisContainer(Replay replay, DrawableRuleset drawableRuleset)
: base(replay, drawableRuleset)
{
this.replay = replay;
this.drawableRuleset = drawableRuleset;
InternalChildren = new Drawable[]
{
AimLines = new AimLinesContainer { Depth = float.MaxValue },
HitMarkers = new HitMarkersContainer(),
AimMarkers = new AimMarkersContainer { Depth = float.MinValue }
AimLines = new AimLinesContainer(),
AimMarkers = new AimMarkersContainer(),
};
}
protected override OsuAnalysisSettings CreateAnalysisSettings(Ruleset ruleset)
{
var settings = new OsuAnalysisSettings((OsuRuleset)ruleset);
settings.HitMarkersEnabled.ValueChanged += e => toggleHitMarkers(e.NewValue);
settings.AimMarkersEnabled.ValueChanged += e => toggleAimMarkers(e.NewValue);
settings.AimLinesEnabled.ValueChanged += e => toggleAimLines(e.NewValue);
settings.CursorHideEnabled.ValueChanged += e => toggleCursorHidden(e.NewValue);
return settings;
}
[BackgroundDependencyLoader]
private void load()
{
toggleHitMarkers(AnalysisSettings.HitMarkersEnabled.Value);
toggleAimMarkers(AnalysisSettings.AimMarkersEnabled.Value);
toggleAimLines(AnalysisSettings.AimLinesEnabled.Value);
toggleCursorHidden(AnalysisSettings.CursorHideEnabled.Value);
AddInternal(Settings = new OsuAnalysisSettings());
LoadReplay();
}
protected override void LoadComplete()
{
base.LoadComplete();
Settings.HitMarkersEnabled.BindValueChanged(e => toggleHitMarkers(e.NewValue), true);
Settings.AimMarkersEnabled.BindValueChanged(e => toggleAimMarkers(e.NewValue), true);
Settings.AimLinesEnabled.BindValueChanged(e => toggleAimLines(e.NewValue), true);
Settings.CursorHideEnabled.BindValueChanged(e => toggleCursorHidden(e.NewValue), true);
}
private void toggleHitMarkers(bool value) => HitMarkers.FadeTo(value ? 1 : 0);
private void toggleAimMarkers(bool value) => AimMarkers.FadeTo(value ? 1 : 0);
private void toggleAimLines(bool value) => AimLines.FadeTo(value ? 1 : 0);
private void toggleCursorHidden(bool value) => DrawableRuleset.Playfield.Cursor.FadeTo(value ? 0 : 1);
private void toggleCursorHidden(bool value) => drawableRuleset.Playfield.Cursor.FadeTo(value ? 0 : 1);
protected void LoadReplay()
{
bool leftHeld = false;
bool rightHeld = false;
foreach (var frame in Replay.Frames)
foreach (var frame in replay.Frames)
{
var osuFrame = (OsuReplayFrame)frame;

View File

@ -9,13 +9,8 @@ using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Rulesets.Osu.UI
{
public partial class OsuAnalysisSettings : AnalysisSettings
public partial class OsuAnalysisSettings : PlayerSettingsGroup
{
public OsuAnalysisSettings(Ruleset ruleset)
: base(ruleset)
{
}
[SettingSource("Hit markers", SettingControlType = typeof(PlayerCheckbox))]
public BindableBool HitMarkersEnabled { get; } = new BindableBool();
@ -28,10 +23,18 @@ namespace osu.Game.Rulesets.Osu.UI
[SettingSource("Hide cursor", SettingControlType = typeof(PlayerCheckbox))]
public BindableBool CursorHideEnabled { get; } = new BindableBool();
public OsuAnalysisSettings()
: base("Analysis Settings")
{
}
[BackgroundDependencyLoader]
private void load(IRulesetConfigCache cache)
{
var config = (OsuRulesetConfigManager)cache.GetConfigFor(Ruleset)!;
AddRange(this.CreateSettingsControls());
var config = (OsuRulesetConfigManager)cache.GetConfigFor(new OsuRuleset())!;
config.BindWith(OsuRulesetSetting.ReplayHitMarkersEnabled, HitMarkersEnabled);
config.BindWith(OsuRulesetSetting.ReplayAimMarkersEnabled, AimMarkersEnabled);
config.BindWith(OsuRulesetSetting.ReplayAimLinesEnabled, AimLinesEnabled);

View File

@ -406,7 +406,5 @@ namespace osu.Game.Rulesets
/// Can be overridden to avoid showing scroll speed changes in the editor.
/// </summary>
public virtual bool EditorShowScrollSpeed => true;
public virtual DifficultySection? CreateEditorDifficultySection() => null;
}
}

View File

@ -1,27 +0,0 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Game.Replays;
using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Rulesets.UI
{
public abstract partial class AnalysisContainer : Container
{
protected Replay Replay;
protected DrawableRuleset DrawableRuleset;
public AnalysisSettings AnalysisSettings;
protected AnalysisContainer(Replay replay, DrawableRuleset drawableRuleset)
{
Replay = replay;
DrawableRuleset = drawableRuleset;
AnalysisSettings = CreateAnalysisSettings(drawableRuleset.Ruleset);
}
protected abstract AnalysisSettings CreateAnalysisSettings(Ruleset ruleset);
}
}

View File

@ -596,8 +596,6 @@ namespace osu.Game.Rulesets.UI
/// Invoked when the user requests to pause while the resume overlay is active.
/// </summary>
public abstract void CancelResume();
public virtual AnalysisContainer CreateAnalysisContainer(Replay replay) => null;
}
public class BeatmapInvalidForRulesetException : ArgumentException

View File

@ -291,12 +291,6 @@ namespace osu.Game.Rulesets.UI
/// </summary>
protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
/// <summary>
/// Adds an analysis container to internal children for replays.
/// </summary>
/// <param name="analysisContainer"></param>
public virtual void AddAnalysisContainer(AnalysisContainer analysisContainer) => AddInternal(analysisContainer);
#region Pooling support
private readonly Dictionary<Type, IDrawablePool> pools = new Dictionary<Type, IDrawablePool>();

View File

@ -1,21 +0,0 @@
// 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 osu.Game.Configuration;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Play.PlayerSettings
{
public partial class AnalysisSettings : PlayerSettingsGroup
{
protected Ruleset Ruleset;
public AnalysisSettings(Ruleset ruleset)
: base("Analysis Settings")
{
Ruleset = ruleset;
AddRange(this.CreateSettingsControls());
}
}
}

View File

@ -71,14 +71,6 @@ namespace osu.Game.Screens.Play
playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate);
HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings);
var analysisContainer = DrawableRuleset.CreateAnalysisContainer(GameplayState.Score.Replay);
if (analysisContainer != null)
{
HUDOverlay.PlayerSettingsOverlay.AddAtStart(analysisContainer.AnalysisSettings);
DrawableRuleset.Playfield.AddAnalysisContainer(analysisContainer);
}
}
protected override void PrepareReplay()