1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-22 06:42:55 +08:00

Merge branch 'master' into mania-key-area-skinning

This commit is contained in:
Dean Herbert 2020-03-31 22:31:48 +09:00 committed by GitHub
commit f4db4985d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 396 additions and 34 deletions

View File

@ -51,7 +51,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.331.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.331.0" />
</ItemGroup>
</Project>

View File

@ -72,10 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap)
{
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
{
halfCatcherWidth = catcher.CatchWidth * 0.5f;
halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
}
return new Skill[]
{

View File

@ -37,10 +37,15 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherAnimationState CurrentState { get; private set; }
/// <summary>
/// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable.
/// </summary>
private const float allowed_catch_range = 0.8f;
/// <summary>
/// Width of the area that can be used to attempt catches during gameplay.
/// </summary>
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X);
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range;
protected bool Dashing
{

View File

@ -0,0 +1,49 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
public class TestSceneColumnBackground : ManiaSkinnableTestScene
{
[BackgroundDependencyLoader]
private void load()
{
SetContents(() => new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.8f),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new ColumnTestContainer(0, ManiaAction.Key1)
{
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{
RelativeSizeAxes = Axes.Both
}
},
new ColumnTestContainer(1, ManiaAction.Key2)
{
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Child = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{
RelativeSizeAxes = Axes.Both
}
}
}
});
}
}
}

View File

@ -20,5 +20,6 @@ namespace osu.Game.Rulesets.Mania
public enum ManiaSkinComponents
{
KeyArea
ColumnBackground
}
}

View File

@ -0,0 +1,133 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning
{
public class LegacyColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Container lightContainer;
private Sprite light;
[Resolved]
private Column column { get; set; }
[Resolved(CanBeNull = true)]
private ManiaStage stage { get; set; }
public LegacyColumnBackground()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
{
string lightImage = skin.GetConfig<LegacyManiaSkinConfigurationLookup, string>(
new LegacyManiaSkinConfigurationLookup(stage?.Columns.Count ?? 4, LegacyManiaSkinConfigurationLookups.LightImage, 0))?.Value
?? "mania-stage-light";
float leftLineWidth = skin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
new LegacyManiaSkinConfigurationLookup(stage?.Columns.Count ?? 4, LegacyManiaSkinConfigurationLookups.LeftLineWidth, column.Index))
?.Value ?? 1;
float rightLineWidth = skin.GetConfig<LegacyManiaSkinConfigurationLookup, float>(
new LegacyManiaSkinConfigurationLookup(stage?.Columns.Count ?? 4, LegacyManiaSkinConfigurationLookups.RightLineWidth, column.Index))
?.Value ?? 1;
bool hasLeftLine = leftLineWidth > 0;
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|| stage == null || column.Index == stage.Columns.Count - 1;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
new Box
{
RelativeSizeAxes = Axes.Y,
Width = leftLineWidth,
Alpha = hasLeftLine ? 1 : 0
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = rightLineWidth,
Alpha = hasRightLine ? 1 : 0
},
lightContainer = new Container
{
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Child = light = new Sprite
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Texture = skin.GetTexture(lightImage),
RelativeSizeAxes = Axes.X,
Width = 1,
Alpha = 0
}
}
};
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
if (direction.NewValue == ScrollingDirection.Up)
{
lightContainer.Anchor = Anchor.TopCentre;
lightContainer.Scale = new Vector2(1, -1);
}
else
{
lightContainer.Anchor = Anchor.BottomCentre;
lightContainer.Scale = Vector2.One;
}
}
public bool OnPressed(ManiaAction action)
{
if (action == column.Action.Value)
{
light.FadeIn();
light.ScaleTo(Vector2.One);
}
return false;
}
public void OnReleased(ManiaAction action)
{
// Todo: Should be 400 * 100 / CurrentBPM
const double animation_length = 250;
if (action == column.Action.Value)
{
light.FadeTo(0, animation_length);
light.ScaleTo(new Vector2(1, 0), animation_length);
}
}
}
}

View File

@ -46,6 +46,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
{
case ManiaSkinComponents.KeyArea:
return new LegacyKeyArea();
case ManiaSkinComponents.ColumnBackground:
return new LegacyColumnBackground();
}
break;

