A component representing a falling power-up pickup in the Breakout game. It moves downward each fixed update, checks for collision with the paddle to apply PowerUpEffect components, plays a sound and spawns score popups, and destroys itself when collected or when falling past a death Z.
using System;
using Sandbox;
namespace Breakout;
/// <summary>
/// A pickup that falls from a broken brick. If the paddle catches it, every PowerUpEffect on this
/// object is applied (wide paddle, extra ball, and so on). If it falls past the bottom edge
/// instead, it just disappears.
/// </summary>
[Title( "Power Up" ), Category( "Breakout" ), Icon( "redeem" )]
public sealed class PowerUp : Component
{
[Property] public float FallSpeed { get; set; } = 130f;
[Property] public float CatchHeight { get; set; } = 24f;
public Playfield Playfield { get; set; }
public BreakoutGame Game { get; set; }
protected override void OnEnabled()
{
Playfield ??= Scene.Get<Playfield>();
Game ??= Scene.Get<BreakoutGame>();
GameObject.Tags.Add( "powerup" );
}
protected override void OnFixedUpdate()
{
if ( Playfield is null )
return;
var pos = WorldPosition;
pos.z -= FallSpeed * Time.Delta;
pos.y = Playfield.PlayY;
WorldPosition = pos;
var paddle = Scene.Get<Paddle>();
if ( paddle.IsValid() && IsCaughtBy( paddle, pos ) )
{
ApplyEffects( paddle );
GameObject.Destroy();
return;
}
if ( pos.z < Playfield.DeathZ )
GameObject.Destroy();
}
private bool IsCaughtBy( Paddle paddle, Vector3 pos )
{
float dx = MathF.Abs( pos.x - paddle.WorldPosition.x );
float dz = MathF.Abs( pos.z - paddle.WorldPosition.z );
return dx <= paddle.HalfWidth && dz <= CatchHeight;
}
private void ApplyEffects( Paddle paddle )
{
var caught = Sound.Play( "ball_impact_pad_01" );
if ( caught is not null )
caught.Pitch = 1.7f;
Game?.ShakeCamera( 0.25f );
foreach ( var effect in Components.GetAll<PowerUpEffect>( FindMode.EnabledInSelfAndDescendants ) )
{
effect.Apply( Game, paddle );
if ( !string.IsNullOrEmpty( effect.Label ) )
ScorePopup.Spawn( Scene, WorldPosition + Vector3.Up * 20f, effect.Label, effect.LabelColor, 0.3f );
}
}
}