Merge branch 'master' into fix-sliders-ignoring-actions

This commit is contained in:
Dean Herbert 2017-08-19 00:30:40 +09:00 committed by GitHub
commit a8996b8b5f
23 changed files with 237 additions and 216 deletions

@ -1 +1 @@
Subproject commit 03b7608f210b35dbcd3a811bda002e7a9334d081
Subproject commit 825505e788c4f093b269c61b485d38d50cd68096

View File

@ -14,6 +14,9 @@ using osu.Game.Rulesets.Taiko.UI;
using OpenTK;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps;
using osu.Desktop.Tests.Beatmaps;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
namespace osu.Desktop.Tests.Visual
{
@ -27,7 +30,7 @@ namespace osu.Desktop.Tests.Visual
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
private readonly TaikoPlayfield playfield;
private readonly TaikoRulesetContainer rulesetContainer;
private readonly Container playfieldContainer;
public TestCaseTaikoPlayfield()
@ -51,6 +54,25 @@ namespace osu.Desktop.Tests.Visual
AddStep("Height test 5", () => changePlayfieldSize(5));
AddStep("Reset height", () => changePlayfieldSize(6));
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint());
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new CentreHit() },
BeatmapInfo = new BeatmapInfo
{
Difficulty = new BeatmapDifficulty(),
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
},
},
ControlPointInfo = controlPointInfo
});
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
@ -58,12 +80,9 @@ namespace osu.Desktop.Tests.Visual
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = TaikoPlayfield.DEFAULT_HEIGHT,
Height = 768,
Clock = new FramedClock(rateAdjustClock),
Children = new[]
{
playfield = new TaikoPlayfield()
}
Children = new[] { rulesetContainer = new TaikoRulesetContainer(null, beatmap, true) }
});
}
@ -128,18 +147,18 @@ namespace osu.Desktop.Tests.Visual
}
};
playfield.OnJudgement(h);
rulesetContainer.Playfield.OnJudgement(h);
if (RNG.Next(10) == 0)
{
h.Judgement.SecondHit = true;
playfield.OnJudgement(h);
rulesetContainer.Playfield.OnJudgement(h);
}
}
private void addMissJudgement()
{
playfield.OnJudgement(new DrawableTestHit(new Hit())
rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgement
{
@ -151,22 +170,17 @@ namespace osu.Desktop.Tests.Visual
private void addBarLine(bool major, double delay = scroll_time)
{
BarLine bl = new BarLine
{
StartTime = playfield.Time.Current + delay,
ScrollTime = scroll_time
};
BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay };
playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
}
private void addSwell(double duration = default_duration)
{
playfield.Add(new DrawableSwell(new Swell
rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
{
StartTime = playfield.Time.Current + scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
Duration = duration,
ScrollTime = scroll_time
}));
}
@ -177,43 +191,40 @@ namespace osu.Desktop.Tests.Visual
var d = new DrumRoll
{
StartTime = playfield.Time.Current + scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong,
Duration = duration,
ScrollTime = scroll_time,
};
playfield.Add(new DrawableDrumRoll(d));
rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
}
private void addCentreHit(bool strong)
{
Hit h = new Hit
{
StartTime = playfield.Time.Current + scroll_time,
ScrollTime = scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
playfield.Add(new DrawableCentreHitStrong(h));
rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
playfield.Add(new DrawableCentreHit(h));
rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
{
Hit h = new Hit
{
StartTime = playfield.Time.Current + scroll_time,
ScrollTime = scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
playfield.Add(new DrawableRimHitStrong(h));
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
playfield.Add(new DrawableRimHit(h));
rulesetContainer.Playfield.Add(new DrawableRimHit(h));
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement>

View File

@ -23,12 +23,12 @@ namespace osu.Game.Rulesets.Catch
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(Key.Z, CatchAction.MoveLeft),
new KeyBinding(Key.Left, CatchAction.MoveLeft),
new KeyBinding(Key.X, CatchAction.MoveRight),
new KeyBinding(Key.Right, CatchAction.MoveRight),
new KeyBinding(Key.LShift, CatchAction.Dash),
new KeyBinding(Key.RShift, CatchAction.Dash),
new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
new KeyBinding(InputKey.Left, CatchAction.MoveLeft),
new KeyBinding(InputKey.X, CatchAction.MoveRight),
new KeyBinding(InputKey.Right, CatchAction.MoveRight),
new KeyBinding(InputKey.Shift, CatchAction.Dash),
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
public override IEnumerable<Mod> GetModsFor(ModType type)

View File

@ -27,10 +27,10 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(Key.Z, OsuAction.LeftButton),
new KeyBinding(Key.X, OsuAction.RightButton),
new KeyBinding(Key.LastKey + 1, OsuAction.LeftButton),
new KeyBinding(Key.LastKey + 2, OsuAction.RightButton),
new KeyBinding(InputKey.Z, OsuAction.LeftButton),
new KeyBinding(InputKey.X, OsuAction.RightButton),
new KeyBinding(InputKey.LastKey + 1, OsuAction.LeftButton),
new KeyBinding(InputKey.LastKey + 2, OsuAction.RightButton),
};
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]

View File

@ -2,16 +2,17 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using OpenTK;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
/// <summary>
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
/// </summary>
public class DrawableBarLine : Container
public class DrawableBarLine : DrawableScrollingHitObject<TaikoHitObject, TaikoJudgement>
{
/// <summary>
/// The width of the line tracker.
@ -34,15 +35,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected readonly BarLine BarLine;
public DrawableBarLine(BarLine barLine)
: base(barLine)
{
BarLine = barLine;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Width = tracker_width;
Children = new[]
@ -56,24 +56,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Alpha = 0.75f
}
};
LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
}
protected override void LoadComplete()
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
base.LoadComplete();
this.Delay(BarLine.StartTime - Time.Current).FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
}
private void updateScrollPosition(double time) => this.MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
protected override void Update()
{
base.Update();
updateScrollPosition(Time.Current);
}
}
}