View File

@ -34,6 +34,9 @@ namespace osu.Game.Rulesets.Mania.UI
public readonly Bindable<ManiaAction> Action = new Bindable<ManiaAction>();
private readonly ColumnBackground background;
private readonly ColumnKeyArea keyArea;
private readonly ColumnHitObjectArea hitObjectArea;
internal readonly Container TopLevelContainer;
@ -46,7 +49,10 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Y;
Width = COLUMN_WIDTH;
background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
Drawable background = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.ColumnBackground), _ => new DefaultColumnBackground())
{
RelativeSizeAxes = Axes.Both
};
Container hitTargetContainer;
@ -128,6 +134,7 @@ namespace osu.Game.Rulesets.Mania.UI
accentColour = value;
background.AccentColour = value;
keyArea.AccentColour = value;
hitObjectArea.AccentColour = value;
}
}

View File

@ -0,0 +1,90 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI.Components
{
public class DefaultColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>
{
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Color4 brightColour;
private Color4 dimColour;
private Box background;
private Box backgroundOverlay;
[Resolved]
private Column column { get; set; }
public DefaultColumnBackground()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
InternalChildren = new[]
{
background = new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
},
backgroundOverlay = new Box
{
Name = "Background Gradient Overlay",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Alpha = 0
}
};
background.Colour = column.AccentColour.Darken(5);
brightColour = column.AccentColour.Opacity(0.6f);
dimColour = column.AccentColour.Opacity(0);
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
if (direction.NewValue == ScrollingDirection.Up)
{
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.TopLeft;
backgroundOverlay.Colour = ColourInfo.GradientVertical(brightColour, dimColour);
}
else
{
backgroundOverlay.Anchor = backgroundOverlay.Origin = Anchor.BottomLeft;
backgroundOverlay.Colour = ColourInfo.GradientVertical(dimColour, brightColour);
}
}
public bool OnPressed(ManiaAction action)
{
if (action == column.Action.Value)
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
return false;
}
public void OnReleased(ManiaAction action)
{
if (action == column.Action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
}
}
}

View File

@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Mods
void handleHitCircle(DrawableHitCircle circle)
{
if (!circle.IsHovered)
if (!circle.HitArea.IsHovered)
return;
Debug.Assert(circle.HitObject.HitWindows != null);

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary>
/// A single follow point positioned between two adjacent <see cref="DrawableOsuHitObject"/>s.
/// </summary>
public class FollowPoint : Container
public class FollowPoint : Container, IAnimationTimeReference
{
private const float width = 8;
@ -45,5 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
}
}, confineMode: ConfineMode.NoScaling);
}
public double AnimationStartTime { get; set; }
}
}

View File

@ -116,6 +116,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
int point = 0;
ClearInternal();
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@ -126,13 +128,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
if (InternalChildren.Count > point)
{
fp = (FollowPoint)InternalChildren[point];
fp.ClearTransforms();
}
else
AddInternal(fp = new FollowPoint());
AddInternal(fp = new FollowPoint());
fp.Position = pointStartPosition;
fp.Rotation = rotation;
@ -142,6 +138,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
fp.AnimationStartTime = fadeInTime;
using (fp.BeginAbsoluteSequence(fadeInTime))
{
fp.FadeIn(osuEnd.TimeFadeIn);

View File

@ -62,6 +62,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
}
};
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
if (!overlayAboveNumber)
ChangeInternalChildDepth(hitCircleText, -float.MaxValue);
state.BindTo(drawableObject.State);
state.BindValueChanged(updateState, true);

View File

@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
SliderPathRadius,
AllowSliderBallTint,
CursorExpand,
CursorRotate
CursorRotate,
HitCircleOverlayAboveNumber
}
}

View File

