1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-17 19:04:00 +08:00
Files
osu-lazer/osu.Game.Rulesets.Osu/Skinning/Default/SpinnerRotationTracker.cs
T
Rudi Herouard 7cdbc2c20a Fix "spins per minute" shows up early #31173
Make isSpinnableTime public in SpinnerRotationTracker and use it to set Tracking in OsuModSpunOut.

Tracking was previously set to true, causing the "spins per minute" to appear immediately when the spinner appeared.
2025-04-13 23:35:44 +02:00

133 lines
4.3 KiB
C#

// 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 System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public partial class SpinnerRotationTracker : CircularContainer
{
public override bool IsPresent => true; // handle input when hidden
private readonly DrawableSpinner drawableSpinner;
private Vector2? mousePosition;
private float? lastAngle;
private float currentRotation;
private bool rotationTransferred;
[Resolved(canBeNull: true)]
private IGameplayClock? gameplayClock { get; set; }
public SpinnerRotationTracker(DrawableSpinner drawableSpinner)
{
this.drawableSpinner = drawableSpinner;
drawableSpinner.HitObjectApplied += resetState;
RelativeSizeAxes = Axes.Both;
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public bool Tracking { get; set; }
/// <summary>
/// Whether the spinning is spinning at a reasonable speed to be considered visually spinning.
/// </summary>
public readonly BindableBool IsSpinning = new BindableBool();
/// <summary>
/// Whether currently in the correct time range to allow spinning.
/// </summary>
public bool IsSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current;
protected override bool OnMouseMove(MouseMoveEvent e)
{
mousePosition = Parent!.ToLocalSpace(e.ScreenSpaceMousePosition);
return base.OnMouseMove(e);
}
protected override void Update()
{
base.Update();
if (mousePosition is Vector2 pos)
{
float thisAngle = -float.RadiansToDegrees(MathF.Atan2(pos.X - DrawSize.X / 2, pos.Y - DrawSize.Y / 2));
float delta = lastAngle == null ? 0 : thisAngle - lastAngle.Value;
// Normalise the delta to -180 .. 180
if (delta > 180) delta -= 360;
if (delta < -180) delta += 360;
if (Tracking)
AddRotation(delta);
lastAngle = thisAngle;
}
IsSpinning.Value = IsSpinnableTime && Math.Abs(currentRotation - Rotation) > 10f;
Rotation = (float)Interpolation.Damp(Rotation, currentRotation, 0.99, Math.Abs(Time.Elapsed));
}
/// <summary>
/// Rotate the disc by the provided angle (in addition to any existing rotation).
/// </summary>
/// <remarks>
/// Will be a no-op if not a valid time to spin.
/// </remarks>
/// <param name="delta">The delta angle.</param>
public void AddRotation(float delta)
{
if (!IsSpinnableTime)
return;
if (!rotationTransferred)
{
currentRotation = Rotation;
rotationTransferred = true;
}
Debug.Assert(Math.Abs(delta) <= 180);
double rate = gameplayClock?.GetTrueGameplayRate() ?? Clock.Rate;
delta = (float)(delta * Math.Abs(rate));
currentRotation += delta;
drawableSpinner.Result.History.ReportDelta(Time.Current, delta);
}
private void resetState(DrawableHitObject obj)
{
Tracking = false;
IsSpinning.Value = false;
mousePosition = null;
lastAngle = null;
currentRotation = 0;
Rotation = 0;
rotationTransferred = false;
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (drawableSpinner.IsNotNull())
drawableSpinner.HitObjectApplied -= resetState;
}
}
}