View File

@ -11,6 +11,7 @@ using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
@ -31,30 +32,29 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
RelativeSizeAxes = Axes.Y;
AutoSizeAxes = Axes.X;
Width = (float)HitObject.Duration;
Container<DrawableDrumRollTick> tickContainer;
MainPiece.Add(tickContainer = new Container<DrawableDrumRollTick>
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2((float)HitObject.StartTime, 0),
RelativeChildSize = new Vector2((float)HitObject.Duration, 1)
});
foreach (var tick in drumRoll.Ticks)
{
var newTick = new DrawableDrumRollTick(tick)
{
X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
};
var newTick = new DrawableDrumRollTick(tick);
newTick.OnJudgement += onTickJudgement;
AddNested(newTick);
MainPiece.Add(newTick);
tickContainer.Add(newTick);
}
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece
{
Length = (float)(HitObject.Duration / HitObject.ScrollTime),
PlayfieldLengthReference = () => Parent.DrawSize.X
};
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece();
[BackgroundDependencyLoader]
private void load(OsuColour colours)
@ -63,17 +63,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
accentDarkColour = colours.YellowDarker;
}
protected override void LoadComplete()
{
base.LoadComplete();
// This is naive, however it's based on the reasoning that the hit target
// is further than mid point of the play field, so the time taken to scroll in should always
// be greater than the time taken to scroll out to the left of the screen.
// Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime;
}
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj)
{
if (obj.Judgement.Result == HitResult.Hit)

View File

@ -15,9 +15,21 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public DrawableDrumRollTick(DrumRollTick tick)
: base(tick)
{
// Because ticks aren't added by the ScrollingPlayfield, we need to set the following properties ourselves
RelativePositionAxes = Axes.X;
X = (float)tick.StartTime;
FillMode = FillMode.Fit;
}
protected override void LoadComplete()
{
base.LoadComplete();
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
Width *= Parent.RelativeChildSize.X;
}
protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
@ -47,11 +59,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
protected override void UpdateScrollPosition(double time)
{
// Ticks don't move
}
protected override bool HandleKeyPress(Key key)
{
return Judgement.Result == HitResult.None && UpdateJudgement(true);

View File

@ -29,6 +29,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
protected override void LoadComplete()
{
base.LoadComplete();
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
Width *= Parent.RelativeChildSize.X;
}
protected override void CheckJudgement(bool userTriggered)
{
if (!userTriggered)

View File

@ -118,7 +118,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
MainPiece.Add(symbol = new SwellSymbolPiece());
}
[BackgroundDependencyLoader]
@ -129,6 +128,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
targetRing.BorderColour = colours.YellowDark.Opacity(0.25f);
}
protected override void LoadComplete()
{
base.LoadComplete();
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
Width *= Parent.RelativeChildSize.X;
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
@ -189,20 +196,22 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Expire();
}
protected override void UpdateScrollPosition(double time)
protected override void Update()
{
// Make the swell stop at the hit target
double t = Math.Min(HitObject.StartTime, time);
base.Update();
// Make the swell stop at the hit target
X = (float)Math.Max(Time.Current, HitObject.StartTime);
double t = Math.Min(HitObject.StartTime, Time.Current);
if (t == HitObject.StartTime && !hasStarted)
{
OnStart?.Invoke();
hasStarted = true;
}
base.UpdateScrollPosition(t);
}
protected override bool HandleKeyPress(Key key)
{
if (Judgement.Result != HitResult.None)

View File

@ -12,7 +12,7 @@ using OpenTK.Input;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject, TaikoJudgement>
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableScrollingHitObject<TaikoHitObject, TaikoJudgement>
where TaikoHitType : TaikoHitObject
{
/// <summary>
@ -38,30 +38,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
RelativeSizeAxes = Axes.Both;
Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
RelativePositionAxes = Axes.X;
Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
/// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime.
/// </summary>
/// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
protected override void Update()
{
UpdateScrollPosition(Time.Current);
}
protected virtual bool HandleKeyPress(Key key) => false;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)

View File

@ -1,23 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
/// <summary>
/// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing playfield container.
/// </summary>
public Func<float> PlayfieldLengthReference;
/// <summary>
/// The length of this piece as a multiple of the value returned by <see cref="PlayfieldLengthReference"/>
/// </summary>
public float Length;
public ElongatedCirclePiece()
{
RelativeSizeAxes = Axes.Y;
@ -35,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Right = padding,
};
Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
Width = Parent.DrawSize.X + DrawHeight;
}
}
}

