1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 01:52:55 +08:00

Merge pull request #10831 from bdach/hold-note-fade

Add back fade effect to legacy hold notes
This commit is contained in:
Dan Balasescu 2020-11-17 15:27:10 +09:00 committed by GitHub
commit bcf6974e18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 12 deletions

View File

@ -1,7 +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 osu.Framework.Allocation; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
/// </summary> /// </summary>
public abstract class ManiaHitObjectTestScene : ManiaSkinnableTestScene public abstract class ManiaHitObjectTestScene : ManiaSkinnableTestScene
{ {
[BackgroundDependencyLoader] [SetUp]
private void load() public void SetUp() => Schedule(() =>
{ {
SetContents(() => new FillFlowContainer SetContents(() => new FillFlowContainer
{ {
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
}, },
} }
}); });
} });
protected abstract DrawableManiaHitObject CreateHitObject(); protected abstract DrawableManiaHitObject CreateHitObject();
} }

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.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -26,6 +27,18 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
}); });
} }
[Test]
public void TestFadeOnMiss()
{
AddStep("miss tick", () =>
{
foreach (var holdNote in holdNotes)
holdNote.ChildrenOfType<DrawableHoldNoteHead>().First().MissForcefully();
});
}
private IEnumerable<DrawableHoldNote> holdNotes => CreatedDrawables.SelectMany(d => d.ChildrenOfType<DrawableHoldNote>());
protected override DrawableManiaHitObject CreateHitObject() protected override DrawableManiaHitObject CreateHitObject()
{ {
var note = new HoldNote { Duration = 1000 }; var note = new HoldNote { Duration = 1000 };

View File

@ -51,9 +51,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public double? HoldStartTime { get; private set; } public double? HoldStartTime { get; private set; }
/// <summary> /// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release. /// Time at which the hold note has been broken, i.e. released too early, resulting in a reduced score.
/// </summary> /// </summary>
public bool HasBroken { get; private set; } public double? HoldBrokenTime { get; private set; }
/// <summary> /// <summary>
/// Whether the hold note has been released potentially without having caused a break. /// Whether the hold note has been released potentially without having caused a break.
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
} }
if (Tail.Judged && !Tail.IsHit) if (Tail.Judged && !Tail.IsHit)
HasBroken = true; HoldBrokenTime = Time.Current;
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(ManiaAction action)
@ -298,7 +298,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// If the key has been released too early, the user should not receive full score for the release // If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit) if (!Tail.IsHit)
HasBroken = true; HoldBrokenTime = Time.Current;
releaseTime = Time.Current; releaseTime = Time.Current;
} }

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
ApplyResult(r => ApplyResult(r =>
{ {
// If the head wasn't hit or the hold note was broken, cap the max score to Meh. // If the head wasn't hit or the hold note was broken, cap the max score to Meh.
if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HasBroken)) if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HoldBrokenTime != null))
result = HitResult.Meh; result = HitResult.Meh;
r.Type = result; r.Type = result;

View File

@ -18,9 +18,17 @@ namespace osu.Game.Rulesets.Mania.Skinning
{ {
public class LegacyBodyPiece : LegacyManiaColumnElement public class LegacyBodyPiece : LegacyManiaColumnElement
{ {
private DrawableHoldNote holdNote;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly IBindable<bool> isHitting = new Bindable<bool>(); private readonly IBindable<bool> isHitting = new Bindable<bool>();
/// <summary>
/// Stores the start time of the fade animation that plays when any of the nested
/// hitobjects of the hold note are missed.
/// </summary>
private readonly Bindable<double?> missFadeTime = new Bindable<double?>();
[CanBeNull] [CanBeNull]
private Drawable bodySprite; private Drawable bodySprite;
@ -38,6 +46,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin, IScrollingInfo scrollingInfo, DrawableHitObject drawableObject) private void load(ISkinSource skin, IScrollingInfo scrollingInfo, DrawableHitObject drawableObject)
{ {
holdNote = (DrawableHoldNote)drawableObject;
string imageName = GetColumnSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage)?.Value string imageName = GetColumnSkinConfig<string>(skin, LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage)?.Value
?? $"mania-note{FallbackColumnIndex}L"; ?? $"mania-note{FallbackColumnIndex}L";
@ -92,11 +102,26 @@ namespace osu.Game.Rulesets.Mania.Skinning
InternalChild = bodySprite; InternalChild = bodySprite;
direction.BindTo(scrollingInfo.Direction); direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
var holdNote = (DrawableHoldNote)drawableObject;
isHitting.BindTo(holdNote.IsHitting); isHitting.BindTo(holdNote.IsHitting);
}
protected override void LoadComplete()
{
base.LoadComplete();
direction.BindValueChanged(onDirectionChanged, true);
isHitting.BindValueChanged(onIsHittingChanged, true); isHitting.BindValueChanged(onIsHittingChanged, true);
missFadeTime.BindValueChanged(onMissFadeTimeChanged, true);
holdNote.ApplyCustomUpdateState += applyCustomUpdateState;
applyCustomUpdateState(holdNote, holdNote.State.Value);
}
private void applyCustomUpdateState(DrawableHitObject hitObject, ArmedState state)
{
// ensure that the hold note is also faded out when the head/tail/any tick is missed.
if (state == ArmedState.Miss)
missFadeTime.Value ??= hitObject.HitStateUpdateTime;
} }
private void onIsHittingChanged(ValueChangedEvent<bool> isHitting) private void onIsHittingChanged(ValueChangedEvent<bool> isHitting)
@ -158,10 +183,38 @@ namespace osu.Game.Rulesets.Mania.Skinning
} }
} }
private void onMissFadeTimeChanged(ValueChangedEvent<double?> missFadeTimeChange)
{
if (missFadeTimeChange.NewValue == null)
return;
// this update could come from any nested object of the hold note (or even from an input).
// make sure the transforms are consistent across all affected parts.
using (BeginAbsoluteSequence(missFadeTimeChange.NewValue.Value))
{
// colour and duration matches stable
// transforms not applied to entire hold note in order to not affect hit lighting
const double fade_duration = 60;
holdNote.Head.FadeColour(Colour4.DarkGray, fade_duration);
holdNote.Tail.FadeColour(Colour4.DarkGray, fade_duration);
bodySprite?.FadeColour(Colour4.DarkGray, fade_duration);
}
}
protected override void Update()
{
base.Update();
missFadeTime.Value ??= holdNote.HoldBrokenTime;
}
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (holdNote != null)
holdNote.ApplyCustomUpdateState -= applyCustomUpdateState;
lightContainer?.Expire(); lightContainer?.Expire();
} }
} }