mirror of
https://github.com/ppy/osu.git
synced 2025-01-31 05:23:21 +08:00
Merge pull request #31519 from EVAST9919/trail-rotate
Add support for `CursorTrailRotate` skin command
This commit is contained in:
commit
471180d947
@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -17,6 +18,7 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Testing.Input;
|
using osu.Framework.Testing.Input;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -103,6 +105,23 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("contract", () => this.ChildrenOfType<CursorTrail>().Single().NewPartScale = Vector2.One);
|
AddStep("contract", () => this.ChildrenOfType<CursorTrail>().Single().NewPartScale = Vector2.One);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRotation()
|
||||||
|
{
|
||||||
|
createTest(() =>
|
||||||
|
{
|
||||||
|
var skinContainer = new LegacySkinContainer(renderer, provideMiddle: true, enableRotation: true);
|
||||||
|
var legacyCursorTrail = new LegacyRotatingCursorTrail(skinContainer)
|
||||||
|
{
|
||||||
|
NewPartScale = new Vector2(10)
|
||||||
|
};
|
||||||
|
|
||||||
|
skinContainer.Child = legacyCursorTrail;
|
||||||
|
|
||||||
|
return skinContainer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void createTest(Func<Drawable> createContent) => AddStep("create trail", () =>
|
private void createTest(Func<Drawable> createContent) => AddStep("create trail", () =>
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
@ -121,12 +140,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private readonly IRenderer renderer;
|
private readonly IRenderer renderer;
|
||||||
private readonly bool provideMiddle;
|
private readonly bool provideMiddle;
|
||||||
private readonly bool provideCursor;
|
private readonly bool provideCursor;
|
||||||
|
private readonly bool enableRotation;
|
||||||
|
|
||||||
public LegacySkinContainer(IRenderer renderer, bool provideMiddle, bool provideCursor = true)
|
public LegacySkinContainer(IRenderer renderer, bool provideMiddle, bool provideCursor = true, bool enableRotation = false)
|
||||||
{
|
{
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.provideMiddle = provideMiddle;
|
this.provideMiddle = provideMiddle;
|
||||||
this.provideCursor = provideCursor;
|
this.provideCursor = provideCursor;
|
||||||
|
this.enableRotation = enableRotation;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
@ -152,7 +173,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
public ISample GetSample(ISampleInfo sampleInfo) => null;
|
||||||
|
|
||||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
{
|
||||||
|
switch (lookup)
|
||||||
|
{
|
||||||
|
case OsuSkinConfiguration osuLookup:
|
||||||
|
if (osuLookup == OsuSkinConfiguration.CursorTrailRotate)
|
||||||
|
return SkinUtils.As<TValue>(new BindableBool(enableRotation));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
|
public ISkin FindProvider(Func<ISkin, bool> lookupFunction) => lookupFunction(this) ? this : null;
|
||||||
|
|
||||||
@ -185,5 +218,19 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
|
MoveMouseTo(ToScreenSpace(DrawSize / 2 + DrawSize / 3 * rPos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class LegacyRotatingCursorTrail : LegacyCursorTrail
|
||||||
|
{
|
||||||
|
public LegacyRotatingCursorTrail([NotNull] ISkin skin)
|
||||||
|
: base(skin)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
PartRotation += (float)(Time.Elapsed * 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public partial class LegacyCursor : SkinnableCursor
|
public partial class LegacyCursor : SkinnableCursor
|
||||||
{
|
{
|
||||||
|
public static readonly int REVOLUTION_DURATION = 10000;
|
||||||
|
|
||||||
private const float pressed_scale = 1.3f;
|
private const float pressed_scale = 1.3f;
|
||||||
private const float released_scale = 1f;
|
private const float released_scale = 1f;
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
if (spin)
|
if (spin)
|
||||||
ExpandTarget.Spin(10000, RotationDirection.Clockwise);
|
ExpandTarget.Spin(REVOLUTION_DURATION, RotationDirection.Clockwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Expand()
|
public override void Expand()
|
||||||
|
@ -34,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
private void load(OsuConfigManager config, ISkinSource skinSource)
|
private void load(OsuConfigManager config, ISkinSource skinSource)
|
||||||
{
|
{
|
||||||
cursorSize = config.GetBindable<float>(OsuSetting.GameplayCursorSize).GetBoundCopy();
|
cursorSize = config.GetBindable<float>(OsuSetting.GameplayCursorSize).GetBoundCopy();
|
||||||
|
AllowPartRotation = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorTrailRotate)?.Value ?? true;
|
||||||
|
|
||||||
Texture = skin.GetTexture("cursortrail");
|
Texture = skin.GetTexture("cursortrail");
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
CursorCentre,
|
CursorCentre,
|
||||||
CursorExpand,
|
CursorExpand,
|
||||||
CursorRotate,
|
CursorRotate,
|
||||||
|
CursorTrailRotate,
|
||||||
HitCircleOverlayAboveNumber,
|
HitCircleOverlayAboveNumber,
|
||||||
|
|
||||||
// ReSharper disable once IdentifierTypo
|
// ReSharper disable once IdentifierTypo
|
||||||
|
@ -34,19 +34,24 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual float FadeExponent => 1.7f;
|
protected virtual float FadeExponent => 1.7f;
|
||||||
|
|
||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
|
||||||
private int currentIndex;
|
|
||||||
private IShader shader;
|
|
||||||
private double timeOffset;
|
|
||||||
private float time;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The scale used on creation of a new trail part.
|
/// The scale used on creation of a new trail part.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 NewPartScale = Vector2.One;
|
public Vector2 NewPartScale { get; set; } = Vector2.One;
|
||||||
|
|
||||||
private Anchor trailOrigin = Anchor.Centre;
|
/// <summary>
|
||||||
|
/// The rotation (in degrees) to apply to trail parts when <see cref="AllowPartRotation"/> is <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public float PartRotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to rotate trail parts based on the value of <see cref="PartRotation"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected bool AllowPartRotation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The trail part texture origin.
|
||||||
|
/// </summary>
|
||||||
protected Anchor TrailOrigin
|
protected Anchor TrailOrigin
|
||||||
{
|
{
|
||||||
get => trailOrigin;
|
get => trailOrigin;
|
||||||
@ -57,6 +62,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
|
private Anchor trailOrigin = Anchor.Centre;
|
||||||
|
private int currentIndex;
|
||||||
|
private IShader shader;
|
||||||
|
private double timeOffset;
|
||||||
|
private float time;
|
||||||
|
|
||||||
public CursorTrail()
|
public CursorTrail()
|
||||||
{
|
{
|
||||||
// as we are currently very dependent on having a running clock, let's make our own clock for the time being.
|
// as we are currently very dependent on having a running clock, let's make our own clock for the time being.
|
||||||
@ -220,6 +232,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
private float time;
|
private float time;
|
||||||
private float fadeExponent;
|
private float fadeExponent;
|
||||||
|
private float angle;
|
||||||
|
|
||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
private Vector2 originPosition;
|
private Vector2 originPosition;
|
||||||
@ -239,6 +252,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
texture = Source.texture;
|
texture = Source.texture;
|
||||||
time = Source.time;
|
time = Source.time;
|
||||||
fadeExponent = Source.FadeExponent;
|
fadeExponent = Source.FadeExponent;
|
||||||
|
angle = Source.AllowPartRotation ? float.DegreesToRadians(Source.PartRotation) : 0;
|
||||||
|
|
||||||
originPosition = Vector2.Zero;
|
originPosition = Vector2.Zero;
|
||||||
|
|
||||||
@ -279,6 +293,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
renderer.PushLocalMatrix(DrawInfo.Matrix);
|
renderer.PushLocalMatrix(DrawInfo.Matrix);
|
||||||
|
|
||||||
|
float sin = MathF.Sin(angle);
|
||||||
|
float cos = MathF.Cos(angle);
|
||||||
|
|
||||||
foreach (var part in parts)
|
foreach (var part in parts)
|
||||||
{
|
{
|
||||||
if (part.InvalidationID == -1)
|
if (part.InvalidationID == -1)
|
||||||
@ -289,7 +306,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
vertexBatch.Add(new TexturedTrailVertex
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
{
|
{
|
||||||
Position = new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y),
|
Position = rotateAround(
|
||||||
|
new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y),
|
||||||
|
part.Position, sin, cos),
|
||||||
TexturePosition = textureRect.BottomLeft,
|
TexturePosition = textureRect.BottomLeft,
|
||||||
TextureRect = new Vector4(0, 0, 1, 1),
|
TextureRect = new Vector4(0, 0, 1, 1),
|
||||||
Colour = DrawColourInfo.Colour.BottomLeft.Linear,
|
Colour = DrawColourInfo.Colour.BottomLeft.Linear,
|
||||||
@ -298,7 +317,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
vertexBatch.Add(new TexturedTrailVertex
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
{
|
{
|
||||||
Position = new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y),
|
Position = rotateAround(
|
||||||
|
new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X,
|
||||||
|
part.Position.Y + texture.DisplayHeight * (1 - originPosition.Y) * part.Scale.Y), part.Position, sin, cos),
|
||||||
TexturePosition = textureRect.BottomRight,
|
TexturePosition = textureRect.BottomRight,
|
||||||
TextureRect = new Vector4(0, 0, 1, 1),
|
TextureRect = new Vector4(0, 0, 1, 1),
|
||||||
Colour = DrawColourInfo.Colour.BottomRight.Linear,
|
Colour = DrawColourInfo.Colour.BottomRight.Linear,
|
||||||
@ -307,7 +328,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
vertexBatch.Add(new TexturedTrailVertex
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
{
|
{
|
||||||
Position = new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y),
|
Position = rotateAround(
|
||||||
|
new Vector2(part.Position.X + texture.DisplayWidth * (1 - originPosition.X) * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y),
|
||||||
|
part.Position, sin, cos),
|
||||||
TexturePosition = textureRect.TopRight,
|
TexturePosition = textureRect.TopRight,
|
||||||
TextureRect = new Vector4(0, 0, 1, 1),
|
TextureRect = new Vector4(0, 0, 1, 1),
|
||||||
Colour = DrawColourInfo.Colour.TopRight.Linear,
|
Colour = DrawColourInfo.Colour.TopRight.Linear,
|
||||||
@ -316,7 +339,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
vertexBatch.Add(new TexturedTrailVertex
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
{
|
{
|
||||||
Position = new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y),
|
Position = rotateAround(
|
||||||
|
new Vector2(part.Position.X - texture.DisplayWidth * originPosition.X * part.Scale.X, part.Position.Y - texture.DisplayHeight * originPosition.Y * part.Scale.Y),
|
||||||
|
part.Position, sin, cos),
|
||||||
TexturePosition = textureRect.TopLeft,
|
TexturePosition = textureRect.TopLeft,
|
||||||
TextureRect = new Vector4(0, 0, 1, 1),
|
TextureRect = new Vector4(0, 0, 1, 1),
|
||||||
Colour = DrawColourInfo.Colour.TopLeft.Linear,
|
Colour = DrawColourInfo.Colour.TopLeft.Linear,
|
||||||
@ -330,6 +355,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
shader.Unbind();
|
shader.Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Vector2 rotateAround(Vector2 input, Vector2 origin, float sin, float cos)
|
||||||
|
{
|
||||||
|
float xTranslated = input.X - origin.X;
|
||||||
|
float yTranslated = input.Y - origin.Y;
|
||||||
|
|
||||||
|
return new Vector2(xTranslated * cos - yTranslated * sin, xTranslated * sin + yTranslated * cos) + origin;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
@ -36,6 +36,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 CurrentExpandedScale => skinnableCursor.ExpandTarget?.Scale ?? Vector2.One;
|
public Vector2 CurrentExpandedScale => skinnableCursor.ExpandTarget?.Scale ?? Vector2.One;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current rotation of the cursor.
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentRotation => skinnableCursor.ExpandTarget?.Rotation ?? 0;
|
||||||
|
|
||||||
public IBindable<float> CursorScale => cursorScale;
|
public IBindable<float> CursorScale => cursorScale;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -83,7 +83,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (cursorTrail.Drawable is CursorTrail trail)
|
if (cursorTrail.Drawable is CursorTrail trail)
|
||||||
|
{
|
||||||
trail.NewPartScale = ActiveCursor.CurrentExpandedScale;
|
trail.NewPartScale = ActiveCursor.CurrentExpandedScale;
|
||||||
|
trail.PartRotation = ActiveCursor.CurrentRotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||||
|
Loading…
Reference in New Issue
Block a user