View File

@ -80,7 +80,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
ret.Add(new DrumRollTick
{
FirstTick = first,
ScrollTime = ScrollTime,
TickSpacing = tickSpacing,
StartTime = t,
IsStrong = IsStrong,

View File

@ -24,16 +24,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
public const float DEFAULT_STRONG_SIZE = DEFAULT_SIZE * STRONG_SCALE;
/// <summary>
/// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a <see cref="TimingControlPoint.BeatLength"/> of 1000ms.
/// </summary>
private const double scroll_time = 6000;
/// <summary>
/// Our adjusted <see cref="scroll_time"/> taking into consideration local <see cref="TimingControlPoint.BeatLength"/> and other speed multipliers.
/// </summary>
public double ScrollTime;
/// <summary>
/// Whether this HitObject is a "strong" type.
/// Strong hit objects give more points for hitting the hit object with both keys.
@ -49,12 +39,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
base.ApplyDefaults(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
ScrollTime = scroll_time * (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier / 1000) / difficulty.SliderMultiplier;
Kiai |= effectPoint.KiaiMode;
}
}

View File

@ -18,7 +18,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.UI
{
public class TaikoPlayfield : Playfield<TaikoHitObject, TaikoJudgement>
public class TaikoPlayfield : ScrollingPlayfield<TaikoHitObject, TaikoJudgement>
{
/// <summary>
/// Default height of a <see cref="TaikoPlayfield"/> when inside a <see cref="TaikoRulesetContainer"/>.
@ -35,14 +35,14 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
private const float left_area_size = 240;
protected override Container<Drawable> Content => hitObjectContainer;
private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly Container<DrawableBarLine> barLineContainer;
private readonly Container<DrawableTaikoJudgement> judgementContainer;
private readonly Container hitObjectContainer;
protected override Container<Drawable> Content => content;
private readonly Container content;
private readonly Container topLevelHitContainer;
private readonly Container overlayBackgroundContainer;
@ -52,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Box background;
public TaikoPlayfield()
: base(Axes.X)
{
AddRangeInternal(new Drawable[]
{
@ -96,10 +97,6 @@ namespace osu.Game.Rulesets.Taiko.UI
FillMode = FillMode.Fit,
BlendingMode = BlendingMode.Additive,
},
barLineContainer = new Container<DrawableBarLine>
{
RelativeSizeAxes = Axes.Both,
},
new HitTarget
{
Anchor = Anchor.CentreLeft,
@ -107,7 +104,7 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit
},
hitObjectContainer = new Container
content = new Container
{
RelativeSizeAxes = Axes.Both,
},
@ -181,6 +178,8 @@ namespace osu.Game.Rulesets.Taiko.UI
RelativeSizeAxes = Axes.Both,
}
});
VisibleTimeRange.Value = 6000;
}
[BackgroundDependencyLoader]
@ -205,11 +204,6 @@ namespace osu.Game.Rulesets.Taiko.UI
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
}
public void AddBarLine(DrawableBarLine barLine)
{
barLineContainer.Add(barLine);
}
public override void OnJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> judgedObject)
{
bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
@ -230,7 +224,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!secondHit)
{
if (judgedObject.X >= -0.05f && !(judgedObject is DrawableSwell))
if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
{
// If we're far enough away from the left stage, we should bring outselves in front of it
topLevelHitContainer.Add(judgedObject.CreateProxy());

View File

@ -21,7 +21,7 @@ using System.Linq;
namespace osu.Game.Rulesets.Taiko.UI
{
public class TaikoRulesetContainer : RulesetContainer<TaikoHitObject, TaikoJudgement>
public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject, TaikoJudgement>
{
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@ -36,11 +36,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private void loadBarLines()
{
var taikoPlayfield = Playfield as TaikoPlayfield;
if (taikoPlayfield == null)
return;
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
@ -72,7 +67,7 @@ namespace osu.Game.Rulesets.Taiko.UI
barLine.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
double bl = currentPoint.BeatLength;
if (bl < 800)

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
@ -22,16 +21,16 @@ namespace osu.Game.Input.Bindings
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
{
new KeyBinding(Key.F8, GlobalAction.ToggleChat),
new KeyBinding(Key.F9, GlobalAction.ToggleSocial),
new KeyBinding(new[] { Key.LControl, Key.LAlt, Key.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { Key.LControl, Key.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { Key.LControl, Key.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { Key.LControl, Key.D }, GlobalAction.ToggleDirect),
new KeyBinding(InputKey.F8, GlobalAction.ToggleChat),
new KeyBinding(InputKey.F9, GlobalAction.ToggleSocial),
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect),
};
protected override IEnumerable<Drawable> GetKeyboardInputQueue() =>
handler == null ? base.GetKeyboardInputQueue() : new[] { handler }.Concat(base.GetKeyboardInputQueue());
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
handler == null ? base.KeyBindingInputQueue : new[] { handler }.Concat(base.KeyBindingInputQueue);
}
public enum GlobalAction

View File

@ -11,6 +11,7 @@ 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.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Input;
@ -123,46 +124,74 @@ namespace osu.Game.Overlays.KeyBinding
base.OnHoverLost(state);
}
public override bool AcceptsFocus => true;
public override bool AcceptsFocus => bindTarget == null;
private KeyButton bindTarget;
protected override void OnFocus(InputState state)
{
AutoSizeDuration = 500;
AutoSizeEasing = Easing.OutQuint;
pressAKey.FadeIn(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding();
base.OnFocus(state);
}
public bool AllowMainMouseButtons;
private bool isModifier(Key k) => k < Key.F1;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
switch (args.Key)
{
case Key.Escape:
GetContainingInputManager().ChangeFocus(null);
return true;
case Key.Delete:
bindTarget.UpdateKeyCombination(Key.Unknown);
store.Update(bindTarget.KeyBinding);
GetContainingInputManager().ChangeFocus(null);
return true;
}
protected override bool OnClick(InputState state) => true;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (HasFocus)
{
bindTarget.UpdateKeyCombination(state.Keyboard.Keys.ToArray());
if (!isModifier(args.Key))
if (bindTarget.IsHovered)
{
if (!AllowMainMouseButtons)
{
switch (args.Button)
{
case MouseButton.Left:
case MouseButton.Right:
return true;
}
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
return true;
}
}
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (HasFocus && !state.Mouse.Buttons.Any())
{
if (bindTarget.IsHovered)
finalise();
else
updateBindTarget();
return true;
}
return base.OnKeyDown(state, args);
return base.OnMouseUp(state, args);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!HasFocus)
return false;
switch (args.Key)
{
case Key.Escape:
finalise();
return true;
case Key.Delete:
bindTarget.UpdateKeyCombination(InputKey.None);
finalise();
return true;
}
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
if (!isModifier(args.Key)) finalise();
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
@ -178,27 +207,48 @@ namespace osu.Game.Overlays.KeyBinding
private void finalise()
{
store.Update(bindTarget.KeyBinding);
GetContainingInputManager().ChangeFocus(null);
if (bindTarget != null)
{
store.Update(bindTarget.KeyBinding);
bindTarget.IsBinding = false;
Schedule(() =>
{
// schedule to ensure we don't instantly get focus back on next OnMouseClick (see AcceptFocus impl.)
bindTarget = null;
});
}
if (HasFocus)
GetContainingInputManager().ChangeFocus(null);
pressAKey.FadeOut(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
}
protected override void OnFocus(InputState state)
{
AutoSizeDuration = 500;
AutoSizeEasing = Easing.OutQuint;
pressAKey.FadeIn(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding();
updateBindTarget();
base.OnFocus(state);
}
protected override void OnFocusLost(InputState state)
{
bindTarget.IsBinding = false;
bindTarget = null;
pressAKey.FadeOut(300, Easing.OutQuint);
pressAKey.Padding = new MarginPadding { Bottom = -pressAKey.DrawHeight };
finalise();
base.OnFocusLost(state);
}
protected override bool OnClick(InputState state)
private void updateBindTarget()
{
if (bindTarget != null) bindTarget.IsBinding = false;
bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault();
if (bindTarget != null) bindTarget.IsBinding = true;
return bindTarget != null;
}
private class KeyButton : Container
@ -296,7 +346,7 @@ namespace osu.Game.Overlays.KeyBinding
}
}
public void UpdateKeyCombination(params Key[] newCombination)
public void UpdateKeyCombination(KeyCombination newCombination)
{
KeyBinding.KeyCombination = newCombination;
Text.Text = KeyBinding.KeyCombination.ReadableString();

View File

@ -40,7 +40,10 @@ namespace osu.Game.Overlays.KeyBinding
foreach (Enum v in Enum.GetValues(enumType))
// one row per valid action.
Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v))));
Add(new KeyBindingRow(v, bindings.Where(b => b.Action.Equals((int)(object)v)))
{
AllowMainMouseButtons = Ruleset != null
});
}
}
}

