Game/Playfield.cs

Component that defines the rectangular play area and coordinate convention for the Breakout game. It stores bounds (MinX/MaxX/TopZ/DeathZ/PlayY), grid cell size, offers convenience properties Width and CenterX, clamp and death-check helpers, and draws editor gizmos showing the walls and death line.

File Access
using Sandbox;

namespace Breakout;

/// <summary>
/// Describes the rectangular play area and the 2.5D coordinate setup the whole game uses: X is
/// left/right, Z is up/down (height on screen), and Y is depth into the screen. Everything plays
/// out on one flat sheet at Y = PlayY, so most code keeps Y pinned there. The ball, paddle and
/// power-ups all read these bounds to know where the walls and the bottom "death line" are.
/// DrawGizmos() just draws the outline in the editor.
/// </summary>
[Title( "Playfield" ), Category( "Breakout" ), Icon( "crop_free" )]
public sealed class Playfield : Component
{
	[Property] public float MinX { get; set; } = -320f;   // left wall

	[Property] public float MaxX { get; set; } = 320f;    // right wall

	[Property] public float TopZ { get; set; } = 360f;    // top wall

	[Property] public float DeathZ { get; set; } = -360f; // below this, a ball is lost

	[Property] public float PlayY { get; set; } = 0f;     // the depth everything sits at

	[Property, Group( "Grid" )] public float CellWidth { get; set; } = 64f;

	[Property, Group( "Grid" )] public float CellHeight { get; set; } = 28f;

	/// <summary>
	/// The full left-to-right width of the play area.
	/// </summary>
	public float Width => MaxX - MinX;

	/// <summary>
	/// The horizontal midpoint of the play area.
	/// </summary>
	public float CenterX => (MinX + MaxX) * 0.5f;

	/// <summary>
	/// Keeps an X position inside the side walls, leaving halfExtent of room on each side.
	/// </summary>
	public float ClampX( float x, float halfExtent ) => x.Clamp( MinX + halfExtent, MaxX - halfExtent );

	/// <summary>
	/// True when a position has dropped below the bottom death line (used to spot a lost ball).
	/// </summary>
	public bool IsBelowDeath( Vector3 pos ) => pos.z < DeathZ;

	protected override void DrawGizmos()
	{
		float y = PlayY;

		var topLeft = new Vector3( MinX, y, TopZ );
		var topRight = new Vector3( MaxX, y, TopZ );
		var bottomLeft = new Vector3( MinX, y, DeathZ );
		var bottomRight = new Vector3( MaxX, y, DeathZ );

		Gizmo.Draw.IgnoreDepth = true;
		Gizmo.Draw.LineThickness = 2f;

		Gizmo.Draw.Color = Color.Cyan;
		Gizmo.Draw.Line( bottomLeft, topLeft );
		Gizmo.Draw.Line( bottomRight, topRight );
		Gizmo.Draw.Line( topLeft, topRight );

		Gizmo.Draw.Color = Color.Red;
		Gizmo.Draw.Line( bottomLeft, bottomRight );

		Gizmo.Draw.Color = Color.Red.WithAlpha( 0.85f );
		Gizmo.Draw.Text( "Death line", new Transform( new Vector3( CenterX, y, DeathZ - 16f ) ) );
	}
}