osu/osu.Game/Graphics/Containers/WaveContainer.cs

196 lines
6.2 KiB
C#

// 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.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osuTK.Graphics;
namespace osu.Game.Graphics.Containers
{
public partial class WaveContainer : VisibilityContainer
{
public const float APPEAR_DURATION = 800;
public const float DISAPPEAR_DURATION = 500;
public const float SHADOW_OPACITY = 0.2f;
private const Easing easing_show = Easing.OutSine;
private const Easing easing_hide = Easing.InSine;
private readonly Wave firstWave;
private readonly Wave secondWave;
private readonly Wave thirdWave;
private readonly Wave fourthWave;
private readonly Container<Wave> wavesContainer;
private readonly Container contentContainer;
protected override Container<Drawable> Content => contentContainer;
protected override bool StartHidden => true;
private Sample? samplePopIn;
private Sample? samplePopOut;
// required due to LoadAsyncComplete() in `VisibilityContainer` calling PopOut() during load - similar workaround to `OsuDropdownMenu`
private bool wasShown;
public Color4 FirstWaveColour
{
get => firstWave.Colour;
set => firstWave.Colour = value;
}
public Color4 SecondWaveColour
{
get => secondWave.Colour;
set => secondWave.Colour = value;
}
public Color4 ThirdWaveColour
{
get => thirdWave.Colour;
set => thirdWave.Colour = value;
}
public Color4 FourthWaveColour
{
get => fourthWave.Colour;
set => fourthWave.Colour = value;
}
[BackgroundDependencyLoader(true)]
private void load(AudioManager audio)
{
samplePopIn = audio.Samples.Get("UI/wave-pop-in");
samplePopOut = audio.Samples.Get("UI/overlay-big-pop-out");
}
public WaveContainer()
{
Masking = true;
AddInternal(wavesContainer = new Container<Wave>
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Masking = true,
Children = new[]
{
firstWave = new Wave
{
Rotation = 13,
FinalPosition = -930,
},
secondWave = new Wave
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Rotation = -7,
FinalPosition = -560,
},
thirdWave = new Wave
{
Rotation = 4,
FinalPosition = -390,
},
fourthWave = new Wave
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Rotation = -2,
FinalPosition = -220,
},
},
});
AddInternal(contentContainer = new Container
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
});
}
protected override void PopIn()
{
foreach (var w in wavesContainer)
w.Show();
contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint);
samplePopIn?.Play();
wasShown = true;
}
protected override void PopOut()
{
foreach (var w in wavesContainer)
w.Hide();
contentContainer.MoveToY(2, DISAPPEAR_DURATION, Easing.In);
if (wasShown)
samplePopOut?.Play();
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
// This is done as an optimization, such that invisible parts of the waves
// are masked away, and thus do not consume fill rate.
// todo: revert https://github.com/ppy/osu/commit/aff9e3617da0c8fe252169fae287e39b44575b5e after FTB is fixed on iOS.
wavesContainer.Height = Math.Max(0, DrawHeight - (contentContainer.DrawHeight - contentContainer.Y * DrawHeight));
}
private partial class Wave : VisibilityContainer
{
public float FinalPosition;
protected override bool StartHidden => true;
public Wave()
{
RelativeSizeAxes = Axes.X;
Width = 1.5f;
Masking = true;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(50),
Radius = 20f,
};
Child = new Box { RelativeSizeAxes = Axes.Both };
}
protected override void Update()
{
base.Update();
// We can not use RelativeSizeAxes for Height, because the height
// of our parent diminishes as the content moves up.
Height = Parent!.Parent!.DrawSize.Y * 1.5f;
}
protected override void PopIn() => Schedule(() => this.MoveToY(FinalPosition, APPEAR_DURATION, easing_show));
protected override void PopOut()
{
double duration = IsLoaded ? DISAPPEAR_DURATION : 0;
// scheduling is required as parent may not be present at the time this is called.
Schedule(() => this.MoveToY(Parent!.Parent!.DrawSize.Y, duration, easing_hide));
}
}
}
}