Transposer/Entities/PixelParticle.cs

A small particle entity representing a colored pixel particle. It stores direction, speed, lifetime and easing state, moves with easing, bounces off screen edges, fades out over lifetime, and removes itself when expired.

Native Interop
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) ) );
	}
}