1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-19 12:13:20 +08:00

Merge branch 'master' into editor-position-snap

This commit is contained in:
Dean Herbert 2020-05-21 15:47:20 +09:00
commit b1fd7da824
13 changed files with 106 additions and 65 deletions

View File

@ -12,6 +12,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Audio; using osu.Game.Rulesets.Taiko.Audio;
using osu.Game.Screens.Play;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Taiko.UI namespace osu.Game.Rulesets.Taiko.UI
@ -145,6 +146,9 @@ namespace osu.Game.Rulesets.Taiko.UI
centreHit.Colour = colours.Pink; centreHit.Colour = colours.Pink;
} }
[Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; }
public bool OnPressed(TaikoAction action) public bool OnPressed(TaikoAction action)
{ {
Drawable target = null; Drawable target = null;
@ -157,14 +161,16 @@ namespace osu.Game.Rulesets.Taiko.UI
target = centreHit; target = centreHit;
back = centre; back = centre;
drumSample.Centre?.Play(); if (gameplayClock?.IsSeeking != true)
drumSample.Centre?.Play();
} }
else if (action == RimAction) else if (action == RimAction)
{ {
target = rimHit; target = rimHit;
back = rim; back = rim;
drumSample.Rim?.Play(); if (gameplayClock?.IsSeeking != true)
drumSample.Rim?.Play();
} }
if (target != null) if (target != null)

View File

@ -26,7 +26,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
var storyboard = decoder.Decode(stream); var storyboard = decoder.Decode(stream);
Assert.IsTrue(storyboard.HasDrawable); Assert.IsTrue(storyboard.HasDrawable);
Assert.AreEqual(5, storyboard.Layers.Count()); Assert.AreEqual(6, storyboard.Layers.Count());
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
Assert.IsNotNull(background); Assert.IsNotNull(background);
@ -56,6 +56,13 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(foreground.VisibleWhenPassing); Assert.IsTrue(foreground.VisibleWhenPassing);
Assert.AreEqual("Foreground", foreground.Name); Assert.AreEqual("Foreground", foreground.Name);
StoryboardLayer overlay = storyboard.Layers.FirstOrDefault(l => l.Depth == int.MinValue);
Assert.IsNotNull(overlay);
Assert.IsEmpty(overlay.Elements);
Assert.IsTrue(overlay.VisibleWhenFailing);
Assert.IsTrue(overlay.VisibleWhenPassing);
Assert.AreEqual("Overlay", overlay.Name);
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation));
int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSampleInfo)); int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSampleInfo));

View File

@ -9,6 +9,7 @@ namespace osu.Game.Beatmaps.Legacy
Fail = 1, Fail = 1,
Pass = 2, Pass = 2,
Foreground = 3, Foreground = 3,
Video = 4 Overlay = 4,
Video = 5
} }
} }

View File

@ -254,7 +254,8 @@ namespace osu.Game.Rulesets.Edit
{ {
EditorBeatmap.Add(hitObject); EditorBeatmap.Add(hitObject);
adjustableClock.Seek(hitObject.GetEndTime()); if (adjustableClock.CurrentTime < hitObject.StartTime)
adjustableClock.Seek(hitObject.StartTime);
} }
showGridFor(Enumerable.Empty<HitObject>()); showGridFor(Enumerable.Empty<HitObject>());

View File

@ -11,13 +11,13 @@ using osu.Framework.Extensions.TypeExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Audio;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Screens.Play;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Rulesets.Objects.Drawables namespace osu.Game.Rulesets.Objects.Drawables
@ -96,8 +96,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </remarks> /// </remarks>
protected virtual float SamplePlaybackPosition => 0.5f; protected virtual float SamplePlaybackPosition => 0.5f;
private readonly BindableDouble balanceAdjust = new BindableDouble();
private BindableList<HitSampleInfo> samplesBindable; private BindableList<HitSampleInfo> samplesBindable;
private Bindable<double> startTimeBindable; private Bindable<double> startTimeBindable;
private Bindable<bool> userPositionalHitSounds; private Bindable<bool> userPositionalHitSounds;
@ -173,7 +171,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
} }
Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s))); Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
Samples.AddAdjustment(AdjustableProperty.Balance, balanceAdjust);
AddInternal(Samples); AddInternal(Samples);
} }
@ -352,6 +349,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
{ {
} }
[Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; }
/// <summary> /// <summary>
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>. /// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit. /// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
@ -360,8 +360,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
{ {
const float balance_adjust_amount = 0.4f; const float balance_adjust_amount = 0.4f;
balanceAdjust.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); if (Samples != null && gameplayClock?.IsSeeking != true)
Samples?.Play(); {
Samples.Balance.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0);
Samples.Play();
}
} }
protected override void Update() protected override void Update()

