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.
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 ) ) );
}
}