1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-23 20:07:26 +08:00
osu-lazer/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs

311 lines
11 KiB
C#
Raw Normal View History

2019-08-20 01:44:06 +08:00
// 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.Linq;
2019-08-30 17:46:42 +08:00
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
2019-08-20 01:44:06 +08:00
using osu.Framework.Graphics;
2019-08-30 17:46:42 +08:00
using osu.Framework.Graphics.Colour;
2019-08-20 01:44:06 +08:00
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
2019-08-30 17:46:42 +08:00
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
2019-08-30 17:46:42 +08:00
using osuTK;
2019-08-20 01:44:06 +08:00
2019-08-30 17:46:42 +08:00
namespace osu.Game.Screens.Play.HUD.HitErrorMeters
2019-08-20 01:44:06 +08:00
{
public class BarHitErrorMeter : HitErrorMeter
2019-08-20 01:44:06 +08:00
{
private const int judgement_line_width = 10;
private const int judgement_line_height = 3;
private SpriteIcon arrow;
private SpriteIcon iconEarly;
private SpriteIcon iconLate;
private Container colourBarsEarly;
private Container colourBarsLate;
private Container judgementsContainer;
private double maxHitWindow;
2019-08-20 01:44:06 +08:00
public BarHitErrorMeter()
2019-08-20 01:44:06 +08:00
{
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
2022-03-14 18:18:47 +08:00
const int centre_marker_size = 6;
const int bar_height = 200;
const int bar_width = 2;
const float chevron_size = 8;
const float icon_size = 14;
var hitWindows = HitWindows.GetAllAvailableWindows().ToArray();
InternalChild = new Container
2019-08-20 01:44:06 +08:00
{
AutoSizeAxes = Axes.X,
Height = bar_height,
Margin = new MarginPadding(2),
2019-08-20 01:44:06 +08:00
Children = new Drawable[]
{
colourBars = new Container
2019-08-20 01:44:06 +08:00
{
Name = "colour axis",
X = chevron_size,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = judgement_line_width,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
iconEarly = new SpriteIcon
2019-08-30 18:14:07 +08:00
{
Y = -10,
2022-03-14 18:18:47 +08:00
Size = new Vector2(icon_size),
2019-08-30 18:14:07 +08:00
Icon = FontAwesome.Solid.ShippingFast,
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
2019-08-30 18:14:07 +08:00
},
iconLate = new SpriteIcon
2019-08-30 18:14:07 +08:00
{
Y = 10,
2022-03-14 18:18:47 +08:00
Size = new Vector2(icon_size),
2019-08-30 18:14:07 +08:00
Icon = FontAwesome.Solid.Bicycle,
Anchor = Anchor.BottomCentre,
Origin = Anchor.Centre,
},
colourBarsEarly = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Width = bar_width,
RelativeSizeAxes = Axes.Y,
Height = 0.5f,
Scale = new Vector2(1, -1),
},
colourBarsLate = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.TopCentre,
Width = bar_width,
RelativeSizeAxes = Axes.Y,
Height = 0.5f,
},
judgementsContainer = new Container
{
Name = "judgements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Width = judgement_line_width,
},
new Circle
{
Name = "middle marker behind",
Colour = GetColourForHitResult(hitWindows.Last().result),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(centre_marker_size),
},
new Circle
{
Name = "middle marker in front",
Colour = GetColourForHitResult(hitWindows.Last().result).Darken(0.5f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(centre_marker_size),
Scale = new Vector2(0.5f),
},
}
2019-08-20 01:44:06 +08:00
},
new Container
2019-08-20 01:44:06 +08:00
{
Name = "average chevron",
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Width = chevron_size,
2019-08-20 01:44:06 +08:00
RelativeSizeAxes = Axes.Y,
Child = arrow = new SpriteIcon
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(chevron_size),
}
2019-08-20 01:44:06 +08:00
},
}
};
2019-08-20 01:44:06 +08:00
createColourBars(hitWindows);
2019-08-20 01:44:06 +08:00
}
protected override void LoadComplete()
{
base.LoadComplete();
colourBars.Height = 0;
colourBars.ResizeHeightTo(1, 800, Easing.OutQuint);
2019-08-30 17:50:38 +08:00
arrow.Alpha = 0;
arrow.Delay(200).FadeInFromZero(600);
}
protected override void Update()
{
base.Update();
// undo any layout rotation to display icons in the correct orientation
iconEarly.Rotation = -Rotation;
iconLate.Rotation = -Rotation;
}
private void createColourBars((HitResult result, double length)[] windows)
2019-08-20 13:00:09 +08:00
{
// max to avoid div-by-zero.
maxHitWindow = Math.Max(1, windows.First().length);
for (int i = 0; i < windows.Length; i++)
{
(var result, double length) = windows[i];
float hitWindow = (float)(length / maxHitWindow);
colourBarsEarly.Add(createColourBar(result, hitWindow, i == 0));
colourBarsLate.Add(createColourBar(result, hitWindow, i == 0));
}
Drawable createColourBar(HitResult result, float height, bool requireGradient = false)
{
2019-12-21 19:52:53 +08:00
var colour = GetColourForHitResult(result);
if (requireGradient)
{
// the first bar needs gradient rendering.
const float gradient_start = 0.8f;
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
2019-12-21 19:52:53 +08:00
Colour = colour,
Height = height * gradient_start
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(colour, colour.Opacity(0)),
Y = gradient_start,
Height = height * (1 - gradient_start)
},
}
};
}
return new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colour,
Height = height
};
}
}
2019-08-20 13:00:09 +08:00
private double floatingAverage;
private Container colourBars;
private const int max_concurrent_judgements = 50;
protected override void OnNewJudgement(JudgementResult judgement)
2019-08-20 01:44:06 +08:00
{
2022-03-14 18:18:47 +08:00
const int arrow_move_duration = 400;
if (!judgement.IsHit || judgement.HitObject.HitWindows?.WindowFor(HitResult.Miss) == 0)
2019-08-20 01:44:06 +08:00
return;
if (!judgement.Type.IsScorable() || judgement.Type.IsBonus())
return;
if (judgementsContainer.Count > max_concurrent_judgements)
{
const double quick_fade_time = 100;
// check with a bit of lenience to avoid precision error in comparison.
var old = judgementsContainer.FirstOrDefault(j => j.LifetimeEnd > Clock.CurrentTime + quick_fade_time * 1.1);
if (old != null)
{
old.ClearTransforms();
old.FadeOut(quick_fade_time).Expire();
}
}
judgementsContainer.Add(new JudgementLine
{
Y = getRelativeJudgementPosition(judgement.TimeOffset),
Colour = GetColourForHitResult(judgement.Type),
});
2019-08-20 01:44:06 +08:00
arrow.MoveToY(
getRelativeJudgementPosition(floatingAverage = floatingAverage * 0.9 + judgement.TimeOffset * 0.1)
, arrow_move_duration, Easing.Out);
2019-08-20 01:44:06 +08:00
}
private float getRelativeJudgementPosition(double value) => Math.Clamp((float)((value / maxHitWindow) + 1) / 2, 0, 1);
2019-08-20 01:44:06 +08:00
internal class JudgementLine : CompositeDrawable
2019-08-20 01:44:06 +08:00
{
public JudgementLine()
{
RelativeSizeAxes = Axes.X;
RelativePositionAxes = Axes.Y;
Height = judgement_line_height;
Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
Anchor = Anchor.TopCentre;
InternalChild = new Circle
{
RelativeSizeAxes = Axes.Both,
};
}
2019-08-20 01:44:06 +08:00
protected override void LoadComplete()
{
const int judgement_fade_in_duration = 100;
const int judgement_fade_out_duration = 5000;
base.LoadComplete();
2019-08-20 01:44:06 +08:00
Alpha = 0;
Width = 0;
this
.FadeTo(0.8f, judgement_fade_in_duration, Easing.OutQuint)
.ResizeWidthTo(1, judgement_fade_in_duration, Easing.OutQuint)
.Then()
.FadeOut(judgement_fade_out_duration, Easing.In)
.ResizeWidthTo(0, judgement_fade_out_duration, Easing.In)
.Expire();
}
2019-08-20 01:44:06 +08:00
}
2021-09-10 04:21:51 +08:00
2021-09-20 22:22:36 +08:00
public override void Clear() => judgementsContainer.Clear();
2019-08-20 01:44:06 +08:00
}
}