View File

@ -29,14 +29,16 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
internal bool FrameStablePlayback = true; internal bool FrameStablePlayback = true;
[Cached] public GameplayClock GameplayClock => stabilityGameplayClock;
public GameplayClock GameplayClock { get; }
[Cached(typeof(GameplayClock))]
private readonly StabilityGameplayClock stabilityGameplayClock;
public FrameStabilityContainer(double gameplayStartTime = double.MinValue) public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
GameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock())); stabilityGameplayClock = new StabilityGameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
this.gameplayStartTime = gameplayStartTime; this.gameplayStartTime = gameplayStartTime;
} }
@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.UI
{ {
if (clock != null) if (clock != null)
{ {
parentGameplayClock = clock; stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
GameplayClock.IsPaused.BindTo(clock.IsPaused); GameplayClock.IsPaused.BindTo(clock.IsPaused);
} }
} }
@ -187,5 +189,17 @@ namespace osu.Game.Rulesets.UI
} }
public ReplayInputHandler ReplayInputHandler { get; set; } public ReplayInputHandler ReplayInputHandler { get; set; }
private class StabilityGameplayClock : GameplayClock
{
public IFrameBasedClock ParentGameplayClock;
public StabilityGameplayClock(FramedClock underlyingClock)
: base(underlyingClock)
{
}
public override bool IsSeeking => ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200;
}
} }
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osu.Game.Storyboards.Drawables; using osu.Game.Storyboards.Drawables;
@ -13,6 +14,8 @@ namespace osu.Game.Screens.Play
/// </summary> /// </summary>
public class DimmableStoryboard : UserDimContainer public class DimmableStoryboard : UserDimContainer
{ {
public Container OverlayLayerContainer { get; private set; }
private readonly Storyboard storyboard; private readonly Storyboard storyboard;
private DrawableStoryboard drawableStoryboard; private DrawableStoryboard drawableStoryboard;
@ -24,6 +27,8 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Add(OverlayLayerContainer = new Container());
initializeStoryboard(false); initializeStoryboard(false);
} }
@ -46,9 +51,15 @@ namespace osu.Game.Screens.Play
drawableStoryboard = storyboard.CreateDrawable(); drawableStoryboard = storyboard.CreateDrawable();
if (async) if (async)
LoadComponentAsync(drawableStoryboard, Add); LoadComponentAsync(drawableStoryboard, onStoryboardCreated);
else else
Add(drawableStoryboard); onStoryboardCreated(drawableStoryboard);
}
private void onStoryboardCreated(DrawableStoryboard storyboard)
{
Add(storyboard);
OverlayLayerContainer.Add(storyboard.OverlayLayer.CreateProxy());
} }
} }
} }

View File

