Entities/Obstacle.cs

An Entity2D representing a static rectangular interior obstacle in the arena. It stores position/size/depth, a glass flag that alters collision semantics for players, provides Setup to initialize transform, and CreateVisuals to add a sprite layer for rendering.

File Access
namespace BlockParty;

/// <summary>
/// A static solid rectangle inside the arena (an interior "wall" region a level can place —
/// e.g. a block in the centre, or the filled bottom-middle of a C-shaped arena). Being an
/// <see cref="Entity2D"/> means the shared collision helpers (<see cref="Entity2D.DoesPenetrate"/>,
/// <see cref="Entity2D.Unpenetrate"/>, the directional <c>other.X > X</c> filters) treat it as a
/// solid exactly like a block — so the player lands on / wall-jumps off / is crushed against it and
/// moving blocks bounce off it, all through the existing block-collision paths.
///
/// Obstacles never move and never <see cref="Entity2D.Tick"/>; they are NOT added to the stage's
/// <c>Blocks</c> list (so they don't count toward the win condition or score). Their solidity is
/// gated for the player by <see cref="CharacterAbilities.CollidesWithArenaWalls"/>, matching the
/// outer arena walls: a phasing/wrap character passes through them (and wraps across — see
/// <see cref="WrapArenaEdgesAbility"/>).
/// </summary>
public sealed class Obstacle : Entity2D
{
	/// <summary>True for a GLASS panel: an obstacle solid ONLY to players — every character collides
	/// with it, including wall-phasing/wrap ones (see <c>Player.ObstacleSolidToUs</c>), while blocks,
	/// hazards, lasers and fields all pass straight through. Glass lives in the stage's normal
	/// obstacle list so every player collision/crush/spike path treats it like an interior wall for
	/// free; the block/hazard views skip flagged entries instead (see
	/// <c>GameStage.GetSolidObstacles</c>). Authored spikes on its faces are player-lethal like any
	/// obstacle face.</summary>
	public bool IsGlass { get; set; }

	/// <summary>Position + size in logical pixels; the parent GameObject is placed at the arena
	/// wall depth. Renders as a single solid fill quad (spikes on its faces are drawn separately
	/// by the stage's wall-face system).</summary>
	public void Setup( Vector2 center, Vector2 size )
	{
		Pos = center;
		Size = size;
		Depth = Globals.DEPTH_WALL;
	}

	public void CreateVisuals( Color fillColor, Vector2? visualCenter = null, Vector2? visualSize = null )
	{
		var sr = SpriteLayer.Add( GameObject, "sprites/pixel.sprite", visualSize ?? Size, "idle" );
		sr.Opaque = true;
		sr.Color = fillColor;
		if ( visualCenter is Vector2 center )
		{
			var local = sr.GameObject.LocalPosition;
			sr.GameObject.LocalPosition = new Vector3( center.x - Pos.x, center.y - Pos.y, local.z );
		}
		SyncTransform();
	}
}