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

Merge pull request #24387 from peppy/playfield-skin-layer

Add new skin layer to allow components anchored to the playfield
This commit is contained in:
Bartłomiej Dach 2023-08-03 22:06:17 +02:00 committed by GitHub
commit bd8b9c8e63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 12 deletions

View File

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -25,6 +26,22 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly List<Stage> stages = new List<Stage>(); private readonly List<Stage> stages = new List<Stage>();
public override Quad SkinnableComponentScreenSpaceDrawQuad
{
get
{
if (Stages.Count == 1)
return Stages.First().ScreenSpaceDrawQuad;
RectangleF area = RectangleF.Empty;
foreach (var stage in Stages)
area = RectangleF.Union(area, stage.ScreenSpaceDrawQuad.AABBFloat);
return area;
}
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos)); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
public ManiaPlayfield(List<StageDefinition> stageDefinitions) public ManiaPlayfield(List<StageDefinition> stageDefinitions)

View File

@ -8,6 +8,7 @@ using osu.Game.Overlays;
using osu.Game.Overlays.SkinEditor; using osu.Game.Overlays.SkinEditor;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Skinning;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
@ -19,7 +20,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestToggleEditor() public void TestToggleEditor()
{ {
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox var skinComponentsContainer = new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.SongSelect));
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(skinComponentsContainer, null)
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,

View File

