1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-16 16:17:52 +08:00
osu-lazer/osu.Game/Screens/Menu/OsuLogo.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

489 lines
20 KiB
C#
Raw Normal View History

// 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.
2018-04-13 17:19:50 +08:00
2022-06-17 15:37:17 +08:00
#nullable disable
2016-10-07 16:07:23 +08:00
using System;
2016-11-14 16:23:33 +08:00
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
2017-05-24 00:17:09 +08:00
using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions;
2016-10-07 16:07:23 +08:00
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
2016-10-07 16:07:23 +08:00
using osu.Framework.Graphics.Sprites;
2016-11-14 16:23:33 +08:00
using osu.Framework.Graphics.Textures;
2018-10-02 11:02:47 +08:00
using osu.Framework.Input.Events;
2020-01-09 12:43:44 +08:00
using osu.Framework.Utils;
2017-05-24 00:17:09 +08:00
using osu.Game.Beatmaps.ControlPoints;
2016-12-01 19:21:14 +08:00
using osu.Game.Graphics.Backgrounds;
2017-05-23 11:29:43 +08:00
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
2018-11-20 15:51:59 +08:00
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
2018-04-13 17:19:50 +08:00
2016-11-14 16:23:33 +08:00
namespace osu.Game.Screens.Menu
2016-10-07 16:07:23 +08:00
{
/// <summary>
/// osu! logo and its attachments (pulsing, visualiser etc.)
/// </summary>
2017-05-23 11:29:43 +08:00
public partial class OsuLogo : BeatSyncedContainer
2016-10-07 16:07:23 +08:00
{
public readonly Color4 OsuPink = Color4Extensions.FromHex(@"e967a1");
2018-04-13 17:19:50 +08:00
private const double transition_length = 300;
2018-04-13 17:19:50 +08:00
/// <summary>
/// The osu! logo sprite has a shadow included in its texture.
/// This adjustment vector is used to match the precise edge of the border of the logo.
/// </summary>
public static readonly Vector2 SCALE_ADJUST = new Vector2(0.96f);
private readonly Sprite logo;
private readonly CircularContainer logoContainer;
private readonly Container logoBounceContainer;
2017-05-23 11:29:43 +08:00
private readonly Container logoBeatContainer;
2017-05-24 00:45:01 +08:00
private readonly Container logoAmplitudeContainer;
private readonly Container logoHoverContainer;
private readonly MenuLogoVisualisation visualizer;
2018-04-13 17:19:50 +08:00
2017-11-03 16:54:35 +08:00
private readonly IntroSequence intro;
2018-04-13 17:19:50 +08:00
2021-01-19 16:11:40 +08:00
private Sample sampleClick;
private SampleChannel sampleClickChannel;
2021-01-19 16:11:40 +08:00
private Sample sampleBeat;
private Sample sampleDownbeat;
2018-04-13 17:19:50 +08:00
private readonly Container colourAndTriangles;
2017-05-24 12:29:12 +08:00
private readonly Triangles triangles;
2018-04-13 17:19:50 +08:00
/// <summary>
/// Return value decides whether the logo should play its own sample for the click action.
/// </summary>
public Func<bool> Action;
2018-04-13 17:19:50 +08:00
/// <summary>
/// The size of the logo Sprite with respect to the scale of its hover and bounce containers.
/// </summary>
/// <remarks>Does not account for the scale of this <see cref="OsuLogo"/></remarks>
2019-03-28 14:40:58 +08:00
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X;
2018-04-13 17:19:50 +08:00
public bool IsTracking { get; set; }
private readonly Sprite ripple;
2018-04-13 17:19:50 +08:00
private readonly Container rippleContainer;
2018-04-13 17:19:50 +08:00
2016-12-01 19:21:14 +08:00
public bool Triangles
{
set => colourAndTriangles.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint);
2016-12-01 19:21:14 +08:00
}
2018-04-13 17:19:50 +08:00
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => logoContainer.ReceivePositionalInputAt(screenSpacePos);
2018-04-13 17:19:50 +08:00
2016-10-07 16:07:23 +08:00
public bool Ripple
{
get => rippleContainer.Alpha > 0;
set => rippleContainer.FadeTo(value ? 1 : 0, transition_length, Easing.OutQuint);
2016-10-07 16:07:23 +08:00
}
2018-04-13 17:19:50 +08:00
2020-11-23 13:27:02 +08:00
private const float visualizer_default_alpha = 0.5f;
private readonly Box flashLayer;
2018-04-13 17:19:50 +08:00
private readonly Container impactContainer;
2018-04-13 17:19:50 +08:00
private const double early_activation = 60;
2018-04-13 17:19:50 +08:00
private const float triangles_paused_velocity = 0.5f;
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
2016-10-07 16:07:23 +08:00
public OsuLogo()
{
EarlyActivationMilliseconds = early_activation;
2018-04-13 17:19:50 +08:00
2016-10-07 16:07:23 +08:00
Origin = Anchor.Centre;
2018-04-13 17:19:50 +08:00
2016-10-22 16:50:42 +08:00
AutoSizeAxes = Axes.Both;
2018-04-13 17:19:50 +08:00
2016-10-07 16:07:23 +08:00
Children = new Drawable[]
{
2017-11-03 16:54:35 +08:00
intro = new IntroSequence
{
RelativeSizeAxes = Axes.Both,
},
logoHoverContainer = new Container
2016-10-07 16:07:23 +08:00
{
2016-10-22 16:50:42 +08:00
AutoSizeAxes = Axes.Both,
2016-10-07 16:07:23 +08:00
Children = new Drawable[]
{
2022-10-27 09:33:07 +08:00
logoBounceContainer = new Container
2016-10-07 16:07:23 +08:00
{
2016-10-22 17:40:04 +08:00
AutoSizeAxes = Axes.Both,
2016-12-01 19:21:14 +08:00
Children = new Drawable[]
{
rippleContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
ripple = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
2019-08-21 12:29:50 +08:00
Blending = BlendingParameters.Additive,
Alpha = 0
}
}
},
2017-05-24 00:45:01 +08:00
logoAmplitudeContainer = new Container
2016-12-01 19:21:14 +08:00
{
AutoSizeAxes = Axes.Both,
2016-12-01 19:21:14 +08:00
Children = new Drawable[]
{
2017-05-24 00:45:01 +08:00
logoBeatContainer = new Container
2016-12-01 19:21:14 +08:00
{
2017-05-23 11:29:43 +08:00
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
visualizer = new MenuLogoVisualisation
2017-06-19 08:33:50 +08:00
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
2020-11-23 13:27:02 +08:00
Alpha = visualizer_default_alpha,
Size = SCALE_ADJUST
2017-06-19 08:33:50 +08:00
},
new Container
{
2017-05-24 00:45:01 +08:00
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
2017-05-24 00:45:01 +08:00
logoContainer = new CircularContainer
{
2017-05-23 11:29:43 +08:00
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
2017-05-24 00:45:01 +08:00
RelativeSizeAxes = Axes.Both,
Scale = SCALE_ADJUST,
2017-05-24 00:45:01 +08:00
Masking = true,
2017-05-23 11:29:43 +08:00
Children = new Drawable[]
{
2017-05-24 00:45:01 +08:00
colourAndTriangles = new Container
2017-05-23 11:29:43 +08:00
{
RelativeSizeAxes = Axes.Both,
2017-05-24 00:45:01 +08:00
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuPink,
},
triangles = new Triangles
2017-05-24 00:45:01 +08:00
{
TriangleScale = 4,
ColourLight = Color4Extensions.FromHex(@"ff7db7"),
ColourDark = Color4Extensions.FromHex(@"de5b95"),
2017-05-24 00:45:01 +08:00
RelativeSizeAxes = Axes.Both,
},
}
2017-05-23 11:29:43 +08:00
},
2017-05-24 00:45:01 +08:00
flashLayer = new Box
2017-05-23 11:29:43 +08:00
{
RelativeSizeAxes = Axes.Both,
2019-08-21 12:29:50 +08:00
Blending = BlendingParameters.Additive,
2017-05-24 00:45:01 +08:00
Colour = Color4.White,
Alpha = 0,
2017-05-23 11:29:43 +08:00
},
2017-05-24 00:45:01 +08:00
},
},
2017-05-24 00:45:01 +08:00
logo = new Sprite
{
2017-05-24 00:45:01 +08:00
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
2017-05-24 00:45:01 +08:00
}
},
2017-05-24 00:45:01 +08:00
impactContainer = new CircularContainer
{
2017-05-23 11:29:43 +08:00
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
2017-05-24 00:45:01 +08:00
Alpha = 0,
BorderColour = Color4.White,
RelativeSizeAxes = Axes.Both,
BorderThickness = 10,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
AlwaysPresent = true,
Alpha = 0,
}
}
2017-05-23 11:29:43 +08:00
}
}
}
}
2016-10-07 16:07:23 +08:00
}
}
}
}
}
};
}
2018-04-13 17:19:50 +08:00
/// <summary>
2019-08-31 01:48:45 +08:00
/// Schedule a new external animation. Handled queueing and finishing previous animations in a sane way.
/// </summary>
/// <param name="action">The animation to be performed</param>
/// <param name="waitForPrevious">If true, the new animation is delayed until all previous transforms finish. If false, existing transformed are cleared.</param>
public void AppendAnimatingAction(Action action, bool waitForPrevious)
{
2018-01-16 02:42:17 +08:00
void runnableAction()
{
if (waitForPrevious)
this.DelayUntilTransformsFinished().Schedule(action);
else
{
ClearTransforms();
action();
}
2018-01-16 02:42:17 +08:00
}
2018-04-13 17:19:50 +08:00
if (IsLoaded)
runnableAction();
else
2018-01-16 04:27:00 +08:00
Schedule(runnableAction);
}
2018-04-13 17:19:50 +08:00
[BackgroundDependencyLoader]
2018-08-31 06:04:40 +08:00
private void load(TextureStore textures, AudioManager audio)
2016-10-07 16:07:23 +08:00
{
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
sampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
2018-04-13 17:19:50 +08:00
2018-08-31 06:04:40 +08:00
logo.Texture = textures.Get(@"Menu/logo");
ripple.Texture = textures.Get(@"Menu/logo");
2016-11-01 22:24:14 +08:00
}
2018-04-13 17:19:50 +08:00
2017-05-24 00:45:01 +08:00
private int lastBeatIndex;
2018-04-13 17:19:50 +08:00
2020-06-23 12:49:18 +08:00
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
2017-05-23 11:29:43 +08:00
{
2017-05-24 00:17:09 +08:00
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
2018-04-13 17:19:50 +08:00
2017-05-24 00:45:01 +08:00
lastBeatIndex = beatIndex;
2018-04-13 17:19:50 +08:00
double beatLength = timingPoint.BeatLength;
2018-04-13 17:19:50 +08:00
2017-05-24 00:45:01 +08:00
float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum);
2018-04-13 17:19:50 +08:00
2017-05-24 00:17:09 +08:00
if (beatIndex < 0) return;
2018-04-13 17:19:50 +08:00
if (Action != null && IsHovered)
{
this.Delay(early_activation).Schedule(() =>
{
if (beatIndex % timingPoint.TimeSignature.Numerator == 0)
{
sampleDownbeat?.Play();
}
else
{
var channel = sampleBeat.GetChannel();
channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1);
channel.Play();
}
});
}
2018-04-13 17:19:50 +08:00
2017-07-16 23:28:20 +08:00
logoBeatContainer
2020-11-23 13:27:02 +08:00
.ScaleTo(1 - 0.02f * amplitudeAdjust, early_activation, Easing.Out).Then()
2017-07-23 02:50:25 +08:00
.ScaleTo(1, beatLength * 2, Easing.OutQuint);
2018-04-13 17:19:50 +08:00
ripple.ClearTransforms();
2017-07-16 23:28:20 +08:00
ripple
.ScaleTo(logoAmplitudeContainer.Scale)
2017-07-23 02:50:25 +08:00
.ScaleTo(logoAmplitudeContainer.Scale * (1 + 0.04f * amplitudeAdjust), beatLength, Easing.OutQuint)
.FadeTo(0.15f * amplitudeAdjust).FadeOut(beatLength, Easing.OutQuint);
2018-04-13 17:19:50 +08:00
2017-05-24 00:17:09 +08:00
if (effectPoint.KiaiMode && flashLayer.Alpha < 0.4f)
2017-05-23 15:27:01 +08:00
{
flashLayer.ClearTransforms();
2017-07-16 23:28:20 +08:00
flashLayer
2020-11-23 13:27:02 +08:00
.FadeTo(0.2f * amplitudeAdjust, early_activation, Easing.Out).Then()
2017-07-16 23:28:20 +08:00
.FadeOut(beatLength);
2018-04-13 17:19:50 +08:00
2017-07-16 23:28:20 +08:00
visualizer.ClearTransforms();
visualizer
2020-11-23 13:27:02 +08:00
.FadeTo(visualizer_default_alpha * 1.8f * amplitudeAdjust, early_activation, Easing.Out).Then()
.FadeTo(visualizer_default_alpha, beatLength);
2017-05-23 15:27:01 +08:00
}
this.Delay(early_activation).Schedule(() =>
2022-07-21 11:38:33 +08:00
{
triangles.Velocity += amplitudeAdjust * (effectPoint.KiaiMode ? 6 : 3);
});
2017-05-23 11:29:43 +08:00
}
2018-04-13 17:19:50 +08:00
2017-11-03 16:54:35 +08:00
public void PlayIntro()
{
2017-11-08 13:31:11 +08:00
const double length = 3150;
const double fade = 200;
2018-04-13 17:19:50 +08:00
2017-11-03 16:54:35 +08:00
logoHoverContainer.FadeOut().Delay(length).FadeIn(fade);
intro.Show();
intro.Start(length);
intro.Delay(length + fade).FadeOut();
}
2018-04-13 17:19:50 +08:00
[Resolved]
private MusicController musicController { get; set; }
2017-05-24 00:45:01 +08:00
protected override void Update()
{
base.Update();
2018-04-13 17:19:50 +08:00
2017-05-24 12:05:28 +08:00
const float scale_adjust_cutoff = 0.4f;
2018-04-13 17:19:50 +08:00
2020-08-07 19:51:56 +08:00
if (musicController.CurrentTrack.IsRunning)
{
float maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0;
logoAmplitudeContainer.Scale = new Vector2((float)Interpolation.Damp(logoAmplitudeContainer.Scale.X, 1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 0.9f, Time.Elapsed));
2018-04-13 17:19:50 +08:00
2022-08-02 17:18:40 +08:00
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, triangles_paused_velocity * (IsKiaiTime ? 4 : 2), 0.995f, Time.Elapsed);
}
else
{
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, triangles_paused_velocity, 0.9f, Time.Elapsed);
}
2017-05-24 00:45:01 +08:00
}
2018-04-13 17:19:50 +08:00
public override bool HandlePositionalInput => base.HandlePositionalInput && Alpha > 0.2f;
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override bool OnMouseDown(MouseDownEvent e)
2016-10-07 16:07:23 +08:00
{
if (e.Button != MouseButton.Left) return true;
2017-07-23 02:50:25 +08:00
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
2016-10-07 16:07:23 +08:00
return true;
}
2018-04-13 17:19:50 +08:00
protected override void OnMouseUp(MouseUpEvent e)
2016-10-07 16:07:23 +08:00
{
if (e.Button != MouseButton.Left) return;
2017-07-23 02:50:25 +08:00
logoBounceContainer.ScaleTo(1f, 500, Easing.OutElastic);
2016-10-07 16:07:23 +08:00
}
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override bool OnClick(ClickEvent e)
2016-10-07 16:07:23 +08:00
{
2017-02-25 20:12:39 +08:00
flashLayer.ClearTransforms();
flashLayer.Alpha = 0.4f;
2017-07-23 02:50:25 +08:00
flashLayer.FadeOut(1500, Easing.OutExpo);
if (Action?.Invoke() == true)
{
StopSamplePlayback();
sampleClickChannel = sampleClick.GetChannel();
sampleClickChannel.Play();
}
2016-10-07 16:07:23 +08:00
return true;
}
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override bool OnHover(HoverEvent e)
2016-10-07 16:07:23 +08:00
{
if (Action != null)
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
2016-10-07 16:07:23 +08:00
return true;
}
2018-04-13 17:19:50 +08:00
2018-10-02 11:02:47 +08:00
protected override void OnHoverLost(HoverLostEvent e)
2016-10-07 16:07:23 +08:00
{
2017-07-23 02:50:25 +08:00
logoHoverContainer.ScaleTo(1, 500, Easing.OutElastic);
2016-10-07 16:07:23 +08:00
}
2018-04-13 17:19:50 +08:00
public void Impact()
{
2017-07-23 02:50:25 +08:00
impactContainer.FadeOutFromOne(250, Easing.In);
impactContainer.ScaleTo(SCALE_ADJUST);
impactContainer.ScaleTo(1.12f, 250);
}
2022-10-27 09:33:07 +08:00
public override bool DragBlocksClick => false;
2022-10-27 09:33:07 +08:00
protected override bool OnDragStart(DragStartEvent e) => true;
2022-10-27 09:33:07 +08:00
protected override void OnDrag(DragEvent e)
{
Vector2 change = e.MousePosition - e.MouseDownPosition;
2022-10-27 09:33:07 +08:00
// Diminish the drag distance as we go further to simulate "rubber band" feeling.
change *= change.Length <= 0 ? 0 : MathF.Pow(change.Length, 0.6f) / change.Length;
2022-10-27 09:33:07 +08:00
logoBounceContainer.MoveTo(change);
}
2022-10-27 09:33:07 +08:00
protected override void OnDragEnd(DragEndEvent e)
{
logoBounceContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
base.OnDragEnd(e);
}
private Container defaultProxyTarget;
private Container currentProxyTarget;
private Drawable proxy;
public void StopSamplePlayback() => sampleClickChannel?.Stop();
public Drawable ProxyToContainer(Container c)
{
if (currentProxyTarget != null)
throw new InvalidOperationException("Previous proxy usage was not returned");
if (defaultProxyTarget == null)
throw new InvalidOperationException($"{nameof(SetupDefaultContainer)} must be called first");
currentProxyTarget = c;
defaultProxyTarget.Remove(proxy, false);
currentProxyTarget.Add(proxy);
return proxy;
}
public void ReturnProxy()
{
if (currentProxyTarget == null)
throw new InvalidOperationException("No usage to return");
if (defaultProxyTarget == null)
throw new InvalidOperationException($"{nameof(SetupDefaultContainer)} must be called first");
currentProxyTarget.Remove(proxy, false);
currentProxyTarget = null;
defaultProxyTarget.Add(proxy);
}
public void SetupDefaultContainer(Container container)
{
defaultProxyTarget = container;
defaultProxyTarget.Add(this);
defaultProxyTarget.Add(proxy = CreateProxy());
}
2016-10-07 16:07:23 +08:00
}
2017-05-24 01:53:21 +08:00
}