@ -28,10 +28,11 @@ namespace osu.Game.Scoring.Legacy
{
var score = new Score
{
ScoreInfo = new ScoreInfo(),
Replay = new Replay()
};
WorkingBeatmap workingBeatmap;
using (SerializationReader sr = new SerializationReader(stream))
{
currentRuleset = GetRuleset(sr.ReadByte());
@ -41,7 +42,7 @@ namespace osu.Game.Scoring.Legacy
var version = sr.ReadInt32();
var workingBeatmap = GetBeatmap(sr.ReadString());
workingBeatmap = GetBeatmap(sr.ReadString());
if (workingBeatmap is DummyWorkingBeatmap)
throw new BeatmapNotFoundException();
@ -113,6 +114,10 @@ namespace osu.Game.Scoring.Legacy
CalculateAccuracy(score.ScoreInfo);
// before returning for database import, we must restore the database-sourced BeatmapInfo.
// if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception.
score.ScoreInfo.Beatmap = workingBeatmap.BeatmapInfo;
return score;
}

View File

@ -0,0 +1,25 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Timing;
namespace osu.Game.Skinning
{
/// <summary>
/// Denotes an object which provides a reference time to start animations from.
/// </summary>
[Cached]
public interface IAnimationTimeReference
{
/// <summary>
/// The reference clock.
/// </summary>
IFrameBasedClock Clock { get; }
/// <summary>
/// The time which animations should be started from, relative to <see cref="Clock"/>.
/// </summary>
double AnimationStartTime { get; }
}
}

View File

@ -21,5 +21,8 @@ namespace osu.Game.Skinning
{
KeyImage,
KeyImageDown
LightImage,
LeftLineWidth,
RightLineWidth
}
}

View File

@ -3,10 +3,12 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Timing;
namespace osu.Game.Skinning
{
@ -22,7 +24,7 @@ namespace osu.Game.Skinning
if (textures.Length > 0)
{
var animation = new TextureAnimation
var animation = new SkinnableTextureAnimation
{
DefaultFrameLength = getFrameLength(source, applyConfigFrameRate, textures),
Repeat = looping,
@ -53,6 +55,25 @@ namespace osu.Game.Skinning
}
}
public class SkinnableTextureAnimation : TextureAnimation
{
[Resolved(canBeNull: true)]
private IAnimationTimeReference timeReference { get; set; }
public SkinnableTextureAnimation()
: base(false)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
if (timeReference != null)
Clock = new FramedOffsetClock(timeReference.Clock) { Offset = -timeReference.AnimationStartTime };
}
}
private const double default_frame_time = 1000 / 60d;
private static double getFrameLength(ISkin source, bool applyConfigFrameRate, Texture[] textures)

View File

@ -50,7 +50,7 @@ namespace osu.Game.Storyboards.Drawables
AddInternal(Content = new Container<DrawableStoryboardLayer>
{
Size = new Vector2(640, 480),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
namespace osu.Game.Storyboards.Drawables
{
public class DrawableStoryboardLayer : LifetimeManagementContainer
public class DrawableStoryboardLayer : CompositeDrawable
{
public StoryboardLayer Layer { get; }
public bool Enabled;
@ -23,17 +23,34 @@ namespace osu.Game.Storyboards.Drawables
Origin = Anchor.Centre;
Enabled = layer.VisibleWhenPassing;
Masking = layer.Masking;
InternalChild = new LayerElementContainer(layer);
}
[BackgroundDependencyLoader]
private void load(CancellationToken? cancellationToken)
private class LayerElementContainer : LifetimeManagementContainer
{
foreach (var element in Layer.Elements)
{
cancellationToken?.ThrowIfCancellationRequested();
private readonly StoryboardLayer storyboardLayer;
if (element.IsDrawable)
AddInternal(element.CreateDrawable());
public LayerElementContainer(StoryboardLayer layer)
{
storyboardLayer = layer;
Width = 640;
Height = 480;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
}
[BackgroundDependencyLoader]
private void load(CancellationToken? cancellationToken)
{
foreach (var element in storyboardLayer.Elements)
{
cancellationToken?.ThrowIfCancellationRequested();
if (element.IsDrawable)
AddInternal(element.CreateDrawable());
}
}
}
}

View File

@ -22,8 +22,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.331.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.331.0" />
<PackageReference Include="Sentry" Version="2.1.1" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -70,8 +70,8 @@
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.331.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.331.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -79,7 +79,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.327.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.331.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />