Transposer/Entities/PixelParticle.cs
namespace Sandbox.Transposer;
/// <summary>
/// Generic coloured particle with direction, speed, lifetime, and easing.
/// Bounces off screen edges and fades out. Used for the white explosion
/// effect when collecting a coin.
/// </summary>
public class PixelParticle : Entity
{
private Color _color;
private float _speedMax;
private Vector2 _direction;
private float _lifetimeMax;
private float _lifetime; // Counts DOWN — drives opacity (full → transparent).
private float _elapsedTime; // Counts UP — drives speed easing (fast → slow).
public PixelParticle( Color color, float x, float y, TransposerScene scene,
Vector2 direction, float speed, float lifetime )
{
_color = color;
_scene = scene;
_speedMax = speed;
_direction = direction;
_hitbox = new PixelRect( 0, 0, 1, 1 );
_lifetimeMax = lifetime;
_lifetime = lifetime;
_collideable = true;
ExactX = x;
ExactY = y;
_layer = Globals.DEPTH_PARTICLE;
}
public override void UpdateEntity( float deltaTime )
{
base.UpdateEntity( deltaTime );
_elapsedTime += deltaTime;
// Speed eases from full to 20% over the particle's lifetime — fast burst, slow drift.
float speed = PixelUtils.Map( _elapsedTime, 0f, _lifetimeMax, _speedMax, _speedMax * 0.2f, EaseType.SineOut );
Vector2 newPos = ExactPos + _direction * speed * deltaTime;
int newX = (int)MathF.Round( newPos.x );
int newY = (int)MathF.Round( newPos.y );
if ( newX < 3 || newX > Screen.PixelWidth - 4 )
_direction = new Vector2( -_direction.x, _direction.y );
else if ( newY < 3 || newY > Screen.PixelHeight - 4 )
_direction = new Vector2( _direction.x, -_direction.y );
else
ExactPos = newPos;
_lifetime -= deltaTime;
if ( _lifetime <= 0f )
_scene.RemoveEntity( this );
}
public override void Draw()
{
float opacity = PixelUtils.Map( _lifetime, _lifetimeMax, 0f, 1f, 0f, EaseType.SineOut );
Screen.AddPixel( PixelX, PixelY, new Color32( (byte)(_color.r * 255), (byte)(_color.g * 255), (byte)(_color.b * 255), (byte)(opacity * 255f) ) );
}
}