mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 16:12:54 +08:00
Merge pull request #11055 from ekrctb/catch-stateless-rng
This commit is contained in:
commit
0134ac94a7
@ -3,11 +3,12 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -37,39 +38,50 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
||||||
SetProperties(new DrawableFruit(new Fruit
|
new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||||
{
|
{
|
||||||
IndexInBeatmap = indexInBeatmap,
|
IndexInBeatmap = indexInBeatmap,
|
||||||
HyperDashBindable = { Value = hyperdash }
|
HyperDashBindable = { Value = hyperdash }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
private Drawable createDrawableBanana() =>
|
private Drawable createDrawableBanana() =>
|
||||||
SetProperties(new DrawableBanana(new Banana()));
|
new TestDrawableCatchHitObjectSpecimen(new DrawableBanana(new Banana()));
|
||||||
|
|
||||||
private Drawable createDrawableDroplet(bool hyperdash = false) =>
|
private Drawable createDrawableDroplet(bool hyperdash = false) =>
|
||||||
SetProperties(new DrawableDroplet(new Droplet
|
new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||||
{
|
{
|
||||||
HyperDashBindable = { Value = hyperdash }
|
HyperDashBindable = { Value = hyperdash }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
private Drawable createDrawableTinyDroplet() => SetProperties(new DrawableTinyDroplet(new TinyDroplet()));
|
private Drawable createDrawableTinyDroplet() => new TestDrawableCatchHitObjectSpecimen(new DrawableTinyDroplet(new TinyDroplet()));
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual DrawableCatchHitObject SetProperties(DrawableCatchHitObject d)
|
public class TestDrawableCatchHitObjectSpecimen : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
public readonly ManualClock ManualClock;
|
||||||
|
|
||||||
|
public TestDrawableCatchHitObjectSpecimen(DrawableCatchHitObject d)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
ManualClock = new ManualClock();
|
||||||
|
Clock = new FramedClock(ManualClock);
|
||||||
|
|
||||||
var hitObject = d.HitObject;
|
var hitObject = d.HitObject;
|
||||||
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 0 });
|
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
hitObject.StartTime = 1000000000000;
|
|
||||||
hitObject.Scale = 1.5f;
|
hitObject.Scale = 1.5f;
|
||||||
|
hitObject.StartTime = 500;
|
||||||
|
|
||||||
d.Anchor = Anchor.Centre;
|
d.Anchor = Anchor.Centre;
|
||||||
d.RelativePositionAxes = Axes.None;
|
|
||||||
d.Position = Vector2.Zero;
|
|
||||||
d.HitObjectApplied += _ =>
|
d.HitObjectApplied += _ =>
|
||||||
{
|
{
|
||||||
d.LifetimeStart = double.NegativeInfinity;
|
d.LifetimeStart = double.NegativeInfinity;
|
||||||
d.LifetimeEnd = double.PositiveInfinity;
|
d.LifetimeEnd = double.PositiveInfinity;
|
||||||
};
|
};
|
||||||
return d;
|
|
||||||
|
InternalChild = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
96
osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs
Normal file
96
osu.Game.Rulesets.Catch.Tests/TestSceneFruitRandomness.cs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneFruitRandomness : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly TestDrawableFruit drawableFruit;
|
||||||
|
private readonly TestDrawableBanana drawableBanana;
|
||||||
|
|
||||||
|
public TestSceneFruitRandomness()
|
||||||
|
{
|
||||||
|
drawableFruit = new TestDrawableFruit(new Fruit());
|
||||||
|
drawableBanana = new TestDrawableBanana(new Banana());
|
||||||
|
|
||||||
|
Add(new TestDrawableCatchHitObjectSpecimen(drawableFruit) { X = -200 });
|
||||||
|
Add(new TestDrawableCatchHitObjectSpecimen(drawableBanana));
|
||||||
|
|
||||||
|
AddSliderStep("start time", 500, 600, 0, x =>
|
||||||
|
{
|
||||||
|
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFruitRandomness()
|
||||||
|
{
|
||||||
|
// Use values such that the banana colour changes (2/3 of the integers are okay)
|
||||||
|
const int initial_start_time = 500;
|
||||||
|
const int another_start_time = 501;
|
||||||
|
|
||||||
|
float fruitRotation = 0;
|
||||||
|
float bananaRotation = 0;
|
||||||
|
float bananaScale = 0;
|
||||||
|
Color4 bananaColour = new Color4();
|
||||||
|
|
||||||
|
AddStep("Initialize start time", () =>
|
||||||
|
{
|
||||||
|
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
|
||||||
|
|
||||||
|
fruitRotation = drawableFruit.InnerRotation;
|
||||||
|
bananaRotation = drawableBanana.InnerRotation;
|
||||||
|
bananaScale = drawableBanana.InnerScale;
|
||||||
|
bananaColour = drawableBanana.AccentColour.Value;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("change start time", () =>
|
||||||
|
{
|
||||||
|
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = another_start_time;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("fruit rotation is changed", () => drawableFruit.InnerRotation != fruitRotation);
|
||||||
|
AddAssert("banana rotation is changed", () => drawableBanana.InnerRotation != bananaRotation);
|
||||||
|
AddAssert("banana scale is changed", () => drawableBanana.InnerScale != bananaScale);
|
||||||
|
AddAssert("banana colour is changed", () => drawableBanana.AccentColour.Value != bananaColour);
|
||||||
|
|
||||||
|
AddStep("reset start time", () =>
|
||||||
|
{
|
||||||
|
drawableFruit.HitObject.StartTime = drawableBanana.HitObject.StartTime = initial_start_time;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("rotation and scale restored", () =>
|
||||||
|
drawableFruit.InnerRotation == fruitRotation &&
|
||||||
|
drawableBanana.InnerRotation == bananaRotation &&
|
||||||
|
drawableBanana.InnerScale == bananaScale &&
|
||||||
|
drawableBanana.AccentColour.Value == bananaColour);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDrawableFruit : DrawableFruit
|
||||||
|
{
|
||||||
|
public float InnerRotation => ScaleContainer.Rotation;
|
||||||
|
|
||||||
|
public TestDrawableFruit(Fruit h)
|
||||||
|
: base(h)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDrawableBanana : DrawableBanana
|
||||||
|
{
|
||||||
|
public float InnerRotation => ScaleContainer.Rotation;
|
||||||
|
public float InnerScale => ScaleContainer.Scale.X;
|
||||||
|
|
||||||
|
public TestDrawableBanana(Banana h)
|
||||||
|
: base(h)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
AddStep("fruit changes visual and hyper", () => SetContents(() => SetProperties(new DrawableFruit(new Fruit
|
AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||||
{
|
{
|
||||||
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
AddStep("droplet changes hyper", () => SetContents(() => SetProperties(new DrawableDroplet(new Droplet
|
AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||||
{
|
{
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
using osu.Game.Rulesets.Catch.Judgements;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -31,17 +30,12 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
Samples = samples;
|
Samples = samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4? colour;
|
|
||||||
|
|
||||||
Color4 IHasComboInformation.GetComboColour(IReadOnlyList<Color4> comboColours)
|
|
||||||
{
|
|
||||||
// override any external colour changes with banananana
|
// override any external colour changes with banananana
|
||||||
return colour ??= getBananaColour();
|
Color4 IHasComboInformation.GetComboColour(IReadOnlyList<Color4> comboColours) => getBananaColour();
|
||||||
}
|
|
||||||
|
|
||||||
private Color4 getBananaColour()
|
private Color4 getBananaColour()
|
||||||
{
|
{
|
||||||
switch (RNG.Next(0, 3))
|
switch (StatelessRNG.NextInt(3, RandomSeed))
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return new Color4(255, 240, 0, 255);
|
return new Color4(255, 240, 0, 255);
|
||||||
|
@ -97,6 +97,12 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
set => ScaleBindable.Value = value;
|
set => ScaleBindable.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The seed value used for visual randomness such as fruit rotation.
|
||||||
|
/// The value is <see cref="HitObject.StartTime"/> truncated to an integer.
|
||||||
|
/// </summary>
|
||||||
|
public int RandomSeed => (int)StartTime;
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -21,6 +20,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// start time affects the random seed which is used to determine the banana colour
|
||||||
|
StartTimeBindable.BindValueChanged(_ => UpdateComboColour());
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
base.UpdateInitialTransforms();
|
base.UpdateInitialTransforms();
|
||||||
@ -28,14 +35,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
const float end_scale = 0.6f;
|
const float end_scale = 0.6f;
|
||||||
const float random_scale_range = 1.6f;
|
const float random_scale_range = 1.6f;
|
||||||
|
|
||||||
ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RNG.NextSingle()))
|
ScaleContainer.ScaleTo(HitObject.Scale * (end_scale + random_scale_range * RandomSingle(3)))
|
||||||
.Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
|
.Then().ScaleTo(HitObject.Scale * end_scale, HitObject.TimePreempt);
|
||||||
|
|
||||||
ScaleContainer.RotateTo(getRandomAngle())
|
ScaleContainer.RotateTo(getRandomAngle(1))
|
||||||
.Then()
|
.Then()
|
||||||
.RotateTo(getRandomAngle(), HitObject.TimePreempt);
|
.RotateTo(getRandomAngle(2), HitObject.TimePreempt);
|
||||||
|
|
||||||
float getRandomAngle() => 180 * (RNG.NextSingle() * 2 - 1);
|
float getRandomAngle(int series) => 180 * (RandomSingle(series) * 2 - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PlaySamples()
|
public override void PlaySamples()
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -20,12 +21,19 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
|
|
||||||
protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH;
|
protected override float SamplePlaybackPosition => HitObject.X / CatchPlayfield.WIDTH;
|
||||||
|
|
||||||
|
public int RandomSeed => HitObject?.RandomSeed ?? 0;
|
||||||
|
|
||||||
protected DrawableCatchHitObject([CanBeNull] CatchHitObject hitObject)
|
protected DrawableCatchHitObject([CanBeNull] CatchHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a random number in range [0,1) based on seed <see cref="RandomSeed"/>.
|
||||||
|
/// </summary>
|
||||||
|
public float RandomSingle(int series) => StatelessRNG.NextSingle(RandomSeed, series);
|
||||||
|
|
||||||
protected override void OnApply()
|
protected override void OnApply()
|
||||||
{
|
{
|
||||||
base.OnApply();
|
base.OnApply();
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
base.UpdateInitialTransforms();
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
// roughly matches osu-stable
|
// roughly matches osu-stable
|
||||||
float startRotation = RNG.NextSingle() * 20;
|
float startRotation = RandomSingle(1) * 20;
|
||||||
double duration = HitObject.TimePreempt + 2000;
|
double duration = HitObject.TimePreempt + 2000;
|
||||||
|
|
||||||
ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
|
ScaleContainer.RotateTo(startRotation).RotateTo(startRotation + 720, duration);
|
||||||
|
@ -5,7 +5,7 @@ using System;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
ScaleContainer.Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
|
||||||
|
|
||||||
IndexInBeatmap.BindValueChanged(change =>
|
IndexInBeatmap.BindValueChanged(change =>
|
||||||
{
|
{
|
||||||
VisualRepresentation.Value = GetVisualRepresentation(change.NewValue);
|
VisualRepresentation.Value = GetVisualRepresentation(change.NewValue);
|
||||||
@ -41,6 +39,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
HyperDash.BindValueChanged(_ => updatePiece(), true);
|
HyperDash.BindValueChanged(_ => updatePiece(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateInitialTransforms()
|
||||||
|
{
|
||||||
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
|
ScaleContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
|
||||||
|
}
|
||||||
|
|
||||||
private void updatePiece()
|
private void updatePiece()
|
||||||
{
|
{
|
||||||
ScaleContainer.Child = new SkinnableDrawable(
|
ScaleContainer.Child = new SkinnableDrawable(
|
||||||
|
Loading…
Reference in New Issue
Block a user