1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-13 07:43:00 +08:00

Make object on the catcher plate separate CaughtObject class

This commit is contained in:
ekrctb 2020-12-08 20:34:08 +09:00
parent ed50fd445e
commit c301223d8c
10 changed files with 141 additions and 64 deletions

View File

@ -275,7 +275,7 @@ namespace osu.Game.Rulesets.Catch.Tests
public class TestCatcher : Catcher
{
public IEnumerable<DrawablePalpableCatchHitObject> CaughtObjects => this.ChildrenOfType<DrawablePalpableCatchHitObject>();
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
public TestCatcher(Container trailsTarget, Container droppedObjectTarget, BeatmapDifficulty difficulty)
: base(trailsTarget, droppedObjectTarget, difficulty)

View File

@ -0,0 +1,83 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Skinning.Default;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
[Cached(typeof(CaughtObject))]
public abstract class CaughtObject : SkinnableDrawable
{
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
public CatchHitObject HitObject { get; private set; }
/// <summary>
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
/// </summary>
public virtual bool StaysOnPlate => true;
public override bool RemoveWhenNotAlive => true;
protected CaughtObject(CatchSkinComponents skinComponent, Func<ISkinComponent, Drawable> defaultImplementation)
: base(new CatchSkinComponent(skinComponent), defaultImplementation)
{
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.None;
Size = new Vector2(CatchHitObject.OBJECT_RADIUS * 2);
}
public virtual void CopyFrom(DrawablePalpableCatchHitObject drawableObject)
{
HitObject = drawableObject.HitObject;
Scale = drawableObject.Scale / 2;
Rotation = drawableObject.Rotation;
AccentColour.Value = drawableObject.AccentColour.Value;
}
}
public class CaughtFruit : CaughtObject
{
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
public CaughtFruit()
: base(CatchSkinComponents.Fruit, _ => new FruitPiece())
{
}
public override void CopyFrom(DrawablePalpableCatchHitObject drawableObject)
{
base.CopyFrom(drawableObject);
var drawableFruit = (DrawableFruit)drawableObject;
VisualRepresentation.Value = drawableFruit.VisualRepresentation.Value;
}
}
public class CaughtBanana : CaughtObject
{
public CaughtBanana()
: base(CatchSkinComponents.Banana, _ => new BananaPiece())
{
}
}
public class CaughtDroplet : CaughtObject
{
public override bool StaysOnPlate => false;
public CaughtDroplet()
: base(CatchSkinComponents.Droplet, _ => new DropletPiece())
{
}
}
}

View File

@ -50,10 +50,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
public Func<CatchHitObject, bool> CheckPosition;
public bool IsOnPlate;
public override bool RemoveWhenNotAlive => IsOnPlate;
protected override JudgementResult CreateResult(Judgement judgement) => new CatchJudgementResult(HitObject, judgement);
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -11,8 +11,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
public class DrawableDroplet : DrawablePalpableCatchHitObject
{
public override bool StaysOnPlate => false;
public DrawableDroplet()
: this(null)
{

View File

@ -24,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
/// </summary>
protected virtual float ScaleFactor => 1;
/// <summary>
/// Whether this hit object should stay on the catcher plate when the object is caught by the catcher.
/// </summary>
public virtual bool StaysOnPlate => true;
public float DisplayRadius => CatchHitObject.OBJECT_RADIUS * HitObject.Scale * ScaleFactor;
protected DrawablePalpableCatchHitObject([CanBeNull] CatchHitObject h)
@ -43,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
{
XBindable.BindValueChanged(x =>
{
if (!IsOnPlate) X = x.NewValue;
X = x.NewValue;
}, true);
ScaleBindable.BindValueChanged(scale =>

View File

@ -21,6 +21,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
[CanBeNull]
protected DrawableHitObject DrawableHitObject { get; private set; }
[Resolved(canBeNull: true)]
[CanBeNull]
protected CaughtObject CaughtObject { get; private set; }
/// <summary>
/// A part of this piece that will be faded out while falling in the playfield.
/// </summary>
@ -45,6 +49,9 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
HyperDash.BindTo(hitObject.HyperDash);
}
if (CaughtObject != null)
AccentColour.BindTo(CaughtObject.AccentColour);
HyperDash.BindValueChanged(hyper =>
{
if (HyperBorderPiece != null)
@ -54,8 +61,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
protected override void Update()
{
if (BorderPiece != null && DrawableHitObject?.HitObject != null)
BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1);
if (BorderPiece != null)
{
if (DrawableHitObject?.HitObject != null)
BorderPiece.Alpha = (float)Math.Clamp((DrawableHitObject.HitObject.StartTime - Time.Current) / 500, 0, 1);
else
BorderPiece.Alpha = 0;
}
}
}
}

View File

@ -43,6 +43,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
if (fruit != null)
VisualRepresentation.BindTo(fruit.VisualRepresentation);
var caughtFruit = (CaughtFruit)CaughtObject;
if (caughtFruit != null)
VisualRepresentation.BindTo(caughtFruit.VisualRepresentation);
}
}
}

View File

@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
if (fruit != null)
VisualRepresentation.BindTo(fruit.VisualRepresentation);
var caughtFruit = (CaughtFruit)CaughtObject;
if (caughtFruit != null)
VisualRepresentation.BindTo(caughtFruit.VisualRepresentation);
VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true);
}

View File

