Add generics to ApplyInitialValue/ApplyTransforms for ability to return custom transform sequences

*sigh*
This commit is contained in:
Salman Ahmed 2024-03-08 03:06:49 +03:00
parent b450abb687
commit fa9b2f0cd5
25 changed files with 122 additions and 193 deletions

View File

@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Gameplay
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
sprite.Group.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None);
sprite.Commands.AddAlpha(firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1, Easing.None);
storyboard.GetLayer("Background").Add(sprite);
@ -73,16 +73,16 @@ namespace osu.Game.Tests.Visual.Gameplay
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
// these should be ignored as we have an alpha visibility blocker proceeding this command.
sprite.Group.AddScale(loop_start_time, -18000, 0, 1, Easing.None);
sprite.Commands.AddScale(loop_start_time, -18000, 0, 1, Easing.None);
var loopGroup = sprite.AddLoopingGroup(loop_start_time, 50);
loopGroup.AddScale(loop_start_time, -18000, 0, 1, Easing.None);
var target = addEventToLoop ? loopGroup : sprite.Group;
var target = addEventToLoop ? loopGroup : sprite.Commands;
double loopRelativeOffset = addEventToLoop ? -loop_start_time : 0;
target.AddAlpha(loopRelativeOffset + firstStoryboardEvent, loopRelativeOffset + firstStoryboardEvent + 500, 0, 1, Easing.None);
// these should be ignored due to being in the future.
sprite.Group.AddAlpha(18000, 20000, 0, 1, Easing.None);
sprite.Commands.AddAlpha(18000, 20000, 0, 1, Easing.None);
loopGroup.AddAlpha(38000, 40000, 0, 1, Easing.None);
storyboard.GetLayer("Background").Add(sprite);

View File

@ -216,7 +216,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
var storyboard = new Storyboard();
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
sprite.Group.AddAlpha(0, duration, 1, 0, Easing.None);
sprite.Commands.AddAlpha(0, duration, 1, 0, Easing.None);
storyboard.GetLayer("Background").Add(sprite);
return storyboard;
}

View File

@ -424,7 +424,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
public void TestIntroStoryboardElement() => testLeadIn(b =>
{
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
sprite.Group.AddAlpha(-2000, 0, 0, 1, Easing.None);
sprite.Commands.AddAlpha(-2000, 0, 0, 1, Easing.None);
b.Storyboard.GetLayer("Background").Add(sprite);
});

View File

