1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 20:13:21 +08:00

Merge pull request #22109 from OpenSauce04/taiko-touch-ddkk-new

Add ability to change osu!taiko touch screen layout from KDDK to DDKK/KKDD alternative control schemes
This commit is contained in:
Bartłomiej Dach 2023-02-04 22:26:28 +01:00 committed by GitHub
commit 6aae0e8a60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 246 additions and 46 deletions

View File

@ -2,8 +2,11 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets.Taiko.Configuration;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Tests.Visual;
@ -14,36 +17,48 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
private DrumTouchInputArea drumTouchInputArea = null!;
[SetUpSteps]
public void SetUpSteps()
private readonly Bindable<TaikoTouchControlScheme> controlScheme = new Bindable<TaikoTouchControlScheme>();
[BackgroundDependencyLoader]
private void load()
{
AddStep("create drum", () =>
var config = (TaikoRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
config.BindWith(TaikoRulesetSetting.TouchControlScheme, controlScheme);
}
private void createDrum()
{
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo)
{
Child = new TaikoInputManager(new TaikoRuleset().RulesetInfo)
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
new InputDrum
{
new InputDrum
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 0.2f,
},
drumTouchInputArea = new DrumTouchInputArea
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
},
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Height = 0.2f,
},
};
});
drumTouchInputArea = new DrumTouchInputArea
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}
}
};
}
[Test]
public void TestDrum()
{
AddStep("create drum", createDrum);
AddStep("show drum", () => drumTouchInputArea.Show());
AddStep("change scheme (kddk)", () => controlScheme.Value = TaikoTouchControlScheme.KDDK);
AddStep("change scheme (kkdd)", () => controlScheme.Value = TaikoTouchControlScheme.KKDD);
AddStep("change scheme (ddkk)", () => controlScheme.Value = TaikoTouchControlScheme.DDKK);
}
protected override Ruleset CreateRuleset() => new TaikoRuleset();
}
}

View File

@ -0,0 +1,28 @@
// 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 osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets.Taiko.Configuration
{
public class TaikoRulesetConfigManager : RulesetConfigManager<TaikoRulesetSetting>
{
public TaikoRulesetConfigManager(SettingsStore? settings, RulesetInfo ruleset, int? variant = null)
: base(settings, ruleset, variant)
{
}
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
SetDefault(TaikoRulesetSetting.TouchControlScheme, TaikoTouchControlScheme.KDDK);
}
}
public enum TaikoRulesetSetting
{
TouchControlScheme
}
}

View File

@ -0,0 +1,12 @@
// 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.
namespace osu.Game.Rulesets.Taiko.Configuration
{
public enum TaikoTouchControlScheme
{
KDDK,
DDKK,
KKDD
}
}

View File