@ -31,6 +31,11 @@ namespace osu.Game.Screens.Play
public bool IsRunning => underlyingClock.IsRunning; public bool IsRunning => underlyingClock.IsRunning;
/// <summary>
/// Whether an ongoing seek operation is active.
/// </summary>
public virtual bool IsSeeking => false;
public void ProcessFrame() public void ProcessFrame()
{ {
// we do not want to process the underlying clock. // we do not want to process the underlying clock.

View File

@ -267,6 +267,7 @@ namespace osu.Game.Screens.Play
{ {
target.AddRange(new[] target.AddRange(new[]
{ {
DimmableStoryboard.OverlayLayerContainer.CreateProxy(),
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
{ {
Clock = DrawableRuleset.FrameStableClock, Clock = DrawableRuleset.FrameStableClock,

View File

@ -4,11 +4,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers;
using osu.Game.Audio; using osu.Game.Audio;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -17,25 +17,32 @@ namespace osu.Game.Skinning
{ {
private readonly ISampleInfo[] hitSamples; private readonly ISampleInfo[] hitSamples;
private List<(AdjustableProperty property, BindableDouble bindable)> adjustments;
private SampleChannel[] channels;
[Resolved] [Resolved]
private ISampleStore samples { get; set; } private ISampleStore samples { get; set; }
public SkinnableSound(ISampleInfo hitSamples)
: this(new[] { hitSamples })
{
}
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples) public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
{ {
this.hitSamples = hitSamples.ToArray(); this.hitSamples = hitSamples.ToArray();
} InternalChild = samplesContainer = new AudioContainer<DrawableSample>();
public SkinnableSound(ISampleInfo hitSamples)
{
this.hitSamples = new[] { hitSamples };
} }
private bool looping; private bool looping;
private readonly AudioContainer<DrawableSample> samplesContainer;
public BindableNumber<double> Volume => samplesContainer.Volume;
public BindableNumber<double> Balance => samplesContainer.Balance;
public BindableNumber<double> Frequency => samplesContainer.Frequency;
public BindableNumber<double> Tempo => samplesContainer.Tempo;
public bool Looping public bool Looping
{ {
get => looping; get => looping;
@ -45,33 +52,23 @@ namespace osu.Game.Skinning
looping = value; looping = value;
channels?.ForEach(c => c.Looping = looping); samplesContainer.ForEach(c => c.Looping = looping);
} }
} }
public void Play() => channels?.ForEach(c => c.Play()); public void Play() => samplesContainer.ForEach(c =>
public void Stop() => channels?.ForEach(c => c.Stop());
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
{ {
if (adjustments == null) adjustments = new List<(AdjustableProperty, BindableDouble)>(); if (c.AggregateVolume.Value > 0)
c.Play();
});
adjustments.Add((type, adjustBindable)); public void Stop() => samplesContainer.ForEach(c => c.Stop());
channels?.ForEach(c => c.AddAdjustment(type, adjustBindable));
}
public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
{
adjustments?.Remove((type, adjustBindable));
channels?.ForEach(c => c.RemoveAdjustment(type, adjustBindable));
}
public override bool IsPresent => Scheduler.HasPendingTasks; public override bool IsPresent => Scheduler.HasPendingTasks;
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {
channels = hitSamples.Select(s => var channels = hitSamples.Select(s =>
{ {
var ch = skin.GetSample(s); var ch = skin.GetSample(s);
@ -88,27 +85,12 @@ namespace osu.Game.Skinning
{ {
ch.Looping = looping; ch.Looping = looping;
ch.Volume.Value = s.Volume / 100.0; ch.Volume.Value = s.Volume / 100.0;
if (adjustments != null)
{
foreach (var (property, bindable) in adjustments)
ch.AddAdjustment(property, bindable);
}
} }
return ch; return ch;
}).Where(c => c != null).ToArray(); }).Where(c => c != null);
}
protected override void Dispose(bool isDisposing) samplesContainer.ChildrenEnumerable = channels.Select(c => new DrawableSample(c));
{
base.Dispose(isDisposing);
if (channels != null)
{
foreach (var c in channels)
c.Dispose();
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using System.Threading; using System.Threading;
using osuTK; using osuTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -72,6 +73,8 @@ namespace osu.Game.Storyboards.Drawables
} }
} }
public DrawableStoryboardLayer OverlayLayer => Children.Single(layer => layer.Name == "Overlay");
private void updateLayerVisibility() private void updateLayerVisibility()
{ {
foreach (var layer in Children) foreach (var layer in Children)

View File

@ -19,19 +19,26 @@ namespace osu.Game.Storyboards
public double FirstEventTime => Layers.Min(l => l.Elements.FirstOrDefault()?.StartTime ?? 0); public double FirstEventTime => Layers.Min(l => l.Elements.FirstOrDefault()?.StartTime ?? 0);
/// <summary>
/// Depth of the currently front-most storyboard layer, excluding the overlay layer.
/// </summary>
private int minimumLayerDepth;
public Storyboard() public Storyboard()
{ {
layers.Add("Video", new StoryboardLayer("Video", 4, false)); layers.Add("Video", new StoryboardLayer("Video", 4, false));
layers.Add("Background", new StoryboardLayer("Background", 3)); layers.Add("Background", new StoryboardLayer("Background", 3));
layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, }); layers.Add("Fail", new StoryboardLayer("Fail", 2) { VisibleWhenPassing = false, });
layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, }); layers.Add("Pass", new StoryboardLayer("Pass", 1) { VisibleWhenFailing = false, });
layers.Add("Foreground", new StoryboardLayer("Foreground", 0)); layers.Add("Foreground", new StoryboardLayer("Foreground", minimumLayerDepth = 0));
layers.Add("Overlay", new StoryboardLayer("Overlay", int.MinValue));
} }
public StoryboardLayer GetLayer(string name) public StoryboardLayer GetLayer(string name)
{ {
if (!layers.TryGetValue(name, out var layer)) if (!layers.TryGetValue(name, out var layer))
layers[name] = layer = new StoryboardLayer(name, layers.Values.Min(l => l.Depth) - 1); layers[name] = layer = new StoryboardLayer(name, --minimumLayerDepth);
return layer; return layer;
} }

View File

@ -33,6 +33,6 @@ namespace osu.Game.Storyboards
} }
public DrawableStoryboardLayer CreateDrawable() public DrawableStoryboardLayer CreateDrawable()
=> new DrawableStoryboardLayer(this) { Depth = Depth, }; => new DrawableStoryboardLayer(this) { Depth = Depth, Name = Name };
} }
} }