From d2fefdbf453b992d82db05641e3cc8a8dcd2a1a1 Mon Sep 17 00:00:00 2001 From: Rudi Herouard Date: Sun, 15 Sep 2024 22:23:45 +0200 Subject: [PATCH] Create CenterCircle --- .../UI/CenterCircle/CenterCircle.cs | 98 ++++++++++ .../UI/CenterCircle/CenterCircleContainer.cs | 20 +++ .../UI/CenterCircle/FanShaped.cs | 168 ++++++++++++++++++ .../UI/GitarooPlayfield.cs | 1 + 4 files changed, 287 insertions(+) create mode 100644 osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircle.cs create mode 100644 osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircleContainer.cs create mode 100644 osu.Game.Rulesets.Gitaroo/UI/CenterCircle/FanShaped.cs diff --git a/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircle.cs b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircle.cs new file mode 100644 index 0000000..7f8d022 --- /dev/null +++ b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircle.cs @@ -0,0 +1,98 @@ +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Gitaroo.UI.CenterCircle; + +public partial class CenterCircle : Circle, IKeyBindingHandler +{ + private int downCount; + + private readonly ColourInfo notPressedColor = new ColourInfo + { + TopLeft = new Color4(183, 115, 229, byte.MaxValue), + TopRight = new Color4(65, 97, 225, byte.MaxValue), + BottomLeft = new Color4(10, 105, 246, byte.MaxValue), + BottomRight = new Color4(75, 107, 250, byte.MaxValue), + HasSingleColour = false + }; + + private readonly ColourInfo pressedColor = new ColourInfo + { + TopLeft = new Color4(182, 0, 228, byte.MaxValue), + TopRight = new Color4(68, 0, 232, byte.MaxValue), + BottomLeft = new Color4(7, 0, 246, byte.MaxValue), + BottomRight = new Color4(74, 0, 248, byte.MaxValue), + HasSingleColour = false + }; + + private const float pressed_scale = 1.15f; + private const float released_scale = 1f; + + public CenterCircle() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Size = new Vector2(25); + Colour = notPressedColor; + Masking = true; + BorderThickness = 2.5f; + BorderColour = Color4.Gray; + } + + internal void UpdateCircle(int downCount) + { + Colour = downCount > 0 ? pressedColor : notPressedColor; + + if (downCount > 0) + expand(); + else + contract(); + } + + private void expand() + { + this.ScaleTo(released_scale) + .ScaleTo(pressed_scale, 400, Easing.OutElasticHalf); + } + + private void contract() + { + this.ScaleTo(released_scale, 400, Easing.OutQuad); + } + + // Code below based of osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorContainer.cs + + public bool OnPressed(KeyBindingPressEvent e) + { + switch (e.Action) + { + case GitarooAction.Button1: + case GitarooAction.Button2: + downCount++; + UpdateCircle(downCount); + break; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + switch (e.Action) + { + case GitarooAction.Button1: + case GitarooAction.Button2: + downCount = Math.Max(0, downCount - 1); + + if (downCount == 0) + UpdateCircle(downCount); + break; + } + } +} diff --git a/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircleContainer.cs b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircleContainer.cs new file mode 100644 index 0000000..ff8ba59 --- /dev/null +++ b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/CenterCircleContainer.cs @@ -0,0 +1,20 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Gitaroo.UI.CenterCircle; + +public partial class CenterCircleContainer : Container +{ + public CenterCircleContainer() + { + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + InternalChildren = new Container[] + { + new FanShaped(), + new CenterCircle() + }; + } +} diff --git a/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/FanShaped.cs b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/FanShaped.cs new file mode 100644 index 0000000..08998ff --- /dev/null +++ b/osu.Game.Rulesets.Gitaroo/UI/CenterCircle/FanShaped.cs @@ -0,0 +1,168 @@ +using System; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Gitaroo.UI.CenterCircle; + +public partial class FanShaped : Container +{ + private readonly Triangle leftArrow, rightArrow; + private readonly Container rotatingContainer; + private const float left_arrow_max_x = -86; + private const float right_arrow_max_x = 86; + private const float fan_shaped_max_y = 155; + private const float fan_shape_angle = 70; + + public FanShaped() + { + RelativeSizeAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + InternalChildren = new[] + { + rotatingContainer = new Container + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.TopCentre, + Alpha = 0f, + Rotation = 90, + Children = new[] + { + new Triangle // Fan Shaped + { + Origin = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Size = new Vector2(fan_shaped_get_x(fan_shape_angle), fan_shaped_max_y), + Colour = ColourInfo.GradientVertical(Color4.Cyan, Color4.Cyan.Opacity(0)), + }, + + new Triangle // Middle arrow + { + Origin = Anchor.BottomCentre, + Anchor = Anchor.TopCentre, + Size = new Vector2(17, 50), + Colour = Color4.White, + Alpha = 0.5f, + Rotation = 180, + }, + + leftArrow = new Triangle // Left Arrow + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.TopCentre, + Size = new Vector2(11, 13), + Position = new Vector2(left_arrow_max_x, 5), + Colour = Color4.Cyan, + Alpha = 0.5f, + Rotation = 180, + }, + + rightArrow = new Triangle // Right Arrow + { + Origin = Anchor.BottomRight, + Anchor = Anchor.TopCentre, + Size = new Vector2(11, 13), + Position = new Vector2(right_arrow_max_x, 5), + Colour = Color4.Cyan, + Alpha = 0.5f, + Rotation = 180, + } + } + } + }; + } + + private float fan_shaped_get_x(float angle) + { + if (angle is >= 180 or <= 0) + { + throw new InvalidOperationException($"Invalid FanShaped angle: {angle}°. The angle must be less than 180° and more than 0°."); + } + + return (float)(Math.Tan(MathHelper.DegreesToRadians(angle / 2f)) * fan_shaped_max_y * 2f); + } + + private void fanShapeFadeIn() + { + const float reset_value = 0.5f; + const float down_time = 40; + const float up_time = 750; + + rotatingContainer.Animate( + t => t.FadeTo(Math.Min(t.Alpha - reset_value, 0), down_time, Easing.OutQuint) + ).Then( + t => t.FadeIn(up_time, Easing.OutQuint)); + + leftArrow.Animate( + t => t.MoveToX(Math.Max(t.X - reset_value * left_arrow_max_x, 0), down_time, Easing.OutQuint) + ).Then( + t => t.MoveToX(left_arrow_max_x, up_time, Easing.OutQuint)); + + rightArrow.Animate( + t => t.MoveToX(Math.Min(t.X - reset_value * right_arrow_max_x, 0), down_time, Easing.OutQuint) + ).Then( + t => t.MoveToX(right_arrow_max_x, up_time, Easing.OutQuint)); + } + + private void fanShapeFadeOut() + { + const float reset_value = 0.5f; + + const float down_time = 40; + const float up_time = 750; + + rotatingContainer.Animate( + t => t.FadeTo(Math.Min(t.Alpha + reset_value, 1), down_time, Easing.OutQuint) + ).Then( + t => t.FadeOut(up_time, Easing.OutQuint)); + + leftArrow.Animate( + t => t.MoveToX(Math.Max(t.X + reset_value * left_arrow_max_x, left_arrow_max_x), down_time, Easing.OutQuint) + ).Then( + t => t.MoveToX(0, up_time, Easing.OutQuint)); + + rightArrow.Animate( + t => t.MoveToX(Math.Min(t.X + reset_value * right_arrow_max_x, right_arrow_max_x), down_time, Easing.OutQuint) + ).Then( + t => t.MoveToX(0, up_time, Easing.OutQuint)); + } + + protected override bool OnHover(HoverEvent e) + { + fanShapeFadeIn(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + fanShapeFadeOut(); + } + + private float getDegreesFromPosition(Vector2 a, Vector2 b) + { + Vector2 direction = b - a; + float angle = MathHelper.RadiansToDegrees(MathF.Atan2(direction.Y, direction.X)); + if (angle < 0f) angle += 360f; + + return angle - 90; + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + rotatingContainer.Rotation = getDegreesFromPosition(AnchorPosition, e.MousePosition); + return base.OnMouseMove(e); + } + + // todo: implement joystick support + protected override bool OnJoystickAxisMove(JoystickAxisMoveEvent e) + { + return base.OnJoystickAxisMove(e); + } +} diff --git a/osu.Game.Rulesets.Gitaroo/UI/GitarooPlayfield.cs b/osu.Game.Rulesets.Gitaroo/UI/GitarooPlayfield.cs index deb6993..0b75832 100644 --- a/osu.Game.Rulesets.Gitaroo/UI/GitarooPlayfield.cs +++ b/osu.Game.Rulesets.Gitaroo/UI/GitarooPlayfield.cs @@ -19,6 +19,7 @@ private void load() AddRangeInternal(new Drawable[] { HitObjectContainer, + new CenterCircle.CenterCircleContainer() }); } }