diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs new file mode 100644 index 0000000000..144adf9098 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs @@ -0,0 +1,159 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK.Input; +using osu.Framework.GameModes.Testing; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Graphics.Transformations; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.MathUtils; +using osu.Framework.Graphics.Sprites; + +namespace osu.Desktop.Tests +{ + class TestCaseScoreCounter : TestCase + { + public override string Name => @"ScoreCounter"; + + public override string Description => @"Tests multiple counters"; + + public override void Reset() + { + base.Reset(); + + ScoreCounter uc = new ScoreCounter + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + TextSize = 40, + RollingDuration = 1000, + RollingEasing = EasingTypes.Out, + Count = 0, + Position = new Vector2(20, 20), + LeadingZeroes = 7, + }; + Add(uc); + + StandardComboCounter sc = new StandardComboCounter + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, 20), + IsRollingProportional = true, + RollingDuration = 20, + PopOutDuration = 250, + Count = 0, + TextSize = 40, + }; + Add(sc); + + CatchComboCounter cc = new CatchComboCounter + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + IsRollingProportional = true, + RollingDuration = 20, + PopOutDuration = 250, + Count = 0, + TextSize = 40, + }; + Add(cc); + + AlternativeComboCounter ac = new AlternativeComboCounter + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, 80), + IsRollingProportional = true, + RollingDuration = 20, + ScaleFactor = 2, + Count = 0, + TextSize = 40, + }; + Add(ac); + + + AccuracyCounter pc = new AccuracyCounter + { + Origin = Anchor.TopRight, + Anchor = Anchor.TopRight, + RollingDuration = 1000, + RollingEasing = EasingTypes.Out, + Count = 100.0f, + Position = new Vector2(20, 60), + }; + Add(pc); + + SpriteText text = new SpriteText + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, 190), + Text = @"- unset -", + }; + Add(text); + + StarCounter tc = new StarCounter + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + Position = new Vector2(20, 160), + }; + Add(tc); + + AddButton(@"Reset all", delegate + { + uc.Count = 0; + sc.Count = 0; + ac.Count = 0; + cc.Count = 0; + pc.SetCount(0, 0); + tc.Count = 0; + text.Text = tc.Count.ToString("0.00"); + }); + + AddButton(@"Hit! :D", delegate + { + uc.Count += 300 + (ulong)(300.0 * (sc.Count > 0 ? sc.Count - 1 : 0) / 25.0); + sc.Count++; + ac.Count++; + cc.CatchFruit(new Color4( + Math.Max(0.5f, RNG.NextSingle()), + Math.Max(0.5f, RNG.NextSingle()), + Math.Max(0.5f, RNG.NextSingle()), + 1) + ); + pc.Numerator++; + pc.Denominator++; + }); + + AddButton(@"miss...", delegate + { + sc.Count = 0; + ac.Count = 0; + cc.Count = 0; + pc.Denominator++; + }); + + AddButton(@"Alter stars", delegate + { + tc.Count = RNG.NextSingle() * (tc.MaxStars + 1); + text.Text = tc.Count.ToString("0.00"); + }); + + AddButton(@"Stop counters", delegate + { + uc.StopRolling(); + sc.StopRolling(); + cc.StopRolling(); + ac.StopRolling(); + pc.StopRolling(); + tc.StopRolling(); + }); + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index c54140b267..44a1aef6de 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -140,6 +140,7 @@ + diff --git a/osu.Game/Graphics/UserInterface/AccuracyCounter.cs b/osu.Game/Graphics/UserInterface/AccuracyCounter.cs new file mode 100644 index 0000000000..d13cd20107 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/AccuracyCounter.cs @@ -0,0 +1,102 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transformations; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Used as an accuracy counter. Represented visually as a percentage, internally as a fraction. + /// + public class AccuracyCounter : NumericRollingCounter + { + protected override Type transformType => typeof(TransformAccuracy); + + private long numerator = 0; + public long Numerator + { + get + { + return numerator; + } + set + { + numerator = value; + updateCount(); + } + } + + private ulong denominator = 0; + public ulong Denominator + { + get + { + return denominator; + } + set + { + denominator = value; + updateCount(); + } + } + + public void SetCount(long num, ulong den) + { + numerator = num; + denominator = den; + updateCount(); + } + + private void updateCount() + { + Count = Denominator == 0 ? 100.0f : (Numerator * 100.0f) / Denominator; + } + + public override void ResetCount() + { + numerator = 0; + denominator = 0; + updateCount(); + StopRolling(); + } + + protected override string formatCount(float count) + { + return count.ToString("0.00") + "%"; + } + + protected class TransformAccuracy : Transform + { + public override float CurrentValue + { + get + { + double time = Time; + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + } + + public override void Apply(Drawable d) + { + base.Apply(d); + (d as AccuracyCounter).VisibleCount = CurrentValue; + } + + public TransformAccuracy(IClock clock) + : base(clock) + { + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/AlternativeComboCounter.cs b/osu.Game/Graphics/UserInterface/AlternativeComboCounter.cs new file mode 100644 index 0000000000..8c5510eca4 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/AlternativeComboCounter.cs @@ -0,0 +1,89 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics.Transformations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Allows tint and vertical scaling animation. Used in osu!taiko and osu!mania. + /// + public class AlternativeComboCounter : ULongCounter // btw, I'm terribly bad with names... OUENDAN! + { + public Color4 OriginalColour; + public Color4 TintColour = Color4.OrangeRed; + public int TintDuration = 250; + public float ScaleFactor = 2; + public EasingTypes TintEasing = EasingTypes.None; + public bool CanAnimateWhenBackwards = false; + + public AlternativeComboCounter() : base() + { + IsRollingContinuous = false; + } + + public override void Load() + { + base.Load(); + countSpriteText.Hide(); + OriginalColour = Colour; + } + + public override void ResetCount() + { + SetCountWithoutRolling(0); + } + + protected override void transformCount(ulong currentValue, ulong newValue) + { + // Animate rollover only when going backwards + if (newValue > currentValue) + { + updateTransforms(typeof(TransformULongCounter)); + removeTransforms(typeof(TransformULongCounter)); + VisibleCount = newValue; + } + else + transformCount(new TransformULongCounter(Clock), currentValue, newValue); + } + + protected override ulong getProportionalDuration(ulong currentValue, ulong newValue) + { + ulong difference = currentValue > newValue ? currentValue - newValue : currentValue - newValue; + return difference * RollingDuration; + } + + protected virtual void transformAnimate() + { + countSpriteText.FadeColour(TintColour, 0); + countSpriteText.ScaleTo(new Vector2(1, ScaleFactor)); + countSpriteText.FadeColour(OriginalColour, TintDuration, TintEasing); + countSpriteText.ScaleTo(new Vector2(1, 1), TintDuration, TintEasing); + } + + protected override void transformVisibleCount(ulong currentValue, ulong newValue) + { + if (countSpriteText != null) + { + countSpriteText.Text = newValue.ToString("#,0"); + if (newValue == 0) + { + countSpriteText.FadeOut(TintDuration); + return; + } + countSpriteText.Show(); + if (newValue > currentValue || CanAnimateWhenBackwards) + { + transformAnimate(); + } + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/CatchComboCounter.cs b/osu.Game/Graphics/UserInterface/CatchComboCounter.cs new file mode 100644 index 0000000000..9fb8caa07a --- /dev/null +++ b/osu.Game/Graphics/UserInterface/CatchComboCounter.cs @@ -0,0 +1,60 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK.Graphics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Similar to Standard, but without the 'x' and has tinted pop-ups. Used in osu!catch. + /// + public class CatchComboCounter : StandardComboCounter + { + public CatchComboCounter() : base() + { + CanPopOutWhenBackwards = true; + } + + protected override string formatCount(ulong count) + { + return count.ToString("#,0"); + } + + protected override void transformCount(ulong currentValue, ulong newValue) + { + // Animate rollover only when going backwards + if (newValue > currentValue) + { + updateTransforms(typeof(TransformULongCounter)); + removeTransforms(typeof(TransformULongCounter)); + VisibleCount = newValue; + } + else + { + // Backwards pop-up animation has no tint colour + popOutSpriteText.Colour = countSpriteText.Colour; + transformCount(new TransformULongCounter(Clock), currentValue, newValue); + } + } + + /// + /// Increaces counter and tints pop-out before animation. + /// + /// Last grabbed fruit colour. + public void CatchFruit(Color4 colour) + { + popOutSpriteText.Colour = colour; + Count++; + } + + public override void ResetCount() + { + base.ResetCount(); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/NumericRollingCounter.cs b/osu.Game/Graphics/UserInterface/NumericRollingCounter.cs new file mode 100644 index 0000000000..ce0efb6450 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/NumericRollingCounter.cs @@ -0,0 +1,63 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Skeleton for a numeric counter with a simple roll-up animation. + /// + /// Type of the actual counter. + public abstract class NumericRollingCounter : RollingCounter + { + protected SpriteText countSpriteText; + + protected float textSize = 20.0f; + public float TextSize + { + get { return textSize; } + set + { + textSize = value; + updateTextSize(); + } + } + + public override void Load() + { + base.Load(); + Children = new Drawable[] + { + countSpriteText = new SpriteText + { + Text = formatCount(Count), + TextSize = this.TextSize, + Anchor = this.Anchor, + Origin = this.Origin, + }, + }; + } + + protected override void transformVisibleCount(T currentValue, T newValue) + { + if (countSpriteText != null) + { + countSpriteText.Text = formatCount(newValue); + } + } + + protected virtual void updateTextSize() + { + if (countSpriteText != null) + countSpriteText.TextSize = TextSize; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs new file mode 100644 index 0000000000..b0f757a378 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -0,0 +1,240 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Transformations; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Skeleton for a counter which value rolls-up in a lapse of time. + /// + /// + /// This class only abstracts the basics to roll-up a value in a lapse of time by using Transforms. + /// In order to show a value, you must implement a way to display it, i.e., as a numeric counter or a bar. + /// + /// Type of the actual counter. + public abstract class RollingCounter : Container + { + /// + /// Type of the Transform to use. + /// + /// + /// Must be a subclass of Transform + /// + protected virtual Type transformType => typeof(Transform); + + protected ulong RollingTotalDuration = 0; + + /// + /// If true, each time the Count is updated, it will roll over from the current visible value. + /// Else, it will roll up from the current count value. + /// + public bool IsRollingContinuous = true; + + /// + /// If true, the roll-up duration will be proportional to the counter. + /// + public bool IsRollingProportional = false; + + /// + /// If IsRollingProportional = false, duration in milliseconds for the counter roll-up animation for each + /// element; else duration in milliseconds for the counter roll-up animation in total. + /// + public ulong RollingDuration = 0; + + /// + /// Easing for the counter rollover animation. + /// + public EasingTypes RollingEasing = EasingTypes.None; + + protected T prevVisibleCount; + protected T visibleCount; + + /// + /// Value shown at the current moment. + /// + public virtual T VisibleCount + { + get + { + return visibleCount; + } + protected set + { + prevVisibleCount = visibleCount; + if (visibleCount.Equals(value)) + return; + visibleCount = value; + transformVisibleCount(prevVisibleCount, value); + } + } + + protected T prevCount; + protected T count; + + /// + /// Actual value of counter. + /// + public virtual T Count + { + get + { + return count; + } + set + { + prevCount = count; + count = value; + if (Clock != null) + { + RollingTotalDuration = + IsRollingProportional + ? getProportionalDuration(VisibleCount, value) + : RollingDuration; + transformCount(IsRollingContinuous ? VisibleCount : prevCount, value); + } + } + } + + protected RollingCounter() + { + Debug.Assert( + transformType.IsSubclassOf(typeof(Transform)) || transformType == typeof(Transform), + @"transformType should be a subclass of Transform." + ); + } + + public override void Load() + { + base.Load(); + removeTransforms(transformType); + if (Count == null) + ResetCount(); + VisibleCount = Count; + } + + /// + /// Sets count value, bypassing rollover animation. + /// + /// New count value. + public virtual void SetCountWithoutRolling(T count) + { + Count = count; + StopRolling(); + } + + /// + /// Stops rollover animation, forcing the visible count to be the actual count. + /// + public virtual void StopRolling() + { + removeTransforms(transformType); + VisibleCount = Count; + } + + /// + /// Resets count to default value. + /// + public abstract void ResetCount(); + + /// + /// Calculates the duration of the roll-up animation by using the difference between the current visible value + /// and the new final value. + /// + /// + /// To be used in conjunction with IsRollingProportional = true. + /// Unless a derived class needs to have a proportional rolling, it is not necessary to override this function. + /// + /// Current visible value. + /// New final value. + /// Calculated rollover duration in milliseconds. + protected virtual ulong getProportionalDuration(T currentValue, T newValue) + { + return RollingDuration; + } + + /// + /// Used to format counts. + /// + /// Count to format. + /// Count formatted as a string. + protected virtual string formatCount(T count) + { + return count.ToString(); + } + + protected void updateTransforms(Type type) + { + foreach (ITransform t in Transforms.AliveItems) + if (t.GetType() == type) + t.Apply(this); + } + + protected void removeTransforms(Type type) + { + Transforms.RemoveAll(t => t.GetType() == type); + } + + /// + /// Called when the count is updated to add a transformer that changes the value of the visible count (i.e. + /// implement the rollover animation). + /// + /// Count value before modification. + /// Expected count value after modification- + /// + /// Unless you need to set a custom animation according to the current or new value of the count, the + /// recommended approach is to call transformCount(CustomTransformer(Clock), currentValue, newValue), where + /// CustomTransformer is of type transformerType. + /// By using this approach, there is no need to check if the Clock is not null; this validation is done before + /// adding the transformer. + /// + /// + protected virtual void transformCount(T currentValue, T newValue) + { + object[] parameters = { Clock }; + transformCount((Transform)Activator.CreateInstance(transformType, parameters), currentValue, newValue); + } + + /// + /// Intended to be used by transformCount(). + /// + /// + protected void transformCount(Transform transform, T currentValue, T newValue) + { + Type type = transform.GetType(); + + updateTransforms(type); + removeTransforms(type); + + if (Clock == null) + return; + + if (RollingDuration == 0) + { + VisibleCount = Count; + return; + } + + transform.StartTime = Time; + transform.EndTime = Time + RollingTotalDuration; + transform.StartValue = currentValue; + transform.EndValue = newValue; + transform.Easing = RollingEasing; + + Transforms.Add(transform); + } + + /// + /// This procedure is called each time the visible count value is updated. + /// Override to create custom animations. + /// + /// Visible count value before modification. + /// Expected visible count value after modification- + protected abstract void transformVisibleCount(T currentValue, T newValue); + } +} diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs new file mode 100644 index 0000000000..cdf22eaea1 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -0,0 +1,30 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + public class ScoreCounter : ULongCounter + { + /// + /// How many leading zeroes the counter will have. + /// + public uint LeadingZeroes = 0; + + public override void Load() + { + base.Load(); + countSpriteText.FixedWidth = true; + } + + protected override string formatCount(ulong count) + { + return count.ToString("D" + LeadingZeroes); + } + } +} diff --git a/osu.Game/Graphics/UserInterface/StandardComboCounter.cs b/osu.Game/Graphics/UserInterface/StandardComboCounter.cs new file mode 100644 index 0000000000..b3af78b78e --- /dev/null +++ b/osu.Game/Graphics/UserInterface/StandardComboCounter.cs @@ -0,0 +1,108 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Uses the 'x' symbol and has a pop-out effect while rolling over. Used in osu! standard. + /// + public class StandardComboCounter : ULongCounter + { + public SpriteText popOutSpriteText; + + public ulong PopOutDuration = 0; + public float PopOutBigScale = 2.0f; + public float PopOutSmallScale = 1.2f; + public EasingTypes PopOutEasing = EasingTypes.None; + public bool CanPopOutWhenBackwards = false; + public float PopOutInitialAlpha = 0.75f; + + public StandardComboCounter() : base() + { + IsRollingContinuous = false; + } + + public override void Load() + { + base.Load(); + countSpriteText.Alpha = 0; + Add(popOutSpriteText = new SpriteText + { + Text = formatCount(Count), + Origin = this.Origin, + Anchor = this.Anchor, + TextSize = this.TextSize, + Alpha = 0, + }); + } + + protected override void updateTextSize() + { + base.updateTextSize(); + if (popOutSpriteText != null) + popOutSpriteText.TextSize = this.TextSize; + } + + + protected override void transformCount(ulong currentValue, ulong newValue) + { + // Animate rollover only when going backwards + if (newValue > currentValue) + { + updateTransforms(typeof(TransformULongCounter)); + removeTransforms(typeof(TransformULongCounter)); + VisibleCount = newValue; + } + else + transformCount(new TransformULongCounter(Clock), currentValue, newValue); + } + + protected override ulong getProportionalDuration(ulong currentValue, ulong newValue) + { + ulong difference = currentValue > newValue ? currentValue - newValue : currentValue - newValue; + return difference * RollingDuration; + } + + protected override string formatCount(ulong count) + { + return count.ToString("#,0") + "x"; + } + + protected virtual void transformPopOut() + { + countSpriteText.ScaleTo(PopOutSmallScale); + countSpriteText.ScaleTo(1, PopOutDuration, PopOutEasing); + + popOutSpriteText.ScaleTo(PopOutBigScale); + popOutSpriteText.FadeTo(PopOutInitialAlpha); + popOutSpriteText.ScaleTo(1, PopOutDuration, PopOutEasing); + popOutSpriteText.FadeOut(PopOutDuration, PopOutEasing); + } + + protected override void transformVisibleCount(ulong currentValue, ulong newValue) + { + if (countSpriteText != null && popOutSpriteText != null) + { + countSpriteText.Text = popOutSpriteText.Text = formatCount(newValue); + if (newValue == 0) + { + countSpriteText.FadeOut(PopOutDuration); + } + else + { + countSpriteText.Show(); + if (newValue > currentValue || CanPopOutWhenBackwards) + transformPopOut(); + } + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs new file mode 100644 index 0000000000..3ee67a38d6 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -0,0 +1,202 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Transformations; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Shows a float count as stars. Used as star difficulty display. + /// + public class StarCounter : RollingCounter + { + protected override Type transformType => typeof(TransformStarCounter); + + protected Container starContainer; + protected List stars = new List(); + + public ulong StarAnimationDuration = 500; + public EasingTypes StarAnimationEasing = EasingTypes.OutElasticHalf; + public ulong FadeDuration = 100; + public float MinStarSize = 0.3f; + public float MinStarAlpha = 0.5f; + public int MaxStars = 10; + public int StarSize = 20; + public int StarSpacing = 4; + + public StarCounter() : base() + { + IsRollingProportional = true; + RollingDuration = 150; + } + + protected override ulong getProportionalDuration(float currentValue, float newValue) + { + return (ulong)(Math.Abs(currentValue - newValue) * RollingDuration); + } + + public override void ResetCount() + { + Count = 0; + StopRolling(); + } + + public override void Load() + { + base.Load(); + + Children = new Drawable[] + { + starContainer = new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Width = MaxStars * StarSize + Math.Max(MaxStars - 1, 0) * StarSpacing, + Height = StarSize, + } + }; + + for (int i = 0; i < MaxStars; i++) + { + TextAwesome star = new TextAwesome + { + Icon = FontAwesome.star, + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + TextSize = StarSize, + Scale = new Vector2(MinStarSize), + Alpha = (i == 0) ? 1.0f : MinStarAlpha, + Position = new Vector2((StarSize + StarSpacing) * i + (StarSize + StarSpacing) / 2, 0), + }; + stars.Add(star); + starContainer.Add(star); + } + + ResetCount(); + } + + protected override void transformCount(float currentValue, float newValue) + { + transformStar((int)Math.Floor(currentValue), currentValue, currentValue < newValue); + transformCount(new TransformStarCounter(Clock), currentValue, newValue); + } + + protected void updateTransformStar(int i) + { + foreach (ITransform t in stars[i].Transforms.AliveItems) + if (t.GetType() == typeof(TransformAlpha) || t.GetType() == typeof(TransformScaleVector)) + t.Apply(stars[i]); + + stars[i].Transforms.RemoveAll(t => + t.GetType() == typeof(TransformScaleVector) || t.GetType() == typeof(TransformAlpha) + ); + } + + protected void transformStarScale(int i, TransformScaleVector transform, bool isIncrement, double startTime) + { + transform.StartTime = startTime; + transform.EndTime = transform.StartTime + StarAnimationDuration; + transform.StartValue = stars[i].Scale; + transform.EndValue = new Vector2( + Interpolation.ValueAt( + Math.Min(Math.Max(i, Count), i + 1), + MinStarSize, + 1.0f, + i, + i + 1 + ) + ); + transform.Easing = StarAnimationEasing; + + stars[i].Transforms.Add(transform); + } + + protected void transformStarAlpha(int i, TransformAlpha transform, bool isIncrement, double startTime) + { + transform.StartTime = startTime; + //if (!isIncrement) + //transform.StartTime += StarAnimationDuration - FadeDuration; + transform.EndTime = transform.StartTime + FadeDuration; + transform.StartValue = stars[i].Alpha; + transform.EndValue = i < Count ? 1.0f : MinStarAlpha; + + stars[i].Transforms.Add(transform); + } + + + protected void transformStar(int i, float value, bool isIncrement) + { + if (i >= MaxStars) + return; + + if (Clock == null) + return; + + // Calculate time where animation should had started + double startTime = Time; + // If incrementing, animation should had started when VisibleCount crossed start of star (i) + if (isIncrement) + startTime -= i == (int)Math.Floor(prevCount) ? + getProportionalDuration(prevCount, value) : getProportionalDuration(i, value); + // If decrementing, animation should had started when VisibleCount crossed end of star (i + 1) + else + startTime -= i == (int)Math.Floor(prevCount) ? + getProportionalDuration(prevCount, value) : getProportionalDuration(i + 1, value); + + updateTransformStar(i); + + transformStarScale(i, new TransformScaleVector(Clock), isIncrement, startTime); + transformStarAlpha(i, new TransformAlpha(Clock), isIncrement, startTime); + } + + protected override void transformVisibleCount(float currentValue, float newValue) + { + // Detect increment that passes over an integer value + if (Math.Ceiling(currentValue) <= Math.Floor(newValue)) + for (int i = (int)Math.Ceiling(currentValue); i <= Math.Floor(newValue); i++) + transformStar(i, newValue, true); + + // Detect decrement that passes over an integer value + if (Math.Floor(currentValue) >= Math.Ceiling(newValue)) + for (int i = (int)Math.Floor(newValue); i < Math.Floor(currentValue); i++) + transformStar(i, newValue, false); + } + + protected class TransformStarCounter : Transform + { + public override float CurrentValue + { + get + { + double time = Time; + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + } + + public override void Apply(Drawable d) + { + base.Apply(d); + (d as StarCounter).VisibleCount = CurrentValue; + } + + public TransformStarCounter(IClock clock) + : base(clock) + { + } + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ULongCounter.cs b/osu.Game/Graphics/UserInterface/ULongCounter.cs new file mode 100644 index 0000000000..35df8f5cc8 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ULongCounter.cs @@ -0,0 +1,59 @@ +//Copyright (c) 2007-2016 ppy Pty Ltd . +//Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transformations; +using osu.Framework.MathUtils; +using osu.Framework.Timing; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A simple rolling counter that accepts unsigned long values. + /// + public class ULongCounter : NumericRollingCounter + { + protected override Type transformType => typeof(TransformULongCounter); + + public override void ResetCount() + { + SetCountWithoutRolling(0); + } + + protected override string formatCount(ulong count) + { + return count.ToString("#,0"); + } + + protected class TransformULongCounter : Transform + { + public override ulong CurrentValue + { + get + { + double time = Time; + if (time < StartTime) return StartValue; + if (time >= EndTime) return EndValue; + + return (ulong)Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing); + } + } + + public override void Apply(Drawable d) + { + base.Apply(d); + (d as ULongCounter).VisibleCount = CurrentValue; + } + + public TransformULongCounter(IClock clock) + : base(clock) + { + } + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7852d68e33..e5da51dafe 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -108,10 +108,19 @@ + + + + + + + + +