From 7fd7dc153846d4d8eb549d65f544ee84c35ab473 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:31 +0900 Subject: [PATCH 1/5] Extract playfield scaling into a separate class And make it more general. --- osu.Game/Rulesets/UI/Playfield.cs | 52 +++-------- osu.Game/Rulesets/UI/ScalableContainer.cs | 86 +++++++++++++++++++ .../UI/Scrolling/ScrollingPlayfield.cs | 11 ++- osu.Game/osu.Game.csproj | 1 + 4 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Rulesets/UI/ScalableContainer.cs diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index a7fed7059b..bbf20c2c26 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -3,52 +3,37 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; using osu.Framework.Allocation; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : Container + public abstract class Playfield : ScalableContainer { /// /// The HitObjects contained in this Playfield. /// public HitObjectContainer HitObjects { get; private set; } - public Container ScaledContent; - - protected override Container Content => content; - private readonly Container content; - - private List nestedPlayfields; - /// /// All the s nested inside this playfield. /// public IReadOnlyList NestedPlayfields => nestedPlayfields; + private List nestedPlayfields; /// /// A container for keeping track of DrawableHitObjects. /// - /// Whether we want our internal coordinate system to be scaled to a specified width. - protected Playfield(float? customWidth = null) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected Playfield(float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { RelativeSizeAxes = Axes.Both; - - AddInternal(ScaledContent = new ScaledContainer - { - CustomWidth = customWidth, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - content = new Container - { - RelativeSizeAxes = Axes.Both, - } - } - }); } [BackgroundDependencyLoader] @@ -94,22 +79,5 @@ namespace osu.Game.Rulesets.UI /// Creates the container that will be used to contain the s. /// protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); - - private class ScaledContainer : Container - { - /// - /// A value (in game pixels that we should scale our content to match). - /// - public float? CustomWidth; - - //dividing by the customwidth will effectively scale our content to the required container size. - protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale; - - protected override void Update() - { - base.Update(); - RelativeChildSize = new Vector2(DrawScale.X, RelativeChildSize.Y); - } - } } } diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs new file mode 100644 index 0000000000..e1c1427470 --- /dev/null +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -0,0 +1,86 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + public class ScalableContainer : Container + { + /// + /// The scaled content. + /// + public readonly Container ScaledContent; + + protected override Container Content => content; + private readonly Container content; + + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + public ScalableContainer(float? customWidth = null, float? customHeight = null) + { + AddInternal(ScaledContent = new ScaledContainer + { + CustomWidth = customWidth, + CustomHeight = customHeight, + RelativeSizeAxes = Axes.Both, + Child = content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + public class ScaledContainer : Container + { + /// + /// The value to scale the width of the content to match. + /// If null, is used. + /// + public float? CustomWidth; + + /// + /// The value to scale the height of the content to match. + /// if null, is used. + /// + public float? CustomHeight; + + /// + /// The scale that is required for the size of the content to match and . + /// + private Vector2 sizeScale + { + get + { + if (CustomWidth.HasValue && CustomHeight.HasValue) + return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); + if (CustomWidth.HasValue) + return new Vector2(DrawSize.X / CustomWidth.Value); + if (CustomHeight.HasValue) + return new Vector2(DrawSize.Y / CustomHeight.Value); + return Vector2.One; + } + } + + /// + /// Scale the content to the required container size by multiplying by . + /// + protected override Vector2 DrawScale => sizeScale * base.DrawScale; + + protected override void Update() + { + base.Update(); + RelativeChildSize = sizeScale; + } + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index e168f6daec..1c1c8f7f61 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -62,9 +62,14 @@ namespace osu.Game.Rulesets.UI.Scrolling /// Creates a new . /// /// The direction in which s in this container should scroll. - /// Whether we want our internal coordinate system to be scaled to a specified width - protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null) - : base(customWidth) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { this.direction = direction; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 39261fbe57..afeb791029 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -357,6 +357,7 @@ + From 6b8b39abc51bb07062ef22b92fa18ba9cbc3468a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:55 +0900 Subject: [PATCH 2/5] Rewrite SelectionLayer testcase to construct an entire HitObjectComposer --- .../Visual/TestCaseEditorSelectionLayer.cs | 61 +++++++------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 50a39e6c33..5e0c0e165c 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -5,15 +5,13 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -27,44 +25,31 @@ namespace osu.Game.Tests.Visual }; [BackgroundDependencyLoader] - private void load() + private void load(OsuGameBase osuGame) { - var playfield = new OsuEditPlayfield(); - - Children = new Drawable[] + osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap { - new Container + HitObjects = new List { - RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(new StopwatchClock()), - Child = playfield + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider + { + ControlPoints = new List + { + new Vector2(128, 256), + new Vector2(344, 256), + }, + Distance = 400, + Position = new Vector2(128, 256), + Velocity = 1, + TickDistance = 100, + Scale = 0.5f, + } }, - new SelectionLayer(playfield) - }; + }); - var hitCircle1 = new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }; - var hitCircle2 = new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }; - var slider = new Slider - { - ControlPoints = new List - { - new Vector2(128, 256), - new Vector2(344, 256), - }, - Distance = 400, - Position = new Vector2(128, 256), - Velocity = 1, - TickDistance = 100, - Scale = 0.5f, - }; - - hitCircle1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - hitCircle2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - playfield.Add(new DrawableHitCircle(hitCircle1)); - playfield.Add(new DrawableHitCircle(hitCircle2)); - playfield.Add(new DrawableSlider(slider)); + Child = new OsuHitObjectComposer(new OsuRuleset()); } } } From 4934ef742941b864efddf5d00d55ecf5ca71a135 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:01:33 +0900 Subject: [PATCH 3/5] Add playfield aspect ratio + scaling modifications to composer layers --- .../Edit/OsuHitObjectComposer.cs | 4 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 50 +++++++++++++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6652a5fde2..ae19706da3 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,10 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Edit @@ -25,5 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit new HitObjectCompositionTool(), new HitObjectCompositionTool() }; + + protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7f22b3764c..62669150aa 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -25,6 +26,9 @@ namespace osu.Game.Rulesets.Edit protected ICompositionTool CurrentTool { get; private set; } + private RulesetContainer rulesetContainer; + private readonly Container[] layerContainers = new Container[2]; + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -35,7 +39,6 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { - RulesetContainer rulesetContainer; try { rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); @@ -46,6 +49,20 @@ namespace osu.Game.Rulesets.Edit return; } + layerContainers[0] = CreateLayerContainer(); + layerContainers[0].Child = new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } + }; + + layerContainers[1] = CreateLayerContainer(); + layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield); + RadioButtonCollection toolboxCollection; InternalChild = new GridContainer { @@ -66,20 +83,13 @@ namespace osu.Game.Rulesets.Edit }, new Container { + Name = "Content", RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, + layerContainers[0], rulesetContainer, - new SelectionLayer(rulesetContainer.Playfield) + layerContainers[1] } } }, @@ -102,10 +112,28 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + layerContainers.ForEach(l => + { + l.Anchor = rulesetContainer.Playfield.Anchor; + l.Origin = rulesetContainer.Playfield.Origin; + l.Position = rulesetContainer.Playfield.Position; + l.Size = rulesetContainer.Playfield.Size; + }); + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Creates a which provides a layer above or below the . + /// + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); } } From 6757be200723d143dbb8cf574c08408c3326ed52 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 20:15:42 +0900 Subject: [PATCH 4/5] Fix incorrect RelativeChildSize in Catch --- osu.Game/Rulesets/UI/ScalableContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index e1c1427470..43ed770f77 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.UI protected override void Update() { base.Update(); - RelativeChildSize = sizeScale; + RelativeChildSize = new Vector2(CustomWidth.HasValue ? sizeScale.X : RelativeChildSize.X, CustomHeight.HasValue ? sizeScale.Y : RelativeChildSize.Y); } } } From 7c942eb592d5e06b54e31828ae8cb0291318bcfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 13:42:31 +0900 Subject: [PATCH 5/5] Tidy up layer container logic --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 41 +++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 62669150aa..1246127257 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -27,12 +26,11 @@ namespace osu.Game.Rulesets.Edit protected ICompositionTool CurrentTool { get; private set; } private RulesetContainer rulesetContainer; - private readonly Container[] layerContainers = new Container[2]; + private readonly List layerContainers = new List(); protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; - RelativeSizeAxes = Axes.Both; } @@ -42,6 +40,9 @@ namespace osu.Game.Rulesets.Edit try { rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); + + // TODO: should probably be done at a RulesetContainer level to share logic with Player. + rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); } catch (Exception e) { @@ -49,19 +50,13 @@ namespace osu.Game.Rulesets.Edit return; } - layerContainers[0] = CreateLayerContainer(); - layerContainers[0].Child = new Container + ScalableContainer createLayerContainerWithContent(Drawable content) { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, - Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } - }; - - layerContainers[1] = CreateLayerContainer(); - layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield); + var container = CreateLayerContainer(); + container.Child = content; + layerContainers.Add(container); + return container; + } RadioButtonCollection toolboxCollection; InternalChild = new GridContainer @@ -87,9 +82,17 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - layerContainers[0], + createLayerContainerWithContent(new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } + }), rulesetContainer, - layerContainers[1] + createLayerContainerWithContent(new SelectionLayer(rulesetContainer.Playfield)) } } }, @@ -100,8 +103,6 @@ namespace osu.Game.Rulesets.Edit } }; - rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); - toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( @@ -134,6 +135,6 @@ namespace osu.Game.Rulesets.Edit /// /// Creates a which provides a layer above or below the . /// - protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; } }