Blocks/BlockSpawnIn.cs

A component that animates a block popping in. It captures the block's intended LocalScale, sets it to a tiny start scale on enable, then over Duration (after an optional Delay) eases the scale up with an overshoot using an EaseOutBack curve and destroys itself when finished.

using Sandbox;

namespace Breakout;

/// <summary>
/// Plays the "pop in" animation when a brick appears: it starts tiny and scales up to full size
/// with a slight overshoot, then removes itself. Delay lets rows stagger their entrance so the
/// wall drops in from top to bottom.
/// </summary>
[Title( "Block Spawn In" ), Category( "Breakout" ), Icon( "animation" )]
public sealed class BlockSpawnIn : Component
{
	[Property] public float Delay { get; set; }

	[Property] public float Duration { get; set; } = 0.24f;

	private const float StartScale = 0.02f;

	private Vector3 targetScale;
	private bool captured;
	private TimeSince sinceEnabled;

	protected override void OnEnabled()
	{
		if ( !captured )
		{
			targetScale = LocalScale;
			captured = true;
		}

		LocalScale = targetScale * StartScale;
		sinceEnabled = 0f;
	}

	protected override void OnUpdate()
	{
		if ( Duration <= 0f )
		{
			LocalScale = targetScale;
			Destroy();
			return;
		}

		float t = (sinceEnabled - Delay) / Duration;

		if ( t < 0f )
		{
			LocalScale = targetScale * StartScale;
			return;
		}

		if ( t >= 1f )
		{
			LocalScale = targetScale;
			Destroy();
			return;
		}

		LocalScale = targetScale * EaseOutBack( t );
	}

	// Easing curve that shoots a little past full size and settles back, giving each brick a
	// springy pop as it appears.
	private static float EaseOutBack( float t )
	{
		const float s = 1.70158f;
		t -= 1f;
		return 1f + t * t * ((s + 1f) * t + s);
	}
}