Initial structure for new hitobject judgement system

This commit is contained in:
smoogipoo
2018-08-01 21:04:03 +09:00
parent 41512667a8
commit d51d0e8547
3 changed files with 44 additions and 28 deletions

View File

@ -28,19 +28,19 @@ namespace osu.Game.Rulesets.Judgements
/// </summary> /// </summary>
public int HighestComboAtJudgement; public int HighestComboAtJudgement;
/// <summary>
/// Whether this <see cref="Judgement"/> has a result.
/// </summary>
public bool HasResult => Result > HitResult.None;
/// <summary> /// <summary>
/// Whether a successful hit occurred. /// Whether a successful hit occurred.
/// </summary> /// </summary>
public bool IsHit => Result > HitResult.Miss; public bool IsHit => Result > HitResult.Miss;
/// <summary>
/// Whether this judgement is the final judgement for the hit object.
/// </summary>
public bool Final = true;
/// <summary> /// <summary>
/// The offset from a perfect hit at which this judgement occurred. /// The offset from a perfect hit at which this judgement occurred.
/// Populated when added via <see cref="DrawableHitObject{TObject}.AddJudgement"/>. /// Populated when added via <see cref="DrawableHitObject.ApplyJudgement"/>.
/// </summary> /// </summary>
public double TimeOffset { get; set; } public double TimeOffset { get; set; }

View File

@ -38,9 +38,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
public event Action<DrawableHitObject, Judgement> OnJudgement; public event Action<DrawableHitObject, Judgement> OnJudgement;
public event Action<DrawableHitObject, Judgement> OnJudgementRemoved; public event Action<DrawableHitObject, Judgement> OnJudgementRemoved;
public IReadOnlyList<Judgement> Judgements => judgements;
private readonly List<Judgement> judgements = new List<Judgement>();
/// <summary> /// <summary>
/// Whether a visible judgement should be displayed when this representation is hit. /// Whether a visible judgement should be displayed when this representation is hit.
/// </summary> /// </summary>
@ -49,20 +46,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit. /// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been hit.
/// </summary> /// </summary>
public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && NestedHitObjects.All(n => n.IsHit); public bool IsHit => HitObject.Judgements.All(j => j.IsHit) && NestedHitObjects.All(n => n.IsHit);
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged. /// Whether this <see cref="DrawableHitObject"/> and all of its nested <see cref="DrawableHitObject"/>s have been judged.
/// </summary> /// </summary>
public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && NestedHitObjects.All(h => h.AllJudged); public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
/// <summary> /// <summary>
/// Whether this <see cref="DrawableHitObject"/> can be judged. /// Whether this <see cref="DrawableHitObject"/> has been judged.
/// Note: This does NOT include nested hitobjects.
/// </summary> /// </summary>
protected virtual bool ProvidesJudgement => true; public bool Judged => HitObject.Judgements.All(h => h.HasResult);
private bool judgementOccurred; private bool judgementOccurred;
private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
public bool Interactive = true; public bool Interactive = true;
public override bool HandleKeyboardInput => Interactive; public override bool HandleKeyboardInput => Interactive;
@ -128,23 +125,31 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// </summary> /// </summary>
public void PlaySamples() => Samples?.Play(); public void PlaySamples() => Samples?.Play();
private double lastUpdateTime;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; if (lastUpdateTime > Time.Current)
while (judgements.Count > 0)
{ {
var lastJudgement = judgements[judgements.Count - 1]; var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
if (lastJudgement.TimeOffset + endTime <= Time.Current)
break;
judgements.RemoveAt(judgements.Count - 1); for (int i = HitObject.Judgements.Count - 1; i >= 0; i--)
State.Value = ArmedState.Idle; {
var judgement = HitObject.Judgements[i];
OnJudgementRemoved?.Invoke(this, lastJudgement); if (judgement.TimeOffset + endTime <= Time.Current)
break;
judgement.Result = HitResult.None;
State.Value = ArmedState.Idle;
OnJudgementRemoved?.Invoke(this, judgement);
}
} }
lastUpdateTime = Time.Current;
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()
@ -167,16 +172,17 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>. /// Notifies that a new judgement has occurred for this <see cref="DrawableHitObject"/>.
/// </summary> /// </summary>
/// <param name="judgement">The <see cref="Judgement"/>.</param> /// <param name="judgement">The <see cref="Judgement"/>.</param>
protected void AddJudgement(Judgement judgement) protected void ApplyJudgement<T>(T judgement, Action<T> application)
where T : Judgement
{ {
judgementOccurred = true; judgementOccurred = true;
application?.Invoke(judgement);
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller // Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
judgement.TimeOffset = Time.Current - endTime; judgement.TimeOffset = Time.Current - endTime;
judgements.Add(judgement);
switch (judgement.Result) switch (judgement.Result)
{ {
case HitResult.None: case HitResult.None:
@ -207,7 +213,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
foreach (var d in NestedHitObjects) foreach (var d in NestedHitObjects)
judgementOccurred |= d.UpdateJudgement(userTriggered); judgementOccurred |= d.UpdateJudgement(userTriggered);
if (!ProvidesJudgement || judgementFinalized || judgementOccurred) if (judgementOccurred || Judged)
return judgementOccurred; return judgementOccurred;
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
@ -218,7 +224,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// <summary> /// <summary>
/// Checks if any judgements have occurred for this <see cref="DrawableHitObject"/>. This method must construct /// Checks if any judgements have occurred for this <see cref="DrawableHitObject"/>. This method must construct
/// all <see cref="Judgement"/>s and notify of them through <see cref="AddJudgement"/>. /// all <see cref="Judgement"/>s and notify of them through <see cref="ApplyJudgement"/>.
/// </summary> /// </summary>
/// <param name="userTriggered">Whether the user triggered this check.</param> /// <param name="userTriggered">Whether the user triggered this check.</param>
/// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> &gt; 0 /// <param name="timeOffset">The offset from the <see cref="HitObject"/> end time at which this check occurred. A <paramref name="timeOffset"/> &gt; 0

View File

@ -3,12 +3,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Lists; using osu.Framework.Lists;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects namespace osu.Game.Rulesets.Objects
@ -62,6 +64,9 @@ namespace osu.Game.Rulesets.Objects
[JsonIgnore] [JsonIgnore]
public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects.Value; public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects.Value;
private readonly List<Judgement> judgements = new List<Judgement>();
public IReadOnlyList<Judgement> Judgements => judgements;
/// <summary> /// <summary>
/// Applies default values to this HitObject. /// Applies default values to this HitObject.
/// </summary> /// </summary>
@ -71,6 +76,9 @@ namespace osu.Game.Rulesets.Objects
{ {
ApplyDefaultsToSelf(controlPointInfo, difficulty); ApplyDefaultsToSelf(controlPointInfo, difficulty);
judgements.Clear();
judgements.AddRange(CreateJudgements());
if (nestedHitObjects.IsValueCreated) if (nestedHitObjects.IsValueCreated)
nestedHitObjects.Value.Clear(); nestedHitObjects.Value.Clear();
@ -103,6 +111,8 @@ namespace osu.Game.Rulesets.Objects
{ {
} }
protected virtual IEnumerable<Judgement> CreateJudgements() => Enumerable.Empty<Judgement>();
protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject); protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject);
/// <summary> /// <summary>