1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-21 14:52:55 +08:00
osu-lazer/osu.Game.Rulesets.Taiko/UI/DrumTouchInputArea.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

326 lines
11 KiB
C#
Raw Normal View History

// 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.Collections.Generic;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Configuration;
using osuTK;
2022-07-22 15:18:22 +08:00
using osuTK.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
{
/// <summary>
2022-07-22 15:18:22 +08:00
/// An overlay that captures and displays osu!taiko mouse and touch input.
/// </summary>
2022-07-22 16:48:07 +08:00
public partial class DrumTouchInputArea : VisibilityContainer
{
2022-07-22 16:48:07 +08:00
// visibility state affects our child. we always want to handle input.
public override bool PropagatePositionalInputSubTree => true;
public override bool PropagateNonPositionalInputSubTree => true;
private KeyBindingContainer<TaikoAction> keyBindingContainer = null!;
private readonly Dictionary<object, TaikoAction> trackedActions = new Dictionary<object, TaikoAction>();
2022-07-22 16:17:38 +08:00
private Container mainContent = null!;
private DrumSegment leftCentre = null!;
private DrumSegment rightCentre = null!;
private DrumSegment leftRim = null!;
private DrumSegment rightRim = null!;
private readonly Bindable<TaikoTouchControlScheme> configTouchControlScheme = new Bindable<TaikoTouchControlScheme>();
2022-07-22 16:17:38 +08:00
[BackgroundDependencyLoader]
2023-01-10 21:07:07 +08:00
private void load(TaikoInputManager taikoInputManager, TaikoRulesetConfigManager config)
2022-03-12 21:01:40 +08:00
{
2022-07-22 16:17:38 +08:00
Debug.Assert(taikoInputManager.KeyBindingContainer != null);
2023-01-13 03:04:37 +08:00
2022-07-22 16:17:38 +08:00
keyBindingContainer = taikoInputManager.KeyBindingContainer;
2022-07-22 15:18:22 +08:00
2022-07-22 16:17:38 +08:00
// Container should handle input everywhere.
RelativeSizeAxes = Axes.Both;
2022-07-22 15:18:22 +08:00
2022-07-22 16:48:07 +08:00
const float centre_region = 0.80f;
Children = new Drawable[]
{
2022-07-22 16:17:38 +08:00
new Container
2022-06-02 13:36:07 +08:00
{
2022-07-22 16:17:38 +08:00
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
2022-07-22 16:48:07 +08:00
Height = 350,
2022-07-22 16:17:38 +08:00
Y = 20,
Masking = true,
FillMode = FillMode.Fit,
Children = new Drawable[]
{
2022-07-22 16:17:38 +08:00
mainContent = new Container
2022-06-02 13:36:07 +08:00
{
RelativeSizeAxes = Axes.Both,
2022-07-22 16:17:38 +08:00
Children = new Drawable[]
{
leftRim = new DrumSegment
2022-07-22 16:17:38 +08:00
{
2022-07-22 16:48:07 +08:00
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = -2,
2022-07-22 16:17:38 +08:00
},
rightRim = new DrumSegment
2022-07-22 16:17:38 +08:00
{
2022-07-22 16:48:07 +08:00
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = 2,
Rotation = 90,
2022-07-22 16:17:38 +08:00
},
leftCentre = new DrumSegment
2022-07-22 16:17:38 +08:00
{
Anchor = Anchor.BottomCentre,
2022-07-22 16:48:07 +08:00
Origin = Anchor.BottomRight,
X = -2,
Scale = new Vector2(centre_region),
2022-07-22 16:17:38 +08:00
},
rightCentre = new DrumSegment
2022-07-22 16:48:07 +08:00
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = 2,
Scale = new Vector2(centre_region),
2022-07-22 16:48:07 +08:00
Rotation = 90,
2023-01-11 04:05:41 +08:00
}
2022-07-22 16:17:38 +08:00
}
},
}
},
};
config.BindWith(TaikoRulesetSetting.TouchControlScheme, configTouchControlScheme);
configTouchControlScheme.BindValueChanged(scheme =>
2023-01-12 04:06:43 +08:00
{
var actions = getOrderedActionsForScheme(scheme.NewValue);
leftRim.Action = actions[0];
leftCentre.Action = actions[1];
rightCentre.Action = actions[2];
rightRim.Action = actions[3];
}, true);
}
protected override bool OnKeyDown(KeyDownEvent e)
{
// Hide whenever the keyboard is used.
2022-07-22 16:48:07 +08:00
Hide();
return false;
}
2022-07-22 15:18:22 +08:00
protected override bool OnTouchDown(TouchDownEvent e)
{
handleDown(e.Touch.Source, e.ScreenSpaceTouchDownPosition);
return true;
}
protected override void OnTouchUp(TouchUpEvent e)
{
handleUp(e.Touch.Source);
base.OnTouchUp(e);
}
private static TaikoAction[] getOrderedActionsForScheme(TaikoTouchControlScheme scheme)
{
switch (scheme)
{
case TaikoTouchControlScheme.KDDK:
return new[]
{
TaikoAction.LeftRim,
TaikoAction.LeftCentre,
TaikoAction.RightCentre,
TaikoAction.RightRim
};
case TaikoTouchControlScheme.DDKK:
return new[]
{
TaikoAction.LeftCentre,
TaikoAction.RightCentre,
TaikoAction.LeftRim,
TaikoAction.RightRim
};
case TaikoTouchControlScheme.KKDD:
return new[]
{
TaikoAction.LeftRim,
TaikoAction.RightRim,
TaikoAction.LeftCentre,
TaikoAction.RightCentre
};
default:
throw new ArgumentOutOfRangeException(nameof(scheme), scheme, null);
}
}
private void handleDown(object source, Vector2 position)
{
2022-07-22 16:48:07 +08:00
Show();
2023-01-13 03:04:37 +08:00
TaikoAction taikoAction = getTaikoActionFromPosition(position);
// Not too sure how this can happen, but let's avoid throwing.
2024-02-02 18:48:13 +08:00
if (!trackedActions.TryAdd(source, taikoAction))
return;
2023-01-13 03:04:37 +08:00
keyBindingContainer.TriggerPressed(taikoAction);
}
private void handleUp(object source)
{
keyBindingContainer.TriggerReleased(trackedActions[source]);
trackedActions.Remove(source);
}
private TaikoAction getTaikoActionFromPosition(Vector2 inputPosition)
2022-03-12 21:01:40 +08:00
{
2022-07-22 16:48:07 +08:00
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
2022-07-22 15:18:22 +08:00
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
2022-07-22 15:18:22 +08:00
if (leftSide)
return centreHit ? leftCentre.Action : leftRim.Action;
return centreHit ? rightCentre.Action : rightRim.Action;
}
2022-07-22 16:48:07 +08:00
protected override void PopIn()
{
mainContent.FadeIn(500, Easing.OutQuint);
}
protected override void PopOut()
{
mainContent.FadeOut(300);
}
private partial class DrumSegment : CompositeDrawable, IKeyBindingHandler<TaikoAction>
{
private TaikoAction action;
2023-01-13 03:04:37 +08:00
public TaikoAction Action
{
get => action;
set
{
if (action == value)
return;
2023-01-13 03:04:37 +08:00
action = value;
updateColoursFromAction();
}
}
2023-01-13 03:04:37 +08:00
private Circle overlay = null!;
2022-07-22 16:48:07 +08:00
private Circle circle = null!;
2022-07-22 16:48:07 +08:00
[Resolved]
private OsuColour colours { get; set; } = null!;
2022-07-22 16:48:07 +08:00
public override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos);
public DrumSegment()
2022-07-22 16:48:07 +08:00
{
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
}
2022-07-22 16:48:07 +08:00
[BackgroundDependencyLoader]
private void load()
{
2022-07-22 16:48:07 +08:00
InternalChildren = new Drawable[]
{
new Container
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
circle = new Circle
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.8f,
Scale = new Vector2(2),
},
overlay = new Circle
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
Scale = new Vector2(2),
}
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColoursFromAction();
}
2022-07-22 16:48:07 +08:00
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{
if (e.Action == Action)
overlay.FadeTo(1f, 80, Easing.OutQuint);
2022-07-22 16:48:07 +08:00
return false;
}
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{
if (e.Action == Action)
2022-07-22 16:48:07 +08:00
overlay.FadeOut(1000, Easing.OutQuint);
}
private void updateColoursFromAction()
{
if (!IsLoaded)
return;
var colour = getColourFromTaikoAction(Action);
circle.Colour = colour.Multiply(1.4f).Darken(2.8f);
overlay.Colour = colour;
}
private Color4 getColourFromTaikoAction(TaikoAction handledAction)
{
switch (handledAction)
{
case TaikoAction.LeftRim:
case TaikoAction.RightRim:
return colours.Blue;
case TaikoAction.LeftCentre:
case TaikoAction.RightCentre:
return colours.Pink;
}
throw new ArgumentOutOfRangeException();
}
2022-07-22 16:48:07 +08:00
}
}
}