2022-03-10 16:42:58 +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.Collections.Generic ;
using osu.Framework.Allocation ;
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
using osu.Framework.Input ;
using osu.Framework.Input.Bindings ;
using osu.Framework.Input.Events ;
using osu.Game.Graphics ;
using osuTK ;
namespace osu.Game.Rulesets.Taiko.UI
{
/// <summary>
/// An overlay that captures and displays Taiko mouse and touch input.
/// The boundaries of this overlay defines the interactable area for touch input.
2022-06-02 13:48:26 +08:00
/// A secondary InputDrum is attached by this overlay, which defines the circular boundary which distinguishes "centre" from "rim" hits, and also displays input.
2022-03-10 16:42:58 +08:00
/// </summary>
public class DrumTouchInputArea : Container
{
// The percent of the drum that extends past the bottom of the screen (set to 0.0f to show the full drum)
2022-06-01 16:03:21 +08:00
private const float offscreen_percent = 0.35f ;
2022-06-02 13:36:07 +08:00
private readonly InputDrum touchInputDrum ;
private readonly Circle drumBackground ;
2022-03-10 16:42:58 +08:00
private KeyBindingContainer < TaikoAction > keyBindingContainer ;
// Which Taiko action was pressed by the last OnMouseDown event, so that the corresponding action can be released OnMouseUp even if the cursor position moved
private TaikoAction mouseAction ;
// A map of (Finger Index OnTouchDown -> Which Taiko action was pressed), so that the corresponding action can be released OnTouchUp is released even if the touch position moved
2022-06-02 13:36:07 +08:00
private readonly Dictionary < TouchSource , TaikoAction > touchActions = new Dictionary < TouchSource , TaikoAction > ( Enum . GetNames ( typeof ( TouchSource ) ) . Length ) ;
2022-03-10 16:42:58 +08:00
2022-06-02 13:36:07 +08:00
private readonly Container visibleComponents ;
2022-03-11 20:43:57 +08:00
2022-03-12 21:01:40 +08:00
public DrumTouchInputArea ( )
{
2022-03-10 16:42:58 +08:00
RelativeSizeAxes = Axes . Both ;
RelativePositionAxes = Axes . Both ;
Children = new Drawable [ ]
{
2022-06-02 13:36:07 +08:00
visibleComponents = new Container
{
2022-03-11 20:43:57 +08:00
Alpha = 0.0f ,
2022-03-10 16:42:58 +08:00
RelativeSizeAxes = Axes . Both ,
RelativePositionAxes = Axes . Both ,
Anchor = Anchor . BottomCentre ,
Origin = Anchor . BottomCentre ,
Children = new Drawable [ ]
{
2022-06-02 13:36:07 +08:00
drumBackground = new Circle
{
2022-03-10 21:09:07 +08:00
RelativeSizeAxes = Axes . Both ,
RelativePositionAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
FillMode = FillMode . Fit ,
Alpha = 0.9f ,
} ,
2022-06-02 13:36:07 +08:00
touchInputDrum = new InputDrum
{
2022-03-10 16:42:58 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ,
}
} ,
} ;
}
2022-03-10 21:09:07 +08:00
2022-03-10 16:42:58 +08:00
protected override void LoadComplete ( )
{
2022-03-12 21:32:02 +08:00
Padding = new MarginPadding
{
2022-03-10 21:09:07 +08:00
Top = TaikoPlayfield . DEFAULT_HEIGHT * 2f , // Visual elements should start right below the playfield
2022-06-01 16:03:21 +08:00
Bottom = - touchInputDrum . DrawHeight * offscreen_percent , // The drum should go past the bottom of the screen so that it can be wider
2022-03-10 16:42:58 +08:00
} ;
}
2022-03-10 21:09:07 +08:00
[BackgroundDependencyLoader]
private void load ( TaikoInputManager taikoInputManager , OsuColour colours )
{
keyBindingContainer = taikoInputManager ? . KeyBindingContainer ;
drumBackground . Colour = colours . Gray0 ;
}
2022-03-10 16:42:58 +08:00
protected override bool OnMouseDown ( MouseDownEvent e )
{
2022-03-11 20:43:57 +08:00
ShowTouchControls ( ) ;
2022-03-10 16:42:58 +08:00
mouseAction = getTaikoActionFromInput ( e . ScreenSpaceMouseDownPosition ) ;
keyBindingContainer ? . TriggerPressed ( mouseAction ) ;
2022-03-10 21:09:07 +08:00
return true ;
2022-03-10 16:42:58 +08:00
}
protected override void OnMouseUp ( MouseUpEvent e )
{
keyBindingContainer ? . TriggerReleased ( mouseAction ) ;
base . OnMouseUp ( e ) ;
}
protected override bool OnTouchDown ( TouchDownEvent e )
{
2022-03-11 20:43:57 +08:00
ShowTouchControls ( ) ;
2022-03-10 16:42:58 +08:00
TaikoAction taikoAction = getTaikoActionFromInput ( e . ScreenSpaceTouchDownPosition ) ;
2022-03-10 21:09:07 +08:00
touchActions . Add ( e . Touch . Source , taikoAction ) ;
2022-03-10 16:42:58 +08:00
keyBindingContainer ? . TriggerPressed ( touchActions [ e . Touch . Source ] ) ;
2022-03-10 21:09:07 +08:00
return true ;
2022-03-10 16:42:58 +08:00
}
protected override void OnTouchUp ( TouchUpEvent e )
{
keyBindingContainer ? . TriggerReleased ( touchActions [ e . Touch . Source ] ) ;
2022-03-10 21:09:07 +08:00
touchActions . Remove ( e . Touch . Source ) ;
2022-03-10 16:42:58 +08:00
base . OnTouchUp ( e ) ;
}
2022-03-11 20:43:57 +08:00
protected override bool OnKeyDown ( KeyDownEvent e )
{
HideTouchControls ( ) ;
return false ;
}
2022-03-12 21:01:40 +08:00
public void ShowTouchControls ( )
{
2022-03-11 20:43:57 +08:00
visibleComponents . Animate ( components = > components . FadeIn ( 500 , Easing . OutQuint ) ) ;
}
2022-03-12 21:01:40 +08:00
public void HideTouchControls ( )
{
2022-03-11 20:43:57 +08:00
visibleComponents . Animate ( components = > components . FadeOut ( 2000 , Easing . OutQuint ) ) ;
}
2022-03-12 21:01:40 +08:00
private TaikoAction getTaikoActionFromInput ( Vector2 inputPosition )
{
2022-03-10 16:42:58 +08:00
bool centreHit = inputIsCenterHit ( inputPosition ) ;
2022-03-10 22:17:06 +08:00
bool leftSide = inputIsOnLeftSide ( inputPosition ) ;
2022-03-10 16:42:58 +08:00
2022-06-02 13:36:07 +08:00
return centreHit ? ( leftSide ? TaikoAction . LeftCentre : TaikoAction . RightCentre ) : ( leftSide ? TaikoAction . LeftRim : TaikoAction . RightRim ) ;
2022-03-10 16:42:58 +08:00
}
2022-03-12 21:01:40 +08:00
private bool inputIsOnLeftSide ( Vector2 inputPosition )
{
2022-03-10 16:42:58 +08:00
Vector2 inputPositionToDrumCentreDelta = touchInputDrum . ToLocalSpace ( inputPosition ) - touchInputDrum . OriginPosition ;
return inputPositionToDrumCentreDelta . X < 0f ;
}
2022-03-12 21:01:40 +08:00
private bool inputIsCenterHit ( Vector2 inputPosition )
{
2022-03-10 16:42:58 +08:00
Vector2 inputPositionToDrumCentreDelta = touchInputDrum . ToLocalSpace ( inputPosition ) - touchInputDrum . OriginPosition ;
float inputDrumRadius = Math . Max ( touchInputDrum . Width , touchInputDrum . DrawHeight ) / 2f ;
2022-06-01 16:03:21 +08:00
float centreRadius = ( inputDrumRadius * touchInputDrum . CentreSize ) ;
2022-03-10 16:42:58 +08:00
return inputPositionToDrumCentreDelta . Length < = centreRadius ;
}
}
}