mirror of
https://github.com/ppy/osu.git
synced 2026-05-19 10:40:33 +08:00
Add simple multiplayer skip overlay
This commit is contained in:
@@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public Drawable OverlayContent => InternalChild;
|
||||
|
||||
public Drawable FadingContent => (OverlayContent as Container)?.Child;
|
||||
public new Drawable FadingContent => (OverlayContent as Container)?.Child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public partial class TestSceneMultiplayerSkipOverlay : MultiplayerTestScene
|
||||
{
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("join room", () => JoinRoom(CreateDefaultRoom()));
|
||||
WaitForJoined();
|
||||
|
||||
AddStep("add skip overlay", () =>
|
||||
{
|
||||
GameplayClockContainer gameplayClockContainer;
|
||||
|
||||
var working = CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
|
||||
|
||||
Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new MultiplayerSkipOverlay(120000)
|
||||
},
|
||||
};
|
||||
|
||||
gameplayClockContainer.Start();
|
||||
});
|
||||
|
||||
AddStep("set playing state", () => MultiplayerClient.ChangeUserState(API.LocalUser.Value.OnlineID, MultiplayerUserState.Playing));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSkip()
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int i2 = i;
|
||||
|
||||
AddStep($"join user {i2}", () =>
|
||||
{
|
||||
MultiplayerClient.AddUser(new APIUser
|
||||
{
|
||||
Id = i2,
|
||||
Username = $"User {i2}"
|
||||
});
|
||||
|
||||
MultiplayerClient.ChangeUserState(i2, MultiplayerUserState.Playing);
|
||||
});
|
||||
}
|
||||
|
||||
AddStep("local user votes", () => MultiplayerClient.VoteToSkip().WaitSafely());
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int i2 = i;
|
||||
AddStep($"user {i2} votes", () => MultiplayerClient.UserVoteToSkip(i2).WaitSafely());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
Debug.Assert(client.Room != null);
|
||||
}
|
||||
|
||||
protected override SkipOverlay CreateSkipOverlay(double startTime) => new MultiplayerSkipOverlay(startTime);
|
||||
|
||||
protected override void StartGameplay()
|
||||
{
|
||||
// We can enter this screen one of two ways:
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
public partial class MultiplayerSkipOverlay : SkipOverlay
|
||||
{
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; } = null!;
|
||||
|
||||
private Drawable votedIcon = null!;
|
||||
private OsuSpriteText countText = null!;
|
||||
|
||||
public MultiplayerSkipOverlay(double startTime)
|
||||
: base(startTime)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
FadingContent.AddRange(
|
||||
[
|
||||
votedIcon = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Position = new Vector2(50, 0),
|
||||
Size = new Vector2(20),
|
||||
Alpha = 0,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Green
|
||||
},
|
||||
new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.5f),
|
||||
Icon = FontAwesome.Solid.Check
|
||||
}
|
||||
}
|
||||
},
|
||||
countText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
Position = new Vector2(0.75f, 0),
|
||||
Font = OsuFont.Default.With(size: 36, weight: FontWeight.Bold)
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
client.UserStateChanged += onUserStateChanged;
|
||||
client.UserVotedToSkip += onUserVotedToSkip;
|
||||
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void onUserStateChanged(MultiplayerRoomUser user, MultiplayerUserState state)
|
||||
{
|
||||
Schedule(updateText);
|
||||
}
|
||||
|
||||
private void onUserVotedToSkip(int userId) => Schedule(() =>
|
||||
{
|
||||
updateText();
|
||||
|
||||
countText.ScaleTo(1.5f).ScaleTo(1, 200, Easing.OutSine);
|
||||
|
||||
if (userId == client.LocalUser?.UserID)
|
||||
{
|
||||
votedIcon.ScaleTo(1.5f).ScaleTo(1, 200, Easing.OutSine);
|
||||
votedIcon.FadeInFromZero(100);
|
||||
}
|
||||
});
|
||||
|
||||
private void updateText()
|
||||
{
|
||||
if (client.Room == null)
|
||||
return;
|
||||
|
||||
int countTotal = client.Room.Users.Count(u => u.State == MultiplayerUserState.Playing);
|
||||
int countSkipped = client.Room.Users.Count(u => u.State == MultiplayerUserState.Playing && u.VotedToSkip);
|
||||
int countRequired = countTotal / 2 + 1;
|
||||
|
||||
countText.Text = $"{Math.Min(countRequired, countSkipped)} / {countRequired}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private BreakTracker breakTracker;
|
||||
|
||||
private SkipOverlay skipIntroOverlay;
|
||||
protected SkipOverlay SkipIntroOverlay { get; private set; }
|
||||
private SkipOverlay skipOutroOverlay;
|
||||
|
||||
protected ScoreProcessor ScoreProcessor { get; private set; }
|
||||
@@ -500,10 +500,10 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
// display the cursor above some HUD elements.
|
||||
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
|
||||
skipIntroOverlay = new SkipOverlay(DrawableRuleset.GameplayStartTime)
|
||||
SkipIntroOverlay = CreateSkipOverlay(DrawableRuleset.GameplayStartTime).With(o =>
|
||||
{
|
||||
RequestSkip = RequestIntroSkip
|
||||
},
|
||||
o.RequestSkip = RequestIntroSkip;
|
||||
}),
|
||||
skipOutroOverlay = new SkipOverlay(GameplayState.Storyboard.LatestEventTime ?? 0)
|
||||
{
|
||||
RequestSkip = () => progressToResults(false),
|
||||
@@ -522,13 +522,15 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (!Configuration.AllowSkipping || !DrawableRuleset.AllowGameplayOverlays)
|
||||
{
|
||||
skipIntroOverlay.Expire();
|
||||
SkipIntroOverlay.Expire();
|
||||
skipOutroOverlay.Expire();
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
protected virtual SkipOverlay CreateSkipOverlay(double startTime) => new SkipOverlay(startTime);
|
||||
|
||||
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
|
||||
{
|
||||
updateGameplayState();
|
||||
@@ -1158,7 +1160,7 @@ namespace osu.Game.Screens.Play
|
||||
GameplayClockContainer.Reset(startClock: true);
|
||||
|
||||
if (Configuration.AutomaticallySkipIntro)
|
||||
skipIntroOverlay.SkipWhenReady();
|
||||
SkipIntroOverlay.SkipWhenReady();
|
||||
}
|
||||
|
||||
public override void OnSuspending(ScreenTransitionEvent e)
|
||||
|
||||
@@ -38,20 +38,21 @@ namespace osu.Game.Screens.Play
|
||||
private readonly double startTime;
|
||||
|
||||
public Action RequestSkip;
|
||||
|
||||
protected FadeContainer FadingContent { get; private set; }
|
||||
|
||||
private Button button;
|
||||
private ButtonContainer buttonContainer;
|
||||
private Circle remainingTimeBox;
|
||||
|
||||
private FadeContainer fadeContainer;
|
||||
private double displayTime;
|
||||
|
||||
private bool isClickable;
|
||||
private bool skipQueued;
|
||||
|
||||
[Resolved]
|
||||
private IGameplayClock gameplayClock { get; set; }
|
||||
|
||||
internal bool IsButtonVisible => fadeContainer.State == Visibility.Visible && buttonContainer.State.Value == Visibility.Visible;
|
||||
internal bool IsButtonVisible => FadingContent.State == Visibility.Visible && buttonContainer.State.Value == Visibility.Visible;
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
/// <summary>
|
||||
@@ -77,7 +78,7 @@ namespace osu.Game.Screens.Play
|
||||
InternalChild = buttonContainer = new ButtonContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = fadeContainer = new FadeContainer
|
||||
Child = FadingContent = new FadeContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
@@ -107,13 +108,13 @@ namespace osu.Game.Screens.Play
|
||||
public override void Hide()
|
||||
{
|
||||
base.Hide();
|
||||
fadeContainer.Hide();
|
||||
FadingContent.Hide();
|
||||
}
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
base.Show();
|
||||
fadeContainer.TriggerShow();
|
||||
FadingContent.TriggerShow();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -136,7 +137,7 @@ namespace osu.Game.Screens.Play
|
||||
RequestSkip?.Invoke();
|
||||
};
|
||||
|
||||
fadeContainer.TriggerShow();
|
||||
FadingContent.TriggerShow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -183,7 +184,7 @@ namespace osu.Game.Screens.Play
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (isClickable && !e.HasAnyButtonPressed)
|
||||
fadeContainer.TriggerShow();
|
||||
FadingContent.TriggerShow();
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
@@ -563,7 +563,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
public override Task VoteToSkip()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
return UserVoteToSkip(api.LocalUser.Value.OnlineID);
|
||||
}
|
||||
|
||||
public async Task UserVoteToSkip(int userId)
|
||||
{
|
||||
await ((IMultiplayerClient)this).UserVotedToSkip(userId);
|
||||
}
|
||||
|
||||
protected override Task<MultiplayerRoom> CreateRoomInternal(MultiplayerRoom room)
|
||||
|
||||
Reference in New Issue
Block a user