@ -33,6 +33,10 @@ namespace osu.Game.Rulesets.Catch.Skinning
[CanBeNull]
protected DrawableHitObject DrawableHitObject { get; private set; }
[Resolved(canBeNull: true)]
[CanBeNull]
protected CaughtObject CaughtObject { get; private set; }
protected LegacyCatchHitObjectPiece()
{
RelativeSizeAxes = Axes.Both;
@ -73,6 +77,9 @@ namespace osu.Game.Rulesets.Catch.Skinning
HyperDash.BindTo(hitObject.HyperDash);
}
if (CaughtObject != null)
AccentColour.BindTo(CaughtObject.AccentColour);
hyperSprite.Colour = Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value ??
Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
Catcher.DEFAULT_HYPER_DASH_COLOUR;

View File

@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly Container droppedObjectTarget;
private readonly Container<DrawablePalpableCatchHitObject> caughtFruitContainer;
private readonly Container<CaughtObject> caughtFruitContainer;
public CatcherAnimationState CurrentState { get; private set; }
@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI
InternalChildren = new Drawable[]
{
hitExplosionPool = new DrawablePool<HitExplosion>(10),
caughtFruitContainer = new Container<DrawablePalpableCatchHitObject>
caughtFruitContainer = new Container<CaughtObject>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
var positionInStack = computePositionInStack(new Vector2(palpableObject.X - X, 0), palpableObject.DisplayRadius);
placeCaughtObject(hitObject, positionInStack);
placeCaughtObject(palpableObject, positionInStack);
if (hitLighting.Value)
addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
@ -450,21 +450,14 @@ namespace osu.Game.Rulesets.Catch.UI
updateCatcher();
}
private void placeCaughtObject(PalpableCatchHitObject source, Vector2 position)
private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position)
{
var caughtObject = createCaughtObject(source);
var caughtObject = createCaughtObject(drawableObject.HitObject);
if (caughtObject == null) return;
caughtObject.RelativePositionAxes = Axes.None;
caughtObject.CopyFrom(drawableObject);
caughtObject.Position = position;
caughtObject.IsOnPlate = true;
caughtObject.Anchor = Anchor.TopCentre;
caughtObject.Origin = Anchor.Centre;
caughtObject.Scale *= 0.5f;
caughtObject.LifetimeStart = source.StartTime;
caughtObject.LifetimeEnd = double.MaxValue;
caughtFruitContainer.Add(caughtObject);
@ -500,21 +493,18 @@ namespace osu.Game.Rulesets.Catch.UI
hitExplosionContainer.Add(hitExplosion);
}
private DrawablePalpableCatchHitObject createCaughtObject(PalpableCatchHitObject source)
private CaughtObject createCaughtObject(PalpableCatchHitObject source)
{
switch (source)
{
case Banana banana:
return new DrawableBanana(banana);
case Fruit _:
return new CaughtFruit();
case Fruit fruit:
return new DrawableFruit(fruit);
case Banana _:
return new CaughtBanana();
case TinyDroplet tiny:
return new DrawableTinyDroplet(tiny);
case Droplet droplet:
return new DrawableDroplet(droplet);
case Droplet _:
return new CaughtDroplet();
default:
return null;
@ -532,7 +522,7 @@ namespace osu.Game.Rulesets.Catch.UI
drop(caughtObject, animation);
}
private void removeFromPlate(DrawablePalpableCatchHitObject caughtObject, DroppedObjectAnimation animation)
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
{
if (!caughtFruitContainer.Remove(caughtObject))
throw new InvalidOperationException("Can only drop a caught object on the plate");
@ -542,40 +532,28 @@ namespace osu.Game.Rulesets.Catch.UI
drop(caughtObject, animation);
}
private void drop(DrawablePalpableCatchHitObject d, DroppedObjectAnimation animation)
private void drop(CaughtObject d, DroppedObjectAnimation animation)
{
var originalX = d.X * Scale.X;
var startTime = Clock.CurrentTime;
d.Anchor = Anchor.TopLeft;
d.Position = caughtFruitContainer.ToSpaceOfOtherDrawable(d.DrawPosition, droppedObjectTarget);
// we cannot just apply the transforms because DHO clears transforms when state is updated
d.ApplyCustomUpdateState += (o, state) => animate(o, animation, originalX, startTime);
if (d.IsLoaded)
animate(d, animation, originalX, startTime);
}
private void animate(Drawable d, DroppedObjectAnimation animation, float originalX, double startTime)
{
using (d.BeginAbsoluteSequence(startTime))
switch (animation)
{
switch (animation)
{
case DroppedObjectAnimation.Drop:
d.MoveToY(d.Y + 75, 750, Easing.InSine);
d.FadeOut(750);
break;
case DroppedObjectAnimation.Drop:
d.MoveToY(d.Y + 75, 750, Easing.InSine);
d.FadeOut(750);
break;
case DroppedObjectAnimation.Explode:
d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
d.MoveToX(d.X + originalX * 6, 1000);
d.FadeOut(750);
break;
}
d.Expire();
case DroppedObjectAnimation.Explode:
d.MoveToY(d.Y - 50, 250, Easing.OutSine).Then().MoveToY(d.Y + 50, 500, Easing.InSine);
d.MoveToX(d.X + originalX * 6, 1000);
d.FadeOut(750);
break;
}
d.Expire();
}
private enum DroppedObjectAnimation