Merge pull request #17622 from frenzibyte/taiko-target-classic-position

Replicate osu!(stable)'s hit target position with "Classic" mod
This commit is contained in:
Dean Herbert
2022-07-20 20:08:00 +09:00
committed by GitHub
5 changed files with 221 additions and 156 deletions

View File

@ -0,0 +1,19 @@
// 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 osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneTaikoPlayerLegacySkin : LegacySkinPlayerTestScene
{
protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
protected override TestPlayer CreatePlayer(Ruleset ruleset)
{
SelectedMods.Value = new[] { new TaikoModClassic() };
return base.CreatePlayer(ruleset);
}
}
}

View File

@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
drawableTaikoRuleset.LockPlayfieldAspect.Value = false;
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
playfield.ClassicHitTargetPosition.Value = true;
}
public void Update(Playfield playfield)

View File

@ -22,13 +22,14 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
/// </summary>
internal class LegacyInputDrum : Container
{
private Container content;
private LegacyHalfDrum left;
private LegacyHalfDrum right;
private Container content;
public LegacyInputDrum()
{
RelativeSizeAxes = Axes.Both;
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
}
[BackgroundDependencyLoader]

View File

@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
using osuTK;
@ -33,7 +34,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
sampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer);
RelativeSizeAxes = Axes.Both;
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
@ -41,12 +43,32 @@ namespace osu.Game.Rulesets.Taiko.UI
{
Children = new Drawable[]
{
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new DefaultInputDrum())
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
},
sampleTriggerSource
};
}
private class DefaultInputDrum : AspectContainer
{
public DefaultInputDrum()
{
RelativeSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Scale = new Vector2(0.9f),
Children = new Drawable[]
Children = new[]
{
new TaikoHalfDrum(false)
{
@ -71,131 +93,130 @@ namespace osu.Game.Rulesets.Taiko.UI
CentreAction = TaikoAction.RightCentre
}
}
}),
sampleTriggerSource
};
}
/// <summary>
/// A half-drum. Contains one centre and one rim hit.
/// </summary>
private class TaikoHalfDrum : Container, IKeyBindingHandler<TaikoAction>
{
/// <summary>
/// The key to be used for the rim of the half-drum.
/// </summary>
public TaikoAction RimAction;
/// <summary>
/// The key to be used for the centre of the half-drum.
/// </summary>
public TaikoAction CentreAction;
private readonly Sprite rim;
private readonly Sprite rimHit;
private readonly Sprite centre;
private readonly Sprite centreHit;
[Resolved]
private DrumSampleTriggerSource sampleTriggerSource { get; set; }
public TaikoHalfDrum(bool flipped)
{
Masking = true;
Children = new Drawable[]
{
rim = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both
},
rimHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Blending = BlendingParameters.Additive,
},
centre = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f)
},
centreHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f),
Alpha = 0,
Blending = BlendingParameters.Additive
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours)
/// <summary>
/// A half-drum. Contains one centre and one rim hit.
/// </summary>
private class TaikoHalfDrum : Container, IKeyBindingHandler<TaikoAction>
{
rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit");
/// <summary>
/// The key to be used for the rim of the half-drum.
/// </summary>
public TaikoAction RimAction;
rimHit.Colour = colours.Blue;
centreHit.Colour = colours.Pink;
}
/// <summary>
/// The key to be used for the centre of the half-drum.
/// </summary>
public TaikoAction CentreAction;
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{
Drawable target = null;
Drawable back = null;
private readonly Sprite rim;
private readonly Sprite rimHit;
private readonly Sprite centre;
private readonly Sprite centreHit;
if (e.Action == CentreAction)
[Resolved]
private DrumSampleTriggerSource sampleTriggerSource { get; set; }
public TaikoHalfDrum(bool flipped)
{
target = centreHit;
back = centre;
Masking = true;
sampleTriggerSource.Play(HitType.Centre);
}
else if (e.Action == RimAction)
{
target = rimHit;
back = rim;
sampleTriggerSource.Play(HitType.Rim);
Children = new Drawable[]
{
rim = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both
},
rimHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
Blending = BlendingParameters.Additive,
},
centre = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f)
},
centreHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f),
Alpha = 0,
Blending = BlendingParameters.Additive
}
};
}
if (target != null)
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours)
{
const float scale_amount = 0.05f;
const float alpha_amount = 0.5f;
rim.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer");
rimHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-outer-hit");
centre.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner");
centreHit.Texture = textures.Get(@"Gameplay/taiko/taiko-drum-inner-hit");
const float down_time = 40;
const float up_time = 1000;
back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
.Then()
.ScaleTo(1, up_time, Easing.OutQuint);
target.Animate(
t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
).Then(
t => t.ScaleTo(1, up_time, Easing.OutQuint),
t => t.FadeOut(up_time, Easing.OutQuint)
);
rimHit.Colour = colours.Blue;
centreHit.Colour = colours.Pink;
}
return false;
}
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{
Drawable target = null;
Drawable back = null;
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{
if (e.Action == CentreAction)
{
target = centreHit;
back = centre;
sampleTriggerSource.Play(HitType.Centre);
}
else if (e.Action == RimAction)
{
target = rimHit;
back = rim;
sampleTriggerSource.Play(HitType.Rim);
}
if (target != null)
{
const float scale_amount = 0.05f;
const float alpha_amount = 0.5f;
const float down_time = 40;
const float up_time = 1000;
back.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint)
.Then()
.ScaleTo(1, up_time, Easing.OutQuint);
target.Animate(
t => t.ScaleTo(target.Scale.X - scale_amount, down_time, Easing.OutQuint),
t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.OutQuint)
).Then(
t => t.ScaleTo(1, up_time, Easing.OutQuint),
t => t.FadeOut(up_time, Easing.OutQuint)
);
}
return false;
}
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{
}
}
}
}

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
@ -34,6 +35,11 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
public const float DEFAULT_HEIGHT = 200;
/// <summary>
/// Whether the hit target should be nudged further towards the left area, matching the stable "classic" position.
/// </summary>
public Bindable<bool> ClassicHitTargetPosition = new BindableBool();
private Container<HitExplosion> hitExplosionContainer;
private Container<KiaiHitExplosion> kiaiExplosionContainer;
private JudgementContainer<DrawableTaikoJudgement> judgementContainer;
@ -45,8 +51,8 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly IDictionary<HitResult, HitExplosionPool> explosionPools = new Dictionary<HitResult, HitExplosionPool>();
private ProxyContainer topLevelHitContainer;
private InputDrum inputDrum;
private Container rightArea;
private Container leftArea;
/// <remarks>
/// <see cref="Playfield.AddNested"/> is purposefully not called on this to prevent i.e. being able to interact
@ -54,14 +60,43 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </remarks>
private BarLinePlayfield barLinePlayfield;
private Container hitTargetOffsetContent;
private Container playfieldContent;
private Container playfieldOverlay;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
inputDrum = new InputDrum(HitObjectContainer)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
};
InternalChildren = new[]
{
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()),
new Container
{
Name = "Left overlay",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
BorderColour = colours.Gray0,
Children = new[]
{
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
inputDrum.CreateProxy(),
}
},
mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Mascot), _ => Empty())
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.TopLeft,
RelativePositionAxes = Axes.Y,
RelativeSizeAxes = Axes.None,
Y = 0.2f
},
rightArea = new Container
{
Name = "Right area",
@ -71,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
new Container
{
Name = "Masked elements before hit objects",
Name = "Elements before hit objects",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Children = new[]
@ -86,22 +121,28 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
},
hitTargetOffsetContent = new Container
new Container
{
Name = "Masked hit objects content",
RelativeSizeAxes = Axes.Both,
Masking = true,
Child = playfieldContent = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
barLinePlayfield = new BarLinePlayfield(),
HitObjectContainer,
}
}
},
playfieldOverlay = new Container
{
Name = "Elements after hit objects",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
barLinePlayfield = new BarLinePlayfield(),
new Container
{
Name = "Hit objects",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
HitObjectContainer,
drumRollHitContainer = new DrumRollHitContainer()
}
},
drumRollHitContainer = new DrumRollHitContainer(),
kiaiExplosionContainer = new Container<KiaiHitExplosion>
{
Name = "Kiai hit explosions",
@ -117,36 +158,15 @@ namespace osu.Game.Rulesets.Taiko.UI
},
}
},
leftArea = new Container
{
Name = "Left overlay",
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
BorderColour = colours.Gray0,
Children = new Drawable[]
{
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
new InputDrum(HitObjectContainer)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
}
},
mascot = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Mascot), _ => Empty())
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.TopLeft,
RelativePositionAxes = Axes.Y,
RelativeSizeAxes = Axes.None,
Y = 0.2f
},
topLevelHitContainer = new ProxyContainer
{
Name = "Top level hit objects",
RelativeSizeAxes = Axes.Both,
},
drumRollHitContainer.CreateProxy(),
// this is added at the end of the hierarchy to receive input before taiko objects.
// but is proxied below everything to not cover visual effects such as hit explosions.
inputDrum,
};
RegisterPool<Hit, DrawableHit>(50);
@ -193,8 +213,9 @@ namespace osu.Game.Rulesets.Taiko.UI
// Padding is required to be updated for elements which are based on "absolute" X sized elements.
// This is basically allowing for correct alignment as relative pieces move around them.
rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth };
hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
rightArea.Padding = new MarginPadding { Left = inputDrum.Width };
playfieldContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
playfieldOverlay.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
mascot.Scale = new Vector2(DrawHeight / DEFAULT_HEIGHT);
}