@ -31,7 +31,7 @@ namespace osu.Game.IO.Archives
{ {
ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name); ZipArchiveEntry entry = archive.Entries.SingleOrDefault(e => e.Key == name);
if (entry == null) if (entry == null)
throw new FileNotFoundException(); return null;
var owner = MemoryAllocator.Default.Allocate<byte>((int)entry.Size); var owner = MemoryAllocator.Default.Allocate<byte>((int)entry.Size);

View File

@ -13,6 +13,7 @@ using osu.Framework.Threading;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Rulesets;
using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
@ -23,14 +24,22 @@ namespace osu.Game.Overlays.SkinEditor
{ {
public Action<Type>? RequestPlacement; public Action<Type>? RequestPlacement;
private readonly SkinComponentsContainer? target; private readonly SkinComponentsContainer target;
private readonly RulesetInfo? ruleset;
private FillFlowContainer fill = null!; private FillFlowContainer fill = null!;
public SkinComponentToolbox(SkinComponentsContainer? target = null) /// <summary>
: base(target?.Lookup.Ruleset == null ? SkinEditorStrings.Components : LocalisableString.Interpolate($"{SkinEditorStrings.Components} ({target.Lookup.Ruleset.Name})")) /// Create a new component toolbox for the specified taget.
/// </summary>
/// <param name="target">The target. This is mainly used as a dependency source to find candidate components.</param>
/// <param name="ruleset">A ruleset to filter components by. If null, only components which are not ruleset-specific will be included.</param>
public SkinComponentToolbox(SkinComponentsContainer target, RulesetInfo? ruleset)
: base(ruleset == null ? SkinEditorStrings.Components : LocalisableString.Interpolate($"{SkinEditorStrings.Components} ({ruleset.Name})"))
{ {
this.target = target; this.target = target;
this.ruleset = ruleset;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -51,7 +60,7 @@ namespace osu.Game.Overlays.SkinEditor
{ {
fill.Clear(); fill.Clear();
var skinnableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables(target?.Lookup.Ruleset); var skinnableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables(ruleset);
foreach (var type in skinnableTypes) foreach (var type in skinnableTypes)
attemptAddComponent(type); attemptAddComponent(type);
} }

View File

@ -366,14 +366,14 @@ namespace osu.Game.Overlays.SkinEditor
// If the new target has a ruleset, let's show ruleset-specific items at the top, and the rest below. // If the new target has a ruleset, let's show ruleset-specific items at the top, and the rest below.
if (target.NewValue.Ruleset != null) if (target.NewValue.Ruleset != null)
{ {
componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer) componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer, target.NewValue.Ruleset)
{ {
RequestPlacement = requestPlacement RequestPlacement = requestPlacement
}); });
} }
// Remove the ruleset from the lookup to get base components. // Remove the ruleset from the lookup to get base components.
componentsSidebar.Add(new SkinComponentToolbox(getTarget(new SkinComponentsContainerLookup(target.NewValue.Target))) componentsSidebar.Add(new SkinComponentToolbox(skinComponentsContainer, null)
{ {
RequestPlacement = requestPlacement RequestPlacement = requestPlacement
}); });

View File

@ -23,6 +23,7 @@ using osu.Game.Skinning;
using osuTK; using osuTK;
using osu.Game.Rulesets.Objects.Pooling; using osu.Game.Rulesets.Objects.Pooling;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Rulesets.UI namespace osu.Game.Rulesets.UI
{ {
@ -94,6 +95,16 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public readonly BindableBool DisplayJudgements = new BindableBool(true); public readonly BindableBool DisplayJudgements = new BindableBool(true);
/// <summary>
/// A screen space draw quad which resembles the edges of the playfield for skinning purposes.
/// This will allow users / components to snap objects to the "edge" of the playfield.
/// </summary>
/// <remarks>
/// Rulesets which reduce the visible area further than the full relative playfield space itself
/// should retarget this to the ScreenSpaceDrawQuad of the appropriate container.
/// </remarks>
public virtual Quad SkinnableComponentScreenSpaceDrawQuad => ScreenSpaceDrawQuad;
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
[CanBeNull] [CanBeNull]
protected IReadOnlyList<Mod> Mods { get; private set; } protected IReadOnlyList<Mod> Mods { get; private set; }

View File

@ -12,6 +12,7 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -69,7 +70,9 @@ namespace osu.Game.Screens.Play
public Bindable<bool> ShowHealthBar = new Bindable<bool>(true); public Bindable<bool> ShowHealthBar = new Bindable<bool>(true);
[CanBeNull]
private readonly DrawableRuleset drawableRuleset; private readonly DrawableRuleset drawableRuleset;
private readonly IReadOnlyList<Mod> mods; private readonly IReadOnlyList<Mod> mods;
/// <summary> /// <summary>
@ -103,10 +106,11 @@ namespace osu.Game.Screens.Play
private readonly List<Drawable> hideTargets; private readonly List<Drawable> hideTargets;
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true) private readonly Drawable playfieldComponents;
public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods, bool alwaysShowLeaderboard = true)
{ {
Drawable rulesetComponents; Drawable rulesetComponents;
this.drawableRuleset = drawableRuleset; this.drawableRuleset = drawableRuleset;
this.mods = mods; this.mods = mods;
@ -123,6 +127,9 @@ namespace osu.Game.Screens.Play
rulesetComponents = drawableRuleset != null rulesetComponents = drawableRuleset != null
? new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, } ? new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, }
: Empty(), : Empty(),
playfieldComponents = drawableRuleset != null
? new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.Playfield, drawableRuleset.Ruleset.RulesetInfo)) { AlwaysPresent = true, }
: Empty(),
topRightElements = new FillFlowContainer topRightElements = new FillFlowContainer
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
@ -162,7 +169,7 @@ namespace osu.Game.Screens.Play
}, },
}; };
hideTargets = new List<Drawable> { mainComponents, rulesetComponents, topRightElements }; hideTargets = new List<Drawable> { mainComponents, rulesetComponents, playfieldComponents, topRightElements };
if (!alwaysShowLeaderboard) if (!alwaysShowLeaderboard)
hideTargets.Add(LeaderboardFlow); hideTargets.Add(LeaderboardFlow);
@ -230,6 +237,16 @@ namespace osu.Game.Screens.Play
{ {
base.Update(); base.Update();
if (drawableRuleset != null)
{
Quad playfieldScreenSpaceDrawQuad = drawableRuleset.Playfield.SkinnableComponentScreenSpaceDrawQuad;
playfieldComponents.Position = ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft);
playfieldComponents.Width = (ToLocalSpace(playfieldScreenSpaceDrawQuad.TopRight) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length;
playfieldComponents.Height = (ToLocalSpace(playfieldScreenSpaceDrawQuad.BottomLeft) - ToLocalSpace(playfieldScreenSpaceDrawQuad.TopLeft)).Length;
playfieldComponents.Rotation = drawableRuleset.Playfield.Rotation;
}
float? lowestTopScreenSpaceLeft = null; float? lowestTopScreenSpaceLeft = null;
float? lowestTopScreenSpaceRight = null; float? lowestTopScreenSpaceRight = null;

View File

@ -68,7 +68,10 @@ namespace osu.Game.Skinning
MainHUDComponents, MainHUDComponents,
[Description("Song select")] [Description("Song select")]
SongSelect SongSelect,
[Description("Playfield")]
Playfield
} }
} }
} }