@ -19,7 +19,7 @@ namespace osu.Game.Beatmaps.Formats
public class LegacyStoryboardDecoder : LegacyDecoder<Storyboard>
{
private StoryboardSprite? storyboardSprite;
private StoryboardCommandGroup? currentGroup;
private StoryboardCommandGroup? currentCommandsGroup;
private Storyboard storyboard = null!;
@ -166,7 +166,7 @@ namespace osu.Game.Beatmaps.Formats
else
{
if (depth < 2)
currentGroup = storyboardSprite?.Group;
currentCommandsGroup = storyboardSprite?.Commands;
string commandType = split[0];
@ -178,7 +178,7 @@ namespace osu.Game.Beatmaps.Formats
double startTime = split.Length > 2 ? Parsing.ParseDouble(split[2]) : double.MinValue;
double endTime = split.Length > 3 ? Parsing.ParseDouble(split[3]) : double.MaxValue;
int groupNumber = split.Length > 4 ? Parsing.ParseInt(split[4]) : 0;
currentGroup = storyboardSprite?.AddTriggerGroup(triggerName, startTime, endTime, groupNumber);
currentCommandsGroup = storyboardSprite?.AddTriggerGroup(triggerName, startTime, endTime, groupNumber);
break;
}
@ -186,7 +186,7 @@ namespace osu.Game.Beatmaps.Formats
{
double startTime = Parsing.ParseDouble(split[1]);
int repeatCount = Parsing.ParseInt(split[2]);
currentGroup = storyboardSprite?.AddLoopingGroup(startTime, Math.Max(0, repeatCount - 1));
currentCommandsGroup = storyboardSprite?.AddLoopingGroup(startTime, Math.Max(0, repeatCount - 1));
break;
}
@ -205,7 +205,7 @@ namespace osu.Game.Beatmaps.Formats
{
float startValue = Parsing.ParseFloat(split[4]);
float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue;
currentGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing);
currentCommandsGroup?.AddAlpha(startTime, endTime, startValue, endValue, easing);
break;
}
@ -213,7 +213,7 @@ namespace osu.Game.Beatmaps.Formats
{
float startValue = Parsing.ParseFloat(split[4]);
float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue;
currentGroup?.AddScale(startTime, endTime, startValue, endValue, easing);
currentCommandsGroup?.AddScale(startTime, endTime, startValue, endValue, easing);
break;
}
@ -223,7 +223,7 @@ namespace osu.Game.Beatmaps.Formats
float startY = Parsing.ParseFloat(split[5]);
float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX;
float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY;
currentGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing);
currentCommandsGroup?.AddVectorScale(startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY), easing);
break;
}
@ -231,7 +231,7 @@ namespace osu.Game.Beatmaps.Formats
{
float startValue = Parsing.ParseFloat(split[4]);
float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue;
currentGroup?.AddRotation(startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue), easing);
currentCommandsGroup?.AddRotation(startTime, endTime, MathUtils.RadiansToDegrees(startValue), MathUtils.RadiansToDegrees(endValue), easing);
break;
}
@ -241,8 +241,8 @@ namespace osu.Game.Beatmaps.Formats
float startY = Parsing.ParseFloat(split[5]);
float endX = split.Length > 6 ? Parsing.ParseFloat(split[6]) : startX;
float endY = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startY;
currentGroup?.AddX(startTime, endTime, startX, endX, easing);
currentGroup?.AddY(startTime, endTime, startY, endY, easing);
currentCommandsGroup?.AddX(startTime, endTime, startX, endX, easing);
currentCommandsGroup?.AddY(startTime, endTime, startY, endY, easing);
break;
}
@ -250,7 +250,7 @@ namespace osu.Game.Beatmaps.Formats
{
float startValue = Parsing.ParseFloat(split[4]);
float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue;
currentGroup?.AddX(startTime, endTime, startValue, endValue, easing);
currentCommandsGroup?.AddX(startTime, endTime, startValue, endValue, easing);
break;
}
@ -258,7 +258,7 @@ namespace osu.Game.Beatmaps.Formats
{
float startValue = Parsing.ParseFloat(split[4]);
float endValue = split.Length > 5 ? Parsing.ParseFloat(split[5]) : startValue;
currentGroup?.AddY(startTime, endTime, startValue, endValue, easing);
currentCommandsGroup?.AddY(startTime, endTime, startValue, endValue, easing);
break;
}
@ -270,7 +270,7 @@ namespace osu.Game.Beatmaps.Formats
float endRed = split.Length > 7 ? Parsing.ParseFloat(split[7]) : startRed;
float endGreen = split.Length > 8 ? Parsing.ParseFloat(split[8]) : startGreen;
float endBlue = split.Length > 9 ? Parsing.ParseFloat(split[9]) : startBlue;
currentGroup?.AddColour(startTime, endTime,
currentCommandsGroup?.AddColour(startTime, endTime,
new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1), easing);
break;
@ -283,16 +283,16 @@ namespace osu.Game.Beatmaps.Formats
switch (type)
{
case "A":
currentGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive,
currentCommandsGroup?.AddBlendingParameters(startTime, endTime, BlendingParameters.Additive,
startTime == endTime ? BlendingParameters.Additive : BlendingParameters.Inherit, easing);
break;
case "H":
currentGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing);
currentCommandsGroup?.AddFlipH(startTime, endTime, true, startTime == endTime, easing);
break;
case "V":
currentGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing);
currentCommandsGroup?.AddFlipV(startTime, endTime, true, startTime == endTime, easing);
break;
}

