1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 04:07:25 +08:00

Merge branch 'master' into fix-mouse-button-disable

This commit is contained in:
Dan Balasescu 2017-04-26 20:43:56 +09:00 committed by GitHub
commit a243729a54
6 changed files with 110 additions and 53 deletions

@ -1 +1 @@
Subproject commit cc50d1251b00876331691ea2ce7ed18174e4eded
Subproject commit dcbd7a0b6f536f6aadf13a720db40a1d76bf52e2

View File

@ -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);

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.UI
where TObject : HitObject
{
/// <summary>
/// The Beatmap
/// The Beatmap
/// </summary>
public Beatmap<TObject> Beatmap;

View File

@ -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)

View File

@ -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)

View File

@ -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);