diff --git a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
index 67dba71d45..be2a21d23d 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestCasePlayerLoader.cs
@@ -7,16 +7,13 @@ using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Screens;
-using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
-using osuTK;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestCasePlayerLoader : ManualInputManagerTestCase
{
private PlayerLoader loader;
-
private readonly OsuScreenStack stack;
public TestCasePlayerLoader()
@@ -29,8 +26,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Beatmap.Value = new DummyWorkingBeatmap(game);
- AddStep("Reset logo position", () => logo = new OsuLogo { Position = new Vector2(0, 0) });
-
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player
{
AllowPause = false,
@@ -58,6 +53,8 @@ namespace osu.Game.Tests.Visual.Gameplay
AllowLeadIn = false,
AllowResults = false,
}));
+
+ Scheduler.AddDelayed(() => slow.Ready = true, 5000);
});
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
diff --git a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs
index 5591bec0b8..0d4caff97e 100644
--- a/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseFacadeContainer.cs
@@ -11,7 +11,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Testing;
+using osu.Framework.Graphics.UserInterface;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Mods;
@@ -31,7 +31,11 @@ namespace osu.Game.Tests.Visual
typeof(PlayerLoader),
typeof(Player),
typeof(Facade),
- typeof(FacadeContainer)
+ typeof(FacadeContainer),
+ typeof(ButtonSystem),
+ typeof(ButtonSystemState),
+ typeof(Menu),
+ typeof(MainMenu)
};
[Cached]
@@ -68,15 +72,10 @@ namespace osu.Game.Tests.Visual
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
+ Ready = false
})));
}
- [Test]
- public void MainMenuTest()
- {
- AddStep("Add new Main Menu", () => LoadScreen(new MainMenu()));
- }
-
private class TestFacadeContainer : FacadeContainer
{
protected override Facade CreateFacade() => new Facade
@@ -119,16 +118,23 @@ namespace osu.Game.Tests.Visual
base.LogoArriving(logo, resuming);
logo.FadeIn(350);
logo.ScaleTo(new Vector2(0.15f), 350, Easing.In);
- facadeContainer.SetLogo(logo);
+ facadeContainer.SetLogo(logo, 0.3f, 1000, Easing.InOutQuint);
+ facadeContainer.Tracking = true;
moveLogoFacade();
}
+ protected override void LogoExiting(OsuLogo logo)
+ {
+ base.LogoExiting(logo);
+ facadeContainer.Tracking = false;
+ }
+
private void moveLogoFacade()
{
Random random = new Random();
if (facadeFlowComponent.Transforms.Count == 0)
{
- facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, 800), random.Next(0, 600)), 300);
+ facadeFlowComponent.Delay(500).MoveTo(new Vector2(random.Next(0, (int)DrawWidth), random.Next(0, (int)DrawHeight)), 300);
}
if (randomPositions)
@@ -159,11 +165,13 @@ namespace osu.Game.Tests.Visual
private class TestPlayer : Player
{
+ public bool Ready;
+
[BackgroundDependencyLoader]
private void load()
{
// Never finish loading
- while (true)
+ while (!Ready)
Thread.Sleep(1);
}
}
diff --git a/osu.Game/Graphics/Containers/FacadeContainer.cs b/osu.Game/Graphics/Containers/FacadeContainer.cs
index 611cc94958..47ba738f1c 100644
--- a/osu.Game/Graphics/Containers/FacadeContainer.cs
+++ b/osu.Game/Graphics/Containers/FacadeContainer.cs
@@ -10,80 +10,98 @@ using osuTK;
namespace osu.Game.Graphics.Containers
{
+ ///
+ /// A container that creates a to be used by its children.
+ /// This container also updates the position and size of the Facade, and contains logic for tracking an on the Facade's position.
+ ///
public class FacadeContainer : Container
{
+ protected virtual Facade CreateFacade() => new Facade();
+
+ public Facade Facade => facade;
+
+ ///
+ /// Whether or not the logo assigned to this FacadeContainer should be tracking the position its facade.
+ ///
+ public bool Tracking;
+
[Cached]
private Facade facade;
private OsuLogo logo;
+ private float facadeScale;
- private bool tracking;
-
- protected virtual Facade CreateFacade() => new Facade();
+ private Vector2 startPosition;
+ private Easing easing;
+ private double startTime;
+ private double duration;
public FacadeContainer()
{
facade = CreateFacade();
}
- private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre);
-
- public void SetLogo(OsuLogo logo, double transformDelay = 0)
+ ///
+ /// Set the logo that should track the Facade's position, as well as how it should transform to its initial position.
+ ///
+ /// The instance of the logo to be used for tracking.
+ /// The scale of the facade.
+ /// The duration of the initial transform. Default is instant.
+ /// The easing type of the initial transform.
+ public void SetLogo(OsuLogo logo, float facadeScale, double duration = 0, Easing easing = Easing.None)
{
if (logo != null)
{
- facade.Size = new Vector2(logo.SizeForFlow * 0.3f);
this.logo = logo;
- Scheduler.AddDelayed(() =>
- {
- tracking = true;
- }, transformDelay);
}
+
+ this.facadeScale = facadeScale;
+ this.duration = duration;
+ this.easing = easing;
}
- private double startTime;
- private double duration = 1000;
-
- private Vector2 startPosition;
- private Easing easing = Easing.InOutExpo;
+ private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(facade.ScreenSpaceDrawQuad.Centre);
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
- if (logo == null || !tracking)
+ if (logo == null || !Tracking)
return;
- facade.Size = new Vector2(logo.SizeForFlow * 0.3f);
+ facade.Size = new Vector2(logo.SizeForFlow * facadeScale);
if (facade.IsLoaded && logo.Position != logoTrackingPosition)
{
- if (logo.RelativePositionAxes != Axes.None)
- {
- logo.RelativePositionAxes = Axes.None;
- logo.Position = logo.Parent.ToLocalSpace(logo.Position);
- }
+ // Required for the correct position of the logo to be set with respect to logoTrackingPosition
+ logo.RelativePositionAxes = Axes.None;
+ // If this is our first update since tracking has started, initialize our starting values for interpolation
if (startTime == 0)
{
startTime = Time.Current;
+ startPosition = logo.Position;
}
var endTime = startTime + duration;
var remainingDuration = endTime - Time.Current;
- if (remainingDuration <= 0)
- {
- remainingDuration = 0;
- }
+ // If our transform should be instant, our position should already be at logoTrackingPosition, thus set the blend to 0.
+ // If we are already past when the transform should be finished playing, set the blend to 0 so that the logo is always at the position of the facade.
+ var blend = duration > 0 && remainingDuration > 0
+ ? (float)Interpolation.ApplyEasing(easing, remainingDuration / duration)
+ : 0;
- float currentTime = (float)Interpolation.ApplyEasing(easing, remainingDuration / duration);
- logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, currentTime);
+ // Interpolate the position of the logo, where blend 0 is the position of the Facade, and blend 1 is where the logo was when it first began interpolating.
+ logo.Position = Vector2.Lerp(logoTrackingPosition, startPosition, blend);
}
}
}
}
+///
+/// A placeholder container that serves as a dummy object to denote another object's location and size.
+///
public class Facade : Container
{
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs
index 3df4ef9059..4c0bcd399a 100644
--- a/osu.Game/Screens/Menu/ButtonSystem.cs
+++ b/osu.Game/Screens/Menu/ButtonSystem.cs
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
@@ -16,6 +17,7 @@ using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Graphics;
+using osu.Game.Graphics.Containers;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.Online.API;
@@ -53,15 +55,19 @@ namespace osu.Game.Screens.Menu
if (this.logo != null)
{
this.logo.Action = onOsuLogo;
+ facadeContainer.SetLogo(logo, 0.5f);
// osuLogo.SizeForFlow relies on loading to be complete.
buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
updateLogoState();
}
+ else
+ {
+ facadeContainer.Tracking = false;
+ }
}
- private readonly Drawable iconFacade;
private readonly ButtonArea buttonArea;
private readonly Button backButton;
@@ -71,26 +77,29 @@ namespace osu.Game.Screens.Menu
private SampleChannel sampleBack;
+ private readonly FacadeContainer facadeContainer;
+
public ButtonSystem()
{
RelativeSizeAxes = Axes.Both;
- Child = buttonArea = new ButtonArea();
+ Child = facadeContainer = new FacadeContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Child = buttonArea = new ButtonArea()
+ };
- buttonArea.AddRange(new[]
+ buttonArea.AddRange(new Container[]
{
new Button(@"settings", string.Empty, FontAwesome.fa_gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
backButton = new Button(@"back", @"button-back-select", FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH)
{
VisibleState = ButtonSystemState.Play,
},
- iconFacade = new Container //need a container to make the osu! icon flow properly.
- {
- Size = new Vector2(0, ButtonArea.BUTTON_AREA_HEIGHT)
- }
+ facadeContainer.Facade
});
- buttonArea.Flow.CentreTarget = iconFacade;
+ buttonArea.Flow.CentreTarget = facadeContainer.Facade;
}
[Resolved(CanBeNull = true)]
@@ -120,6 +129,15 @@ namespace osu.Game.Screens.Menu
buttonArea.AddRange(buttonsPlay);
buttonArea.AddRange(buttonsTopLevel);
+ buttonArea.ForEach(b =>
+ {
+ if (b is Button)
+ {
+ b.Origin = Anchor.CentreLeft;
+ b.Anchor = Anchor.CentreLeft;
+ }
+ });
+
isIdle.ValueChanged += idle => updateIdleState(idle.NewValue);
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
@@ -247,7 +265,7 @@ namespace osu.Game.Screens.Menu
logoDelayedAction?.Cancel();
logoDelayedAction = Scheduler.AddDelayed(() =>
{
- logoTracking = false;
+ facadeContainer.Tracking = false;
game?.Toolbar.Hide();
@@ -266,19 +284,16 @@ namespace osu.Game.Screens.Menu
break;
case ButtonSystemState.Initial:
logo.ClearTransforms(targetMember: nameof(Position));
- logo.RelativePositionAxes = Axes.None;
bool impact = logo.Scale.X > 0.6f;
if (lastState == ButtonSystemState.Initial)
logo.ScaleTo(0.5f, 200, Easing.In);
- logo.MoveTo(logoTrackingPosition, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In);
-
logoDelayedAction?.Cancel();
logoDelayedAction = Scheduler.AddDelayed(() =>
{
- logoTracking = true;
+ facadeContainer.Tracking = true;
if (impact)
logo.Impact();
@@ -288,35 +303,17 @@ namespace osu.Game.Screens.Menu
break;
default:
logo.ClearTransforms(targetMember: nameof(Position));
- logo.RelativePositionAxes = Axes.None;
- logoTracking = true;
+ facadeContainer.Tracking = true;
logo.ScaleTo(0.5f, 200, Easing.OutQuint);
break;
}
break;
case ButtonSystemState.EnteringMode:
- logoTracking = true;
+ facadeContainer.Tracking = true;
break;
}
}
-
- private Vector2 logoTrackingPosition => logo.Parent.ToLocalSpace(iconFacade.ScreenSpaceDrawQuad.Centre);
-
- private bool logoTracking;
-
- protected override void Update()
- {
- base.Update();
-
- if (logo != null)
- {
- if (logoTracking && logo.RelativePositionAxes == Axes.None && iconFacade.IsLoaded)
- logo.Position = logoTrackingPosition;
-
- iconFacade.Width = logo.SizeForFlow * 0.5f;
- }
- }
}
public enum ButtonSystemState
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index b7155f771f..f35eb6979d 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play
private Player player;
- private FacadeContainer content;
+ private FacadeContainer facadeContainer;
protected virtual FacadeContainer CreateFacadeContainer() => new FacadeContainer();
@@ -62,11 +62,11 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader]
private void load()
{
- InternalChild = content = CreateFacadeContainer();
- content.Anchor = Anchor.Centre;
- content.Origin = Anchor.Centre;
- content.RelativeSizeAxes = Axes.Both;
- content.Children = new Drawable[]
+ InternalChild = facadeContainer = CreateFacadeContainer();
+ facadeContainer.Anchor = Anchor.Centre;
+ facadeContainer.Origin = Anchor.Centre;
+ facadeContainer.RelativeSizeAxes = Axes.Both;
+ facadeContainer.Children = new Drawable[]
{
info = new BeatmapMetadataDisplay(Beatmap.Value)
{
@@ -122,21 +122,21 @@ namespace osu.Game.Screens.Play
private void contentIn()
{
- content.ScaleTo(1, 650, Easing.OutQuint);
- content.FadeInFromZero(400);
+ facadeContainer.ScaleTo(1, 650, Easing.OutQuint);
+ facadeContainer.FadeInFromZero(400);
}
private void contentOut()
{
- content.ScaleTo(0.7f, 300, Easing.InQuint);
- content.FadeOut(250);
+ facadeContainer.ScaleTo(0.7f, 300, Easing.InQuint);
+ facadeContainer.FadeOut(250);
}
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
- content.ScaleTo(0.7f);
+ facadeContainer.ScaleTo(0.7f);
Background?.FadeColour(Color4.White, 800, Easing.OutQuint);
contentIn();
@@ -155,7 +155,15 @@ namespace osu.Game.Screens.Play
logo.MoveTo(new Vector2(0.5f), duration, Easing.In);
logo.FadeIn(350);
- content.SetLogo(logo, duration);
+ facadeContainer.SetLogo(logo, 0.3f, 500, Easing.InOutQuint);
+
+ Scheduler.AddDelayed(() => facadeContainer.Tracking = true, duration);
+ }
+
+ protected override void LogoExiting(OsuLogo logo)
+ {
+ base.LogoExiting(logo);
+ facadeContainer.Tracking = false;
}
protected override void LoadComplete()
@@ -230,7 +238,7 @@ namespace osu.Game.Screens.Play
public override bool OnExiting(IScreen next)
{
- content.ScaleTo(0.7f, 150, Easing.InQuint);
+ facadeContainer.ScaleTo(0.7f, 150, Easing.InQuint);
this.FadeOut(150);
cancelLoad();
@@ -305,7 +313,6 @@ namespace osu.Game.Screens.Play
private LoadingAnimation loading;
private Sprite backgroundSprite;
private ModDisplay modDisplay;
- private FillFlowContainer fillFlowContainer;
public bool Loading
{
@@ -340,7 +347,7 @@ namespace osu.Game.Screens.Play
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
- fillFlowContainer = new FillFlowContainer
+ new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,