mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 17:52:56 +08:00
Merge branch 'master' into fix-mouse-button-disable
This commit is contained in:
commit
a243729a54
@ -1 +1 @@
|
||||
Subproject commit cc50d1251b00876331691ea2ce7ed18174e4eded
|
||||
Subproject commit dcbd7a0b6f536f6aadf13a720db40a1d76bf52e2
|
@ -38,9 +38,9 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
Origin = Anchor.TopLeft,
|
||||
});
|
||||
|
||||
AddStep("Toggle Bar", progress.ToggleBar);
|
||||
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
|
||||
AddWaitStep(5);
|
||||
AddStep("Toggle Bar", progress.ToggleBar);
|
||||
AddStep("Toggle Bar", () => progress.AllowSeeking = !progress.AllowSeeking);
|
||||
AddWaitStep(2);
|
||||
AddRepeatStep("New Values", displayNewValues, 5);
|
||||
|
||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.UI
|
||||
where TObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// The Beatmap
|
||||
/// The Beatmap
|
||||
/// </summary>
|
||||
public Beatmap<TObject> Beatmap;
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public Action RestartRequested;
|
||||
|
||||
public bool IsPaused => !interpolatedSourceClock.IsRunning;
|
||||
public bool IsPaused => !decoupledClock.IsRunning;
|
||||
|
||||
internal override bool AllowRulesetChange => false;
|
||||
|
||||
@ -51,9 +51,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown;
|
||||
|
||||
private IAdjustableClock sourceClock;
|
||||
private OffsetClock offsetClock;
|
||||
private IFrameBasedClock interpolatedSourceClock;
|
||||
private IAdjustableClock adjustableSourceClock;
|
||||
private FramedOffsetClock offsetClock;
|
||||
private DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
@ -70,6 +70,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private SkipButton skipButton;
|
||||
|
||||
private Container hitRendererContainer;
|
||||
|
||||
private HudOverlay hudOverlay;
|
||||
private PauseOverlay pauseOverlay;
|
||||
private FailOverlay failOverlay;
|
||||
@ -87,10 +89,7 @@ namespace osu.Game.Screens.Play
|
||||
if (Beatmap == null)
|
||||
Beatmap = beatmaps.GetWorkingBeatmap(BeatmapInfo, withStoryboard: true);
|
||||
|
||||
if ((Beatmap?.Beatmap?.HitObjects.Count ?? 0) == 0)
|
||||
throw new Exception("No valid objects were found!");
|
||||
|
||||
if (Beatmap == null)
|
||||
if (Beatmap?.Beatmap == null)
|
||||
throw new Exception("Beatmap was not loaded");
|
||||
|
||||
ruleset = osu?.Ruleset.Value ?? Beatmap.BeatmapInfo.Ruleset;
|
||||
@ -108,6 +107,9 @@ namespace osu.Game.Screens.Play
|
||||
rulesetInstance = ruleset.CreateInstance();
|
||||
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap);
|
||||
}
|
||||
|
||||
if (!HitRenderer.Objects.Any())
|
||||
throw new Exception("Beatmap contains no hit objects!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -123,23 +125,31 @@ namespace osu.Game.Screens.Play
|
||||
if (track != null)
|
||||
{
|
||||
audio.Track.SetExclusive(track);
|
||||
sourceClock = track;
|
||||
adjustableSourceClock = track;
|
||||
}
|
||||
|
||||
sourceClock = (IAdjustableClock)track ?? new StopwatchClock();
|
||||
offsetClock = new OffsetClock(sourceClock);
|
||||
adjustableSourceClock = (IAdjustableClock)track ?? new StopwatchClock();
|
||||
|
||||
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
var firstObjectTime = HitRenderer.Objects.First().StartTime;
|
||||
decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.TimingInfo.BeatLengthAt(firstObjectTime) * 4, Beatmap.BeatmapInfo.AudioLeadIn)));
|
||||
decoupledClock.ProcessFrame();
|
||||
|
||||
offsetClock = new FramedOffsetClock(decoupledClock);
|
||||
|
||||
userAudioOffset = config.GetBindable<double>(OsuConfig.AudioOffset);
|
||||
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
|
||||
userAudioOffset.TriggerChange();
|
||||
|
||||
interpolatedSourceClock = new InterpolatingFramedClock(offsetClock);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
sourceClock.Reset();
|
||||
adjustableSourceClock.Reset();
|
||||
|
||||
foreach (var mod in Beatmap.Mods.Value.OfType<IApplicableToClock>())
|
||||
mod.ApplyToClock(sourceClock);
|
||||
mod.ApplyToClock(adjustableSourceClock);
|
||||
|
||||
decoupledClock.ChangeSource(adjustableSourceClock);
|
||||
});
|
||||
|
||||
scoreProcessor = HitRenderer.CreateScoreProcessor();
|
||||
@ -155,7 +165,9 @@ namespace osu.Game.Screens.Play
|
||||
hudOverlay.BindHitRenderer(HitRenderer);
|
||||
|
||||
hudOverlay.Progress.Objects = HitRenderer.Objects;
|
||||
hudOverlay.Progress.AudioClock = interpolatedSourceClock;
|
||||
hudOverlay.Progress.AudioClock = decoupledClock;
|
||||
hudOverlay.Progress.AllowSeeking = HitRenderer.HasReplayLoaded;
|
||||
hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos);
|
||||
|
||||
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
|
||||
HitRenderer.OnAllJudged += onCompletion;
|
||||
@ -165,16 +177,20 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
hitRendererContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = interpolatedSourceClock,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
HitRenderer,
|
||||
skipButton = new SkipButton
|
||||
new Container
|
||||
{
|
||||
Alpha = 0
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = offsetClock,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
HitRenderer,
|
||||
skipButton = new SkipButton { Alpha = 0 },
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -224,7 +240,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
skipButton.Action = () =>
|
||||
{
|
||||
sourceClock.Seek(firstHitObject - skip_required_cutoff - fade_time);
|
||||
decoupledClock.Seek(firstHitObject - skip_required_cutoff - fade_time);
|
||||
skipButton.Action = null;
|
||||
};
|
||||
|
||||
@ -241,7 +257,7 @@ namespace osu.Game.Screens.Play
|
||||
// we want to wait for the source clock to stop so we can be sure all components are in a stable state.
|
||||
if (!IsPaused)
|
||||
{
|
||||
sourceClock.Stop();
|
||||
decoupledClock.Stop();
|
||||
|
||||
Schedule(() => Pause(force));
|
||||
return;
|
||||
@ -268,7 +284,7 @@ namespace osu.Game.Screens.Play
|
||||
hudOverlay.KeyCounter.IsCounting = true;
|
||||
hudOverlay.Progress.Hide();
|
||||
pauseOverlay.Hide();
|
||||
sourceClock.Start();
|
||||
decoupledClock.Start();
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
@ -304,7 +320,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void onFail()
|
||||
{
|
||||
sourceClock.Stop();
|
||||
decoupledClock.Stop();
|
||||
|
||||
HasFailed = true;
|
||||
failOverlay.Retries = RestartCount;
|
||||
@ -332,13 +348,12 @@ namespace osu.Game.Screens.Play
|
||||
Delay(750);
|
||||
Schedule(() =>
|
||||
{
|
||||
sourceClock.Start();
|
||||
decoupledClock.Start();
|
||||
initializeSkipButton();
|
||||
});
|
||||
|
||||
//keep in mind this is using the interpolatedSourceClock so won't be run as early as we may expect.
|
||||
HitRenderer.Alpha = 0;
|
||||
HitRenderer.FadeIn(750, EasingTypes.OutQuint);
|
||||
hitRendererContainer.Alpha = 0;
|
||||
hitRendererContainer.FadeIn(750, EasingTypes.OutQuint);
|
||||
}
|
||||
|
||||
protected override void OnSuspending(Screen next)
|
||||
|
@ -51,6 +51,38 @@ namespace osu.Game.Screens.Play
|
||||
Clock = new FramedClock(clock);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether we running up-to-date with our parent clock.
|
||||
/// If not, we will need to keep processing children until we catch up.
|
||||
/// </summary>
|
||||
private bool requireMoreUpdateLoops;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we in a valid state (ie. should we keep processing children frames).
|
||||
/// This should be set to false when the replay is, for instance, waiting for future frames to arrive.
|
||||
/// </summary>
|
||||
private bool validState;
|
||||
|
||||
protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && validState;
|
||||
|
||||
private bool isAttached => replayInputHandler != null && !UseParentState;
|
||||
|
||||
private const int max_catch_up_updates_per_frame = 50;
|
||||
|
||||
public override bool UpdateSubTree()
|
||||
{
|
||||
requireMoreUpdateLoops = true;
|
||||
validState = true;
|
||||
|
||||
int loops = 0;
|
||||
|
||||
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
|
||||
if (!base.UpdateSubTree())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (parentClock == null) return;
|
||||
@ -58,28 +90,26 @@ namespace osu.Game.Screens.Play
|
||||
clock.Rate = parentClock.Rate;
|
||||
clock.IsRunning = parentClock.IsRunning;
|
||||
|
||||
//if a replayHandler is not attached, we should just pass-through.
|
||||
if (UseParentState || replayInputHandler == null)
|
||||
if (!isAttached)
|
||||
{
|
||||
clock.CurrentTime = parentClock.CurrentTime;
|
||||
base.Update();
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
else
|
||||
{
|
||||
double? newTime = replayInputHandler.SetFrameFromTime(parentClock.CurrentTime);
|
||||
|
||||
if (newTime == null)
|
||||
//we shouldn't execute for this time value
|
||||
break;
|
||||
|
||||
if (clock.CurrentTime == parentClock.CurrentTime)
|
||||
break;
|
||||
{
|
||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
||||
validState = false;
|
||||
return;
|
||||
}
|
||||
|
||||
clock.CurrentTime = newTime.Value;
|
||||
base.Update();
|
||||
}
|
||||
|
||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||
base.Update();
|
||||
}
|
||||
|
||||
protected override void TransformState(InputState state)
|
||||
|
@ -35,6 +35,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private double lastHitTime => ((objects.Last() as IHasEndTime)?.EndTime ?? objects.Last().StartTime) + 1;
|
||||
|
||||
private double firstHitTime => objects.First().StartTime;
|
||||
|
||||
private IEnumerable<HitObject> objects;
|
||||
|
||||
public IEnumerable<HitObject> Objects
|
||||
@ -75,7 +77,7 @@ namespace osu.Game.Screens.Play
|
||||
Origin = Anchor.BottomLeft,
|
||||
SeekRequested = delegate (float position)
|
||||
{
|
||||
OnSeek?.Invoke(position);
|
||||
OnSeek?.Invoke(firstHitTime + position * (lastHitTime - firstHitTime));
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -86,18 +88,28 @@ namespace osu.Game.Screens.Play
|
||||
State = Visibility.Visible;
|
||||
}
|
||||
|
||||
private bool barVisible;
|
||||
private bool allowSeeking;
|
||||
|
||||
public void ToggleBar()
|
||||
public bool AllowSeeking
|
||||
{
|
||||
barVisible = !barVisible;
|
||||
updateBarVisibility();
|
||||
get
|
||||
{
|
||||
return allowSeeking;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (allowSeeking == value) return;
|
||||
|
||||
allowSeeking = value;
|
||||
updateBarVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBarVisibility()
|
||||
{
|
||||
bar.FadeTo(barVisible ? 1 : 0, transition_duration, EasingTypes.In);
|
||||
MoveTo(new Vector2(0, barVisible ? 0 : bottom_bar_height), transition_duration, EasingTypes.In);
|
||||
bar.FadeTo(allowSeeking ? 1 : 0, transition_duration, EasingTypes.In);
|
||||
MoveTo(new Vector2(0, allowSeeking ? 0 : bottom_bar_height), transition_duration, EasingTypes.In);
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
@ -118,7 +130,7 @@ namespace osu.Game.Screens.Play
|
||||
if (objects == null)
|
||||
return;
|
||||
|
||||
double progress = (AudioClock?.CurrentTime ?? Time.Current) / lastHitTime;
|
||||
double progress = ((AudioClock?.CurrentTime ?? Time.Current) - firstHitTime) / lastHitTime;
|
||||
|
||||
bar.UpdatePosition((float)progress);
|
||||
graph.Progress = (int)(graph.ColumnCount * progress);
|
||||
|
Loading…
Reference in New Issue
Block a user