View File

@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Storyboards.Commands
{
@ -28,13 +29,15 @@ namespace osu.Game.Storyboards.Commands
/// Sets the value of the corresponding property in <see cref="Drawable"/> to the start value of this command.
/// </summary>
/// <param name="d">The target drawable.</param>
void ApplyInitialValue(Drawable d);
void ApplyInitialValue<TDrawable>(TDrawable d)
where TDrawable : Drawable, IFlippable, IVectorScalable;
/// <summary>
/// Applies the transforms described by this storyboard command to the target drawable.
/// </summary>
/// <param name="d">The target drawable.</param>
/// <returns>The sequence of transforms applied to the target drawable.</returns>
TransformSequence<Drawable> ApplyTransforms(Drawable d);
TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
where TDrawable : Drawable, IFlippable, IVectorScalable;
}
}

View File

@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Alpha);
public override void ApplyInitialValue(Drawable d) => d.Alpha = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Alpha = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.FadeTo(StartValue).Then().FadeTo(EndValue, Duration, Easing);
}
}

View File

@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Blending);
public override void ApplyInitialValue(Drawable d) => d.Blending = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Blending = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.TransformTo(nameof(d.Blending), StartValue).Delay(Duration)
.TransformTo(nameof(d.Blending), EndValue);
}

View File

@ -16,9 +16,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Colour);
public override void ApplyInitialValue(Drawable d) => d.Colour = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Colour = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.FadeColour(StartValue).Then().FadeColour(EndValue, Duration, Easing);
}
}

View File

