1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-05 10:33:22 +08:00

implement replay analysis into spectating

This commit is contained in:
Sheppsu 2024-09-06 03:58:02 -04:00
parent 9802b59459
commit 8749f9bb64
5 changed files with 78 additions and 24 deletions

View File

@ -19,6 +19,7 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
using osu.Game.Screens.Play;
using osuTK;
@ -34,18 +35,23 @@ namespace osu.Game.Rulesets.Osu.UI
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
[Resolved]
private MultiSpectatorScreen? multiSpectatorScreen { get; set; }
public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
: base(ruleset, beatmap, mods)
{
}
[BackgroundDependencyLoader]
private void load(ReplayPlayer? replayPlayer)
private void load(Player? player)
{
if (replayPlayer != null)
if (player is ReplayPlayer || player is SpectatorPlayer)
{
PlayfieldAdjustmentContainer.Add(new ReplayAnalysisOverlay(replayPlayer.Score.Replay));
replayPlayer.AddSettings(new ReplayAnalysisSettings(Config));
PlayfieldAdjustmentContainer.Add(new ReplayAnalysisOverlay(player.Score.Replay));
if (multiSpectatorScreen == null)
player.AddSettings(new ReplayAnalysisSettings(Config));
cursorHideEnabled = Config.GetBindable<bool>(OsuRulesetSetting.ReplayCursorHideEnabled);
@ -55,6 +61,14 @@ namespace osu.Game.Rulesets.Osu.UI
}
}
protected override void LoadComplete()
{
base.LoadComplete();
if (multiSpectatorScreen != null && !multiSpectatorScreen.SettingsAdded)
multiSpectatorScreen.AddSettingsAsync(new ReplayAnalysisSettings(Config));
}
public override DrawableHitObject<OsuHitObject>? CreateDrawableRepresentation(OsuHitObject h) => null;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor

View File

@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Osu.UI
private readonly Replay replay;
private int replayFrameIndex = 0;
public ReplayAnalysisOverlay(Replay replay)
{
RelativeSizeAxes = Axes.Both;
@ -52,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI
displayLength.BindValueChanged(_ =>
{
// Need to fully reload to make this work.
loaded.Invalidate();
invalidateLoaded();
}, true);
}
@ -60,6 +62,12 @@ namespace osu.Game.Rulesets.Osu.UI
private CancellationTokenSource? generationCancellationSource;
private void invalidateLoaded()
{
loaded.Invalidate();
replayFrameIndex = 0;
}
protected override void Update()
{
base.Update();
@ -75,7 +83,12 @@ namespace osu.Game.Rulesets.Osu.UI
private void initialise()
{
if (loaded.IsValid)
{
if (CursorPath != null)
addEntries();
return;
}
loaded.Validate();
@ -90,14 +103,21 @@ namespace osu.Game.Rulesets.Osu.UI
FrameMarkers = new FrameMarkerContainer(),
};
addEntries();
LoadComponentsAsync(newDrawables, drawables => InternalChildrenEnumerable = drawables, generationCancellationSource.Token);
}
private void addEntries()
{
bool leftHeld = false;
bool rightHeld = false;
// This should probably be async as well, but it's a bit of a pain to debounce and everything.
// Let's address concerns when they are raised.
foreach (var frame in replay.Frames)
while (replayFrameIndex < replay.Frames.Count)
{
var osuFrame = (OsuReplayFrame)frame;
var osuFrame = (OsuReplayFrame)replay.Frames[replayFrameIndex];
bool leftButton = osuFrame.Actions.Contains(OsuAction.LeftButton);
bool rightButton = osuFrame.Actions.Contains(OsuAction.RightButton);
@ -107,7 +127,7 @@ namespace osu.Game.Rulesets.Osu.UI
else if (!leftHeld && leftButton)
{
leftHeld = true;
ClickMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.LeftButton));
ClickMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.LeftButton));
}
if (rightHeld && !rightButton)
@ -115,14 +135,14 @@ namespace osu.Game.Rulesets.Osu.UI
else if (!rightHeld && rightButton)
{
rightHeld = true;
ClickMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.RightButton));
ClickMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, OsuAction.RightButton));
}
FrameMarkers.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, osuFrame.Actions.ToArray()));
CursorPath.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position));
}
FrameMarkers!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position, osuFrame.Actions.ToArray()));
CursorPath!.Add(new AnalysisFrameEntry(osuFrame.Time, displayLength.Value, osuFrame.Position));
LoadComponentsAsync(newDrawables, drawables => InternalChildrenEnumerable = drawables, generationCancellationSource.Token);
replayFrameIndex++;
}
}
}
}

View File

@ -15,6 +15,7 @@ using osu.Game.Online.Rooms;
using osu.Game.Online.Spectator;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Spectate;
using osu.Game.Users;
using osuTK;
@ -24,6 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
/// <summary>
/// A <see cref="SpectatorScreen"/> that spectates multiple users in a match.
/// </summary>
[Cached]
public partial class MultiSpectatorScreen : SpectatorScreen
{
// Isolates beatmap/ruleset to this screen.
@ -39,6 +41,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
/// </summary>
public bool AllPlayersLoaded => instances.All(p => p.PlayerLoaded);
public bool SettingsAdded { get; private set; } = false;
protected override UserActivity InitialActivity => new UserActivity.SpectatingMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
[Resolved]
@ -54,6 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
private SpectatorSyncManager syncManager = null!;
private PlayerGrid grid = null!;
private MultiSpectatorLeaderboard leaderboard = null!;
private FillFlowContainer leaderboardFlow = null!;
private PlayerArea? currentAudioSource;
private readonly Room room;
@ -73,10 +78,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
instances = new PlayerArea[Users.Count];
}
/// <summary>
/// Add a settings group to the bottom of the side container. Intended to be used by rulesets to add spectate-specific settings.
/// </summary>
/// <param name="settings">The settings group to be shown.</param>
public void AddSettingsAsync(PlayerSettingsGroup settings)
{
SettingsAdded = true;
LoadComponentAsync(settings, (loadedSettings) =>
{
loadedSettings.Expanded.Value = false;
leaderboardFlow.Insert(2, loadedSettings);
});
}
[BackgroundDependencyLoader]
private void load()
{
FillFlowContainer leaderboardFlow;
Container scoreDisplayContainer;
InternalChildren = new Drawable[]

View File

@ -36,6 +36,7 @@ using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Scoring;
using osu.Game.Scoring.Legacy;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osu.Game.Users;
@ -171,6 +172,16 @@ namespace osu.Game.Screens.Play
Configuration = configuration ?? new PlayerConfiguration();
}
/// <summary>
/// Add a settings group to the HUD overlay. Intended to be used by rulesets to add replay/spectate-specific settings.
/// </summary>
/// <param name="settings">The settings group to be shown.</param>
public void AddSettings(PlayerSettingsGroup settings) => Schedule(() =>
{
settings.Expanded.Value = false;
HUDOverlay.PlayerSettingsOverlay.Add(settings);
});
private ScreenSuspensionHandler screenSuspension;
private DependencyContainer dependencies;

View File

@ -55,16 +55,6 @@ namespace osu.Game.Screens.Play
this.createScore = createScore;
}
/// <summary>
/// Add a settings group to the HUD overlay. Intended to be used by rulesets to add replay-specific settings.
/// </summary>
/// <param name="settings">The settings group to be shown.</param>
public void AddSettings(PlayerSettingsGroup settings) => Schedule(() =>
{
settings.Expanded.Value = false;
HUDOverlay.PlayerSettingsOverlay.Add(settings);
});
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{