# Shapes

> Working with Shapes in FlatRedBall2. Use when working with AARect, Circle, Polygon, TileShapes, shape creation, visibility, color, IsFilled, OutlineThickness, or visual properties of shapes. Also covers SolidSides for one-way platforms and tile grids. Trigger on any shape-related question.

- Skill: `vchelaru/shapes` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add vchelaru/shapes`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/shapes/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/shapes

---


# Working with Shapes in FlatRedBall2

All shape types and `TileShapes` are in the `FlatRedBall2.Collision` namespace — add `using FlatRedBall2.Collision;` in any file that uses them.

FlatRedBall2 has three built-in shape types: `AARect`, `Circle`, and `Polygon`. All shapes implement both `IRenderable` and `ICollidable`, so they handle both drawing and collision.

## Shape Types

| Type | Key Properties | Notes |
|------|---------------|-------|
| `AARect` | `Width`, `Height` | Cannot rotate |
| `Circle` | `Radius` | |
| `Polygon` | `Points`, `Rotation` | Use factory methods to create |

> **Exposing a shape from an Entity:** make the field a public auto-property (`public Circle Circle { get; private set; }`) and let callers write `entity.Circle.Color = ...` directly. Do not wrap it in a forwarding property like `FillColor` — that pattern silently fails when assigned after `CustomInitialize`. See `entities-and-factories` → `references/reactive-properties.md`.

## Step 1: Create a Shape

```csharp
var rect = new AARect { X = 0, Y = 0, Width = 64, Height = 64 };
var circle = new Circle { X = 100, Y = 0, Radius = 32 };

// Polygon factory methods:
var poly = Polygon.CreateRectangle(64, 64);         // rotatable rectangle
var custom = Polygon.FromPoints(new[] {
    new Vector2(0, 0), new Vector2(50, 0), new Vector2(25, 40),
});
// Concave polygons are fully supported for both rendering and collision.
// FromPoints automatically decomposes concave shapes into convex parts (Hertel-Mehlhorn)
// for correct collision — no manual decomposition needed.
```

## Step 2: Make the Shape Visible

Shapes default to `IsVisible = false`. **Always set `IsVisible = true`** or the shape won't render.

## Step 3: Add to the Render Pipeline

**Option A — Directly on the Screen**: Call `Add` in `CustomInitialize`:
```csharp
Add(rect);
```

**Option B — Attached to an Entity**: `entity.Add(shape)` auto-adds to the render pipeline (as long as `Engine` is set).

> **Note:** `entity.Add(child)` only auto-registers if the entity already has an `Engine` reference (via `Factory` or `Screen.Register`).

## Visual Properties

```csharp
var rect = new AARect
{
    Color = new Color(220, 60, 60, 200),   // RGBA
    IsFilled = false,                       // true = solid fill, false = outline
    OutlineThickness = 3f,                  // line width in pixels
    IsVisible = true,
};
```

> **Polygon fill:** `IsFilled = true` ear-clip triangulates and fills the interior (works for concave polygons). `IsFilled = false` renders outline only. Set `OutlineThickness = 0` to suppress the border when filled.

> **Dashed/dotted outlines, gradients, drop shadows:** FRB shapes render solid outlines only. For richer visualization, drop into Gum's `RectangleRuntime` — Gum ships with every FRB2 game, so there's nothing to add (see `gum-integration`). Docs: https://docs.flatredball.com/gum/code/standard-visuals/rectangleruntime

## Cleanup

```csharp
rect.Destroy();   // removes from parent entity and clears references
```

For shapes added directly to the screen (not via `entity.Add`), also call `Remove(rect)`.

## Common Pitfalls

- **Shape is invisible** — forgot `IsVisible = true`. Default is `false`.
- **Shape is not drawn** — forgot `Add(shape)` on screen, or `entity.Add(shape)` before entity was registered.
- **Shape position looks wrong** — Y+ is up (see `physics-and-movement`). In platformers, entity origin = feet, so offset shapes upward by `Height/2` (see `platformer-movement` skill).
- **Polygon not rotating** — use `Polygon`, not `AARect`.

## TileShapes

`TileShapes` is a grid-based static collision structure for tile maps. Set `X`, `Y`, and `GridSize` before adding tiles — positions are computed at insertion time and not updated if those properties change later.

```csharp
var tiles = new TileShapes { X = 0f, Y = 0f, GridSize = 16f };

// X, Y are the world position of the bottom-left corner of cell (0, 0)
tiles.AddTileAtCell(int col, int row);      // by grid index
tiles.AddTileAtWorld(float x, float y);    // snapped to nearest cell

tiles.RemoveTileAtCell(col, row);
tiles.GetTileAtCell(col, row);             // returns AARect? for inspection

tiles.IsVisible = true;                      // debug visualization
```

`SolidSides` are maintained automatically on every add/remove — interior shared edges between adjacent tiles are cleared to prevent seam snagging. No manual refresh needed.

Integrates with `AddCollisionRelationship` — see `collision-relationships` skill.

## SolidSides (AARect only)

Controls which sides of a rectangle act as solid collision surfaces. Use for one-way platforms (`SolidSides.Up` = only top is solid) and removing interior sides from adjacent tiles to prevent seam snagging.

Default is `SolidSides.All`. Values combine with `|` and `&= ~`.

For detailed usage and dynamic tile grids, see:
- `references/reposition-directions.md` — Full examples, dynamic grids, Gum naming conflict workaround

### Debug overlay

`Overlay.DrawSolidSides` visualizes RD state on the set of rectangles you ask about: each active face shows a small green triangle inside the rect pointing toward that face; suppressed faces show nothing. Pass a `Factory<T>` to inspect every entity's first `AARect` child, or a `TileShapes` to inspect every tile. There is no parameterless overload — the caller always specifies what to see.

```csharp
public override void CustomActivity(FrameTime time)
{
    Overlay.DrawSolidSides(_brickFactory);
    Overlay.DrawSolidSides(_solidTiles);
}
```