@ -4,6 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Storyboards.Drawables;
namespace osu.Game.Storyboards.Commands
{
@ -11,6 +12,11 @@ namespace osu.Game.Storyboards.Commands
{
public double StartTime { get; }
public double EndTime { get; }
public T StartValue { get; }
public T EndValue { get; }
public Easing Easing { get; }
public double Duration => EndTime - StartTime;
protected StoryboardCommand(double startTime, double endTime, T startValue, T endValue, Easing easing)
@ -25,18 +31,13 @@ namespace osu.Game.Storyboards.Commands
Easing = easing;
}
public Easing Easing { get; set; }
public int LoopCount { get; set; }
public double Delay { get; set; }
public T StartValue;
public T EndValue;
public abstract string PropertyName { get; }
public abstract void ApplyInitialValue(Drawable d);
public abstract void ApplyInitialValue<TDrawable>(TDrawable d)
where TDrawable : Drawable, IFlippable, IVectorScalable;
public abstract TransformSequence<Drawable> ApplyTransforms(Drawable d);
public abstract TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
where TDrawable : Drawable, IFlippable, IVectorScalable;
public int CompareTo(StoryboardCommand<T>? other)
{

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using osu.Framework.Graphics;
using osu.Framework.Lists;
@ -13,18 +12,20 @@ namespace osu.Game.Storyboards.Commands
{
public class StoryboardCommandGroup
{
public SortedList<StoryboardCommand<float>> X;
public SortedList<StoryboardCommand<float>> Y;
public SortedList<StoryboardCommand<float>> Scale;
public SortedList<StoryboardCommand<Vector2>> VectorScale;
public SortedList<StoryboardCommand<float>> Rotation;
public SortedList<StoryboardCommand<Color4>> Colour;
public SortedList<StoryboardCommand<float>> Alpha;
public SortedList<StoryboardCommand<BlendingParameters>> BlendingParameters;
public SortedList<StoryboardCommand<bool>> FlipH;
public SortedList<StoryboardCommand<bool>> FlipV;
public SortedList<StoryboardCommand<float>> X = new SortedList<StoryboardCommand<float>>();
public SortedList<StoryboardCommand<float>> Y = new SortedList<StoryboardCommand<float>>();
public SortedList<StoryboardCommand<float>> Scale = new SortedList<StoryboardCommand<float>>();
public SortedList<StoryboardCommand<Vector2>> VectorScale = new SortedList<StoryboardCommand<Vector2>>();
public SortedList<StoryboardCommand<float>> Rotation = new SortedList<StoryboardCommand<float>>();
public SortedList<StoryboardCommand<Color4>> Colour = new SortedList<StoryboardCommand<Color4>>();
public SortedList<StoryboardCommand<float>> Alpha = new SortedList<StoryboardCommand<float>>();
public SortedList<StoryboardCommand<BlendingParameters>> BlendingParameters = new SortedList<StoryboardCommand<BlendingParameters>>();
public SortedList<StoryboardCommand<bool>> FlipH = new SortedList<StoryboardCommand<bool>>();
public SortedList<StoryboardCommand<bool>> FlipV = new SortedList<StoryboardCommand<bool>>();
private readonly IReadOnlyList<IStoryboardCommand>[] lists;
public IReadOnlyList<IStoryboardCommand> AllCommands => allCommands;
private readonly List<IStoryboardCommand> allCommands = new List<IStoryboardCommand>();
/// <summary>
/// Returns the earliest start time of the commands added to this group.
@ -44,28 +45,6 @@ namespace osu.Game.Storyboards.Commands
[JsonIgnore]
public bool HasCommands { get; private set; }
public StoryboardCommandGroup()
{
lists = new IReadOnlyList<IStoryboardCommand>[]
{
X = new SortedList<StoryboardCommand<float>>(),
Y = new SortedList<StoryboardCommand<float>>(),
Scale = new SortedList<StoryboardCommand<float>>(),
VectorScale = new SortedList<StoryboardCommand<Vector2>>(),
Rotation = new SortedList<StoryboardCommand<float>>(),
Colour = new SortedList<StoryboardCommand<Color4>>(),
Alpha = new SortedList<StoryboardCommand<float>>(),
BlendingParameters = new SortedList<StoryboardCommand<BlendingParameters>>(),
FlipH = new SortedList<StoryboardCommand<bool>>(),
FlipV = new SortedList<StoryboardCommand<bool>>()
};
}
/// <summary>
/// Returns all commands contained by this group unsorted.
/// </summary>
public virtual IEnumerable<IStoryboardCommand> GetAllCommands() => lists.SelectMany(l => l);
public void AddX(double startTime, double endTime, float startValue, float endValue, Easing easing)
=> AddCommand(X, new StoryboardXCommand(startTime, endTime, startValue, endValue, easing));
@ -104,6 +83,7 @@ namespace osu.Game.Storyboards.Commands
protected virtual void AddCommand<T>(ICollection<StoryboardCommand<T>> list, StoryboardCommand<T> command)
{
list.Add(command);
allCommands.Add(command);
HasCommands = true;
if (command.StartTime < StartTime)

View File

@ -1,41 +0,0 @@
// 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.
namespace osu.Game.Storyboards.Commands
{
// public class StoryboardCommandList<T> : IStoryboardCommandList
// {
// // todo: change to sorted list and avoid enumerable type on exposed properties?
// private readonly List<StoryboardCommand<T>> commands = new List<StoryboardCommand<T>>();
//
// public IEnumerable<StoryboardCommand<T>> Commands => commands.OrderBy(c => c.StartTime);
//
// IEnumerable<IStoryboardCommand> IStoryboardCommandList.Commands => Commands;
// public bool HasCommands => commands.Count > 0;
//
// public double StartTime { get; private set; } = double.MaxValue;
// public double EndTime { get; private set; } = double.MinValue;
//
// public T? StartValue { get; private set; }
// public T? EndValue { get; private set; }
//
// public void Add(StoryboardCommand<T> command)
// {
// commands.Add(command);
//
// if (command.StartTime < StartTime)
// {
// StartValue = command.StartValue;
// StartTime = command.StartTime;
// }
//
// if (command.EndTime > EndTime)
// {
// EndValue = command.EndValue;
// EndTime = command.EndTime;
// }
// }
//
// public override string ToString() => $"{commands.Count} command(s)";
// }
}

View File

@ -14,12 +14,12 @@ namespace osu.Game.Storyboards.Commands
{
}
public override string PropertyName => nameof(IDrawableStoryboardElement.FlipH);
public override string PropertyName => nameof(IFlippable.FlipH);
public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipH = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.FlipH = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
=> d.TransformTo(nameof(IDrawableStoryboardElement.FlipH), StartValue).Delay(Duration)
.TransformTo(nameof(IDrawableStoryboardElement.FlipH), EndValue);
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.TransformTo(nameof(IFlippable.FlipH), StartValue).Delay(Duration)
.TransformTo(nameof(IFlippable.FlipH), EndValue);
}
}

View File

@ -14,12 +14,12 @@ namespace osu.Game.Storyboards.Commands
{
}
public override string PropertyName => nameof(IDrawableStoryboardElement.FlipV);
public override string PropertyName => nameof(IFlippable.FlipV);
public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).FlipV = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.FlipV = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
=> d.TransformTo(nameof(IDrawableStoryboardElement.FlipV), StartValue).Delay(Duration)
.TransformTo(nameof(IDrawableStoryboardElement.FlipV), EndValue);
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.TransformTo(nameof(IFlippable.FlipV), StartValue).Delay(Duration)
.TransformTo(nameof(IFlippable.FlipV), EndValue);
}
}

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
namespace osu.Game.Storyboards.Commands
@ -52,9 +51,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => command.PropertyName;
public override void ApplyInitialValue(Drawable d) => command.ApplyInitialValue(d);
public override void ApplyInitialValue<TDrawable>(TDrawable d) => command.ApplyInitialValue(d);
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
{
if (loopingGroup.TotalIterations == 0)
return command.ApplyTransforms(d);

View File

@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Rotation);
public override void ApplyInitialValue(Drawable d) => d.Rotation = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Rotation = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.RotateTo(StartValue).Then().RotateTo(EndValue, Duration, Easing);
}
}