View File

@ -32,6 +32,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
public override bool RemoveWhenNotAlive => false;
protected DrawableScrollingHitObject(TObject hitObject)
: base(hitObject)
{

View File

@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Timing
/// </summary>
internal Axes ScrollingAxes;
public override bool RemoveWhenNotAlive => false;
/// <summary>
/// The control point that defines the speed adjustments for this container. This is set by the <see cref="SpeedAdjustmentContainer"/>.
/// </summary>

View File

@ -34,6 +34,8 @@ namespace osu.Game.Rulesets.Timing
/// </summary>
public Axes ScrollingAxes { get; internal set; }
public override bool RemoveWhenNotAlive => false;
/// <summary>
/// The <see cref="MultiplierControlPoint"/> that defines the speed adjustments.
/// </summary>

View File

@ -7,6 +7,7 @@ using System.Linq;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Framework.MathUtils;
@ -154,7 +155,7 @@ namespace osu.Game.Rulesets.UI
/// Hit objects that are to be re-processed on the next update.
/// </summary>
private readonly List<DrawableHitObject> queuedHitObjects = new List<DrawableHitObject>();
private readonly List<SpeedAdjustmentContainer> speedAdjustments = new List<SpeedAdjustmentContainer>();
private readonly Container<SpeedAdjustmentContainer> speedAdjustments;
private readonly Axes scrollingAxes;
@ -165,6 +166,8 @@ namespace osu.Game.Rulesets.UI
public ScrollingHitObjectContainer(Axes scrollingAxes)
{
this.scrollingAxes = scrollingAxes;
AddInternal(speedAdjustments = new Container<SpeedAdjustmentContainer> { RelativeSizeAxes = Axes.Both });
}
/// <summary>
@ -176,9 +179,7 @@ namespace osu.Game.Rulesets.UI
speedAdjustment.ScrollingAxes = scrollingAxes;
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
speedAdjustment.Reversed.BindTo(Reversed);
speedAdjustments.Add(speedAdjustment);
AddInternal(speedAdjustment);
}
public override IEnumerable<DrawableHitObject> Objects => speedAdjustments.SelectMany(s => s.Children);
@ -210,11 +211,11 @@ namespace osu.Game.Rulesets.UI
var hitObject = queuedHitObjects[i];
var target = adjustmentContainerFor(hitObject);
if (target != null)
{
target.Add(hitObject);
queuedHitObjects.RemoveAt(i);
}
if (target == null)
continue;
target.Add(hitObject);
queuedHitObjects.RemoveAt(i);
}
}

View File

@ -370,6 +370,9 @@ namespace osu.Game.Screens.Select
if (!track.IsRunning)
{
// Ensure the track is added to the TrackManager, since it is removed after the player finishes the map.
// Using AddItemToList rather than AddItem so that it doesn't attempt to register adjustment dependencies more than once.
Game.Audio.Track.AddItemToList(track);
if (preview) track.Seek(Beatmap.Value.Metadata.PreviewTime);
track.Start();
}