@ -28,9 +28,13 @@ using osu.Game.Rulesets.Taiko.Skinning.Argon;
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
using osu.Game.Overlays.Settings;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Statistics;
using osu.Game.Skinning;
using osu.Game.Rulesets.Configuration;
using osu.Game.Configuration;
using osu.Game.Rulesets.Taiko.Configuration;
namespace osu.Game.Rulesets.Taiko
{
@ -194,6 +198,10 @@ namespace osu.Game.Rulesets.Taiko
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new TaikoRulesetConfigManager(settings, RulesetInfo);
public override RulesetSettingsSubsection CreateSettings() => new TaikoSettingsSubsection(this);
protected override IEnumerable<HitResult> GetValidHitResults()
{
return new[]

View File

@ -0,0 +1,36 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Localisation;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Taiko.Configuration;
namespace osu.Game.Rulesets.Taiko
{
public partial class TaikoSettingsSubsection : RulesetSettingsSubsection
{
protected override LocalisableString Header => "osu!taiko";
public TaikoSettingsSubsection(TaikoRuleset ruleset)
: base(ruleset)
{
}
[BackgroundDependencyLoader]
private void load()
{
var config = (TaikoRulesetConfigManager)Config;
Children = new Drawable[]
{
new SettingsEnumDropdown<TaikoTouchControlScheme>
{
LabelText = "Touch control scheme",
Current = config.GetBindable<TaikoTouchControlScheme>(TaikoRulesetSetting.TouchControlScheme)
}
};
}
}
}

View File

@ -1,9 +1,11 @@
// 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;
@ -11,6 +13,7 @@ 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;
using osuTK.Graphics;
@ -31,15 +34,18 @@ namespace osu.Game.Rulesets.Taiko.UI
private Container mainContent = null!;
private QuarterCircle leftCentre = null!;
private QuarterCircle rightCentre = null!;
private QuarterCircle leftRim = null!;
private QuarterCircle rightRim = 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>();
[BackgroundDependencyLoader]
private void load(TaikoInputManager taikoInputManager, OsuColour colours)
private void load(TaikoInputManager taikoInputManager, TaikoRulesetConfigManager config)
{
Debug.Assert(taikoInputManager.KeyBindingContainer != null);
keyBindingContainer = taikoInputManager.KeyBindingContainer;
// Container should handle input everywhere.
@ -65,27 +71,27 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
leftRim = new QuarterCircle(TaikoAction.LeftRim, colours.Blue)
leftRim = new DrumSegment
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = -2,
},
rightRim = new QuarterCircle(TaikoAction.RightRim, colours.Blue)
rightRim = new DrumSegment
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = 2,
Rotation = 90,
},
leftCentre = new QuarterCircle(TaikoAction.LeftCentre, colours.Pink)
leftCentre = new DrumSegment
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
X = -2,
Scale = new Vector2(centre_region),
},
rightCentre = new QuarterCircle(TaikoAction.RightCentre, colours.Pink)
rightCentre = new DrumSegment
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomRight,
@ -98,6 +104,17 @@ namespace osu.Game.Rulesets.Taiko.UI
}
},
};
config.BindWith(TaikoRulesetSetting.TouchControlScheme, configTouchControlScheme);
configTouchControlScheme.BindValueChanged(scheme =>
{
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)
@ -119,11 +136,47 @@ namespace osu.Game.Rulesets.Taiko.UI
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)
{
Show();
TaikoAction taikoAction = getTaikoActionFromInput(position);
TaikoAction taikoAction = getTaikoActionFromPosition(position);
// Not too sure how this can happen, but let's avoid throwing.
if (trackedActions.ContainsKey(source))
@ -139,18 +192,15 @@ namespace osu.Game.Rulesets.Taiko.UI
trackedActions.Remove(source);
}
private bool validMouse(MouseButtonEvent e) =>
leftRim.Contains(e.ScreenSpaceMouseDownPosition) || rightRim.Contains(e.ScreenSpaceMouseDownPosition);
private TaikoAction getTaikoActionFromInput(Vector2 inputPosition)
private TaikoAction getTaikoActionFromPosition(Vector2 inputPosition)
{
bool centreHit = leftCentre.Contains(inputPosition) || rightCentre.Contains(inputPosition);
bool leftSide = ToLocalSpace(inputPosition).X < DrawWidth / 2;
if (leftSide)
return centreHit ? TaikoAction.LeftCentre : TaikoAction.LeftRim;
return centreHit ? leftCentre.Action : leftRim.Action;
return centreHit ? TaikoAction.RightCentre : TaikoAction.RightRim;
return centreHit ? rightCentre.Action : rightRim.Action;
}
protected override void PopIn()
@ -163,23 +213,42 @@ namespace osu.Game.Rulesets.Taiko.UI
mainContent.FadeOut(300);
}
private partial class QuarterCircle : CompositeDrawable, IKeyBindingHandler<TaikoAction>
private partial class DrumSegment : CompositeDrawable, IKeyBindingHandler<TaikoAction>
{
private readonly Circle overlay;
private TaikoAction action;
private readonly TaikoAction handledAction;
public TaikoAction Action
{
get => action;
set
{
if (action == value)
return;
private readonly Circle circle;
action = value;
updateColoursFromAction();
}
}
private Circle overlay = null!;
private Circle circle = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
public override bool Contains(Vector2 screenSpacePos) => circle.Contains(screenSpacePos);
public QuarterCircle(TaikoAction handledAction, Color4 colour)
public DrumSegment()
{
this.handledAction = handledAction;
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
new Container
@ -191,7 +260,6 @@ namespace osu.Game.Rulesets.Taiko.UI
circle = new Circle
{
RelativeSizeAxes = Axes.Both,
Colour = colour.Multiply(1.4f).Darken(2.8f),
Alpha = 0.8f,
Scale = new Vector2(2),
},
@ -200,7 +268,6 @@ namespace osu.Game.Rulesets.Taiko.UI
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
Colour = colour,
Scale = new Vector2(2),
}
}
@ -208,18 +275,52 @@ namespace osu.Game.Rulesets.Taiko.UI
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColoursFromAction();
}
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{
if (e.Action == handledAction)
if (e.Action == Action)
overlay.FadeTo(1f, 80, Easing.OutQuint);
return false;
}
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{
if (e.Action == handledAction)
if (e.Action == Action)
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();
}
}
}
}