View File

@ -16,9 +16,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Scale);
public override void ApplyInitialValue(Drawable d) => d.Scale = new Vector2(StartValue);
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Scale = new Vector2(StartValue);
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.ScaleTo(StartValue).Then().ScaleTo(EndValue, Duration, Easing);
}
}

View File

@ -3,7 +3,6 @@
namespace osu.Game.Storyboards.Commands
{
// todo: this is not implemented and has never been, keep that in mind.
public class StoryboardTriggerGroup : StoryboardCommandGroup
{
public string TriggerName;

View File

@ -15,12 +15,12 @@ namespace osu.Game.Storyboards.Commands
{
}
public override string PropertyName => nameof(IDrawableStoryboardElement.VectorScale);
public override string PropertyName => nameof(IVectorScalable.VectorScale);
public override void ApplyInitialValue(Drawable d) => ((IDrawableStoryboardElement)d).VectorScale = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.VectorScale = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
=> d.TransformTo(nameof(IDrawableStoryboardElement.VectorScale), StartValue).Then()
.TransformTo(nameof(IDrawableStoryboardElement.VectorScale), EndValue, Duration, Easing);
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.TransformTo(nameof(d.VectorScale), StartValue).Then()
.TransformTo(nameof(d.VectorScale), EndValue, Duration, Easing);
}
}

View File

@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.X);
public override void ApplyInitialValue(Drawable d) => d.X = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.X = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.MoveToX(StartValue).Then().MoveToX(EndValue, Duration, Easing);
}
}

View File

@ -15,9 +15,9 @@ namespace osu.Game.Storyboards.Commands
public override string PropertyName => nameof(Drawable.Y);
public override void ApplyInitialValue(Drawable d) => d.Y = StartValue;
public override void ApplyInitialValue<TDrawable>(TDrawable d) => d.Y = StartValue;
public override TransformSequence<Drawable> ApplyTransforms(Drawable d)
public override TransformSequence<TDrawable> ApplyTransforms<TDrawable>(TDrawable d)
=> d.MoveToY(StartValue).Then().MoveToY(EndValue, Duration, Easing);
}
}

View File

@ -16,7 +16,7 @@ using osuTK;
namespace osu.Game.Storyboards.Drawables
{
public partial class DrawableStoryboardAnimation : TextureAnimation, IDrawableStoryboardElement
public partial class DrawableStoryboardAnimation : TextureAnimation, IFlippable, IVectorScalable
{
public StoryboardAnimation Animation { get; }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
@ -13,7 +14,7 @@ using osuTK;
namespace osu.Game.Storyboards.Drawables
{
public partial class DrawableStoryboardSprite : Sprite, IDrawableStoryboardElement
public partial class DrawableStoryboardSprite : Sprite, IFlippable, IVectorScalable
{
public StoryboardSprite Sprite { get; }
@ -101,6 +102,9 @@ namespace osu.Game.Storyboards.Drawables
else
Texture = textureStore.Get(Sprite.Path);
if (Sprite.Path == "SB/textbox.png")
Debugger.Break();
Sprite.ApplyTransforms(this);
}

View File

@ -2,14 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osuTK;
namespace osu.Game.Storyboards.Drawables
{
public interface IDrawableStoryboardElement : IDrawable
public interface IFlippable : IDrawable
{
bool FlipH { get; set; }
bool FlipV { get; set; }
Vector2 VectorScale { get; set; }
}
}

View File

@ -0,0 +1,13 @@
// 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.Graphics;
using osuTK;
namespace osu.Game.Storyboards.Drawables
{
public interface IVectorScalable : IDrawable
{
Vector2 VectorScale { get; set; }
}
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Storyboards
{
public class StoryboardSprite : IStoryboardElementWithDuration
{
private readonly List<StoryboardLoopingGroup> loopGroups = new List<StoryboardLoopingGroup>();
private readonly List<StoryboardLoopingGroup> loopingGroups = new List<StoryboardLoopingGroup>();
private readonly List<StoryboardTriggerGroup> triggerGroups = new List<StoryboardTriggerGroup>();
public string Path { get; }
@ -22,7 +22,7 @@ namespace osu.Game.Storyboards
public Anchor Origin;
public Vector2 InitialPosition;
public readonly StoryboardCommandGroup Group = new StoryboardCommandGroup();
public readonly StoryboardCommandGroup Commands = new StoryboardCommandGroup();
public double StartTime
{
@ -35,10 +35,10 @@ namespace osu.Game.Storyboards
// anything before that point can be ignored (the sprite is not visible after all).
var alphaCommands = new List<(double startTime, bool isZeroStartValue)>();
var command = Group.Alpha.FirstOrDefault();
var command = Commands.Alpha.FirstOrDefault();
if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0));
foreach (var loop in loopGroups)
foreach (var loop in loopingGroups)
{
command = loop.Alpha.FirstOrDefault();
if (command != null) alphaCommands.Add((command.StartTime, command.StartValue == 0));
@ -62,8 +62,8 @@ namespace osu.Game.Storyboards
{
// If we got to this point, either no alpha commands were present, or the earliest had a non-zero start value.
// The sprite's StartTime will be determined by the earliest command, regardless of type.
double earliestStartTime = Group.StartTime;
foreach (var l in loopGroups)
double earliestStartTime = Commands.StartTime;
foreach (var l in loopingGroups)
earliestStartTime = Math.Min(earliestStartTime, l.StartTime);
return earliestStartTime;
}
@ -73,9 +73,9 @@ namespace osu.Game.Storyboards
{
get
{
double latestEndTime = Group.EndTime;
double latestEndTime = Commands.EndTime;
foreach (var l in loopGroups)
foreach (var l in loopingGroups)
latestEndTime = Math.Max(latestEndTime, l.EndTime);
return latestEndTime;
@ -86,16 +86,16 @@ namespace osu.Game.Storyboards
{
get
{
double latestEndTime = Group.StartTime;
double latestEndTime = Commands.StartTime;
foreach (var l in loopGroups)
foreach (var l in loopingGroups)
latestEndTime = Math.Max(latestEndTime, l.StartTime + l.Duration * l.TotalIterations);
return latestEndTime;
}
}
public bool HasCommands => Group.HasCommands || loopGroups.Any(l => l.HasCommands);
public bool HasCommands => Commands.HasCommands || loopingGroups.Any(l => l.HasCommands);
public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition)
{
@ -109,7 +109,7 @@ namespace osu.Game.Storyboards
public StoryboardLoopingGroup AddLoopingGroup(double loopStartTime, int repeatCount)
{
var loop = new StoryboardLoopingGroup(loopStartTime, repeatCount);
loopGroups.Add(loop);
loopingGroups.Add(loop);
return loop;
}
@ -120,26 +120,20 @@ namespace osu.Game.Storyboards
return trigger;
}
public void ApplyTransforms(Drawable drawable, IEnumerable<Tuple<StoryboardCommandGroup, double>>? triggeredGroups = null)
public void ApplyTransforms<TDrawable>(TDrawable drawable)
where TDrawable : Drawable, IFlippable, IVectorScalable
{
// For performance reasons, we need to apply the commands in order by start time. Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity.
var commands = Group.GetAllCommands();
commands = commands.Concat(loopGroups.SelectMany(l => l.GetAllCommands()));
// todo: triggers are not implemented yet.
// if (triggeredGroups != null)
// commands = commands.Concat(triggeredGroups.SelectMany(tuple => tuple.Item1.GetAllCommands(tuple.Item2)));
HashSet<string> appliedProperties = new HashSet<string>();
// For performance reasons, we need to apply the commands in chronological order.
// Not doing so will cause many functions to be interleaved, resulting in O(n^2) complexity.
IEnumerable<IStoryboardCommand> commands = Commands.AllCommands;
commands = commands.Concat(loopingGroups.SelectMany(l => l.AllCommands));
foreach (var command in commands.OrderBy(c => c.StartTime))
{
if (!appliedProperties.Contains(command.PropertyName))
{
if (appliedProperties.Add(command.PropertyName))
command.ApplyInitialValue(drawable);
appliedProperties.Add(command.PropertyName);
}
using (drawable.BeginAbsoluteSequence(command.StartTime))
command.ApplyTransforms(drawable);
@ -147,26 +141,5 @@ namespace osu.Game.Storyboards
}
public override string ToString() => $"{Path}, {Origin}, {InitialPosition}";
// todo: need to revisit property initialisation. apparently it has to be done per first command of every affected property (transforms are supposed to do that already?).
// private void generateCommands<T>(List<IGeneratedCommand> resultList, IEnumerable<StoryboardCommandList<T>.TypedCommand> commands,
// DrawablePropertyInitializer<T> initializeProperty, DrawableTransform<T> transform, bool alwaysInitialize = true)
// {
// bool initialized = false;
//
// foreach (var command in commands)
// {
// DrawablePropertyInitializer<T>? initFunc = null;
//
// if (!initialized)
// {
// if (alwaysInitialize || command.StartTime == command.EndTime)
// initFunc = initializeProperty;
// initialized = true;
// }
//
// resultList.Add(new GeneratedCommand<T>(command, initFunc, transform));
// }
// }
}
}