# Levels

> Level Data in FlatRedBall2. Use when working with level layouts, level progression, loading TMX maps, generating collision from tile layers, or transitioning between levels. Covers TMX-based level setup, TileShapes generation, and level advancement patterns.

- Skill: `vchelaru/levels` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/levels`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/levels/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/levels

---


# Level Data in FlatRedBall2

Levels are defined as TMX files using the Tiled map format. Use the `tmx` skill to create or edit TMX files.

## Level Setup in a Screen

Load a TMX file with `TileMap`, add it to the screen, and generate collision:

```csharp
using FlatRedBall2.Tiled;

public class GameScreen : Screen
{
    private TileShapes _solidCollision = null!;

    public override void CustomInitialize()
    {
        var map = new TileMap("Content/Tiled/Level1.tmx", Engine.GraphicsDevice);
        map.CenterOn(0, 0); // center map on world origin
        Add(map);

        _solidCollision = map.GenerateCollisionFromClass("SolidCollision");
        Add(_solidCollision);
    }
}
```

**Key types** (all in `FlatRedBall2.Tiled`):
- `TileMap` — loads a TMX file, wraps layers, generates collision
- `TileMapLayer` — per-layer Z, visibility, and render layer control
- `TileShapes` — collision grid (`FlatRedBall2.Collision`)

## TileMap Position

`TileMap.X` and `TileMap.Y` define the **top-left corner** of the map (Tiled convention). Default (0, 0) places the top-left at the world origin; the map extends right (+X) and down (−Y).

`CenterOn(worldX, worldY)` repositions the map so its center is at the given point.

## Layer Z-Order

Layers are assigned Z values automatically: 1 apart in TMX order, with `GameplayLayer` at Z = 0 if it exists. Entities at Z = 0 naturally interleave at the gameplay layer. Override per-layer Z only when needed:

```csharp
map.GetLayer("Foreground").Z = 100f;
```

## Multiple Collision Types

```csharp
var solid = map.GenerateCollisionFromClass("SolidCollision");
var jumpThrough = map.GenerateCollisionFromClass("JumpThroughCollision");
Add(solid);
Add(jumpThrough);
```

Each collection can have its own collision relationship. For jump-through platforms, set `OneWayDirection = OneWayDirection.Up` and `CanDropThrough = true` on the relationship — the player passes through from below, lands from above, and can drop down with Down+Jump. For hard one-way barriers (`OneWayCollision` tile class — Yoshi-style ratchet doors), set `OneWayDirection = OneWayDirection.Up` and leave `CanDropThrough = false`. See the `collision-relationships` skill for details. By default all tile layers *and* object layers are scanned — hand-drawn rectangle/polygon `<object>`s with a matching Class are collision sources too, not just painted tiles (see "Collision from hand-drawn object-layer shapes" below). Passing `layerName` restricts the scan to a single **tile** layer, skipping object layers entirely:

```csharp
var solid = map.GenerateCollisionFromClass("SolidCollision", layerName: "GameplayLayer");
```

## Slope Tiles and Sub-Cell Shapes

Tiles in the tileset can declare custom collision via an `<objectgroup>` containing polygons and/or plain `<object>` rectangles. `GenerateCollisionFromClass` emits polygons as `Polygon` tiles and rectangles as sub-cell `AARect`s, instead of the default full-cell rect. Polygons and rects can coexist on the same tile. For platformer floors, set `SlopeMode = PlatformerFloor` on the **collision relationship** (not on the collection) so vertical separation uses a heightmap instead of SAT:

```csharp
var solid = map.GenerateCollisionFromClass("SolidCollision");
Add(solid);

var playerVsSolid = AddCollisionRelationship(_playerFactory, solid);
playerVsSolid.SlopeMode = SlopeCollisionMode.PlatformerFloor;
playerVsSolid.BounceFirstOnCollision(elasticity: 0f);
```

`SlopeMode` is a per-relationship concern: the same `solid` collection can simultaneously back a player relationship (`PlatformerFloor`) and a ball relationship (default `Standard` SAT) without conflict.

See the `tmx` skill for how to author the polygon on the tileset tile.

## Collision from Hand-Drawn Object-Layer Shapes

For a collision boundary that doesn't match the tile grid (an irregular wall, a boss-arena floor), draw a rectangle or polygon directly on an object layer with a matching Class instead of stamping tiles — `GenerateCollisionFromClass`/`GenerateCollisionFromProperty` pick these up too. Rectangles support any position, size, and 90-degree-multiple rotation (clipped across however many cells they span); polygons and other-angle rotated rectangles must fit within roughly one cell. See the `tmx` skill's "Collision from an object layer" section for the full capability split and the rotation-pivot gotcha.

## Spawning Entities from Object Layers or Painted Tile Layers

`CreateEntities` scans both **object layers** (for precisely-placed tile objects) and **regular tile layers** (for tiles painted with Tiled's brush). Any tile whose Class matches the requested name becomes one entity — use whichever authoring path fits the designer's workflow. Painted tile layers support class-level (tileset type) custom properties but not instance-level overrides — Tiled has no per-cell property mechanism for painted tiles.

The Class-name convention disambiguates intent via *which method you call*: `GenerateCollisionFromClass("SolidCollision")` turns matching tiles into static `TileShapes` geometry; `CreateEntities("Coin", factory)` turns matching tiles into factory-spawned entities. Pick one method per Class. If a Class must feed both, call `CreateEntities` **first**: it consumes what it matches (clears the tile, removes the object), while `GenerateCollisionFromClass` consumes nothing — so collision-first builds the shape before the removal and you get a shape *and* an entity.

Place tile objects on Tiled object layers (using tiles with a Class set in the tileset). Game code spawns entities from them with `CreateEntities`:

```csharp
// Coins — center origin (default)
map.CreateEntities("Coin", _coinFactory);

// Player — feet at bottom of tile
var player = map.CreateEntities("PlayerSpawn", _playerFactory, Origin.BottomCenter)[0];

// Ceiling turret — top of tile
map.CreateEntities("CeilingTurret", _turretFactory, Origin.TopCenter);
```

The engine converts Tiled's pixel coordinates (Y-down) to world space (Y-up) and sets each spawned entity's `X`/`Y` to the world-space position of the tile object, adjusted by the `Origin` parameter.

**Source tiles are removed by default.** After spawning, `CreateEntities` clears the painted cell and removes the tile-object from its object layer so the source tile doesn't double-draw under the spawned entity. The mutation is in-memory only — the `.tmx` file is untouched, and a hot-reload repopulates the source tiles so the next spawn pass picks them up again. Pass `removeSourceTiles: false` to keep the source tile visible (e.g., the tile doubles as intentional background art). This is the opposite default from `GenerateCollisionFromClass`, which leaves source tiles visible because there the tile *is* the visual.

**Custom properties** set on tile objects in Tiled are automatically applied to matching entity properties via reflection. If a Coin entity has `public int Worth { get; set; }` and the Tiled object has a custom property `Worth=50`, it's set automatically. Supported types: `string`, `int`, `float`, `bool`. Class-level properties (defined once on the tile's type in the tileset) are merged in the same way; an instance-level property on the same key overrides the class-level value. Declare a `TiledGid` property (`int`, `uint`, `long`, or `ulong`) to have the spawning tile's Tiled GID populated automatically — opt-in only, not a member of the base `Entity` class.

**Class matching** checks the object's Class first, then falls back to the tile definition's Class in the tileset. Case-insensitive.

**Sprite + collision on the spawned entity:** author it as an `.achx` chain (single-frame is fine) rather than hand-computing offsets in `CustomInitialize` — see `animation` skill's per-frame shapes.

## Camera Bounds

`TileMap.Bounds` returns a `BoundsRectangle` for `CameraControllingEntity.Map`:

```csharp
cam.Map = map.Bounds;
```

## Map Dimensions

```csharp
map.Width       // total width in world units
map.Height      // total height in world units
map.TileWidth   // single tile width
map.TileHeight  // single tile height
```

## Rooms as Separate Screens

Each distinct room or area is its own `Screen` subclass. There is no built-in "room manager" — `MoveToScreen<T>` is the room transition mechanism. Room state is passed via the configure callback:

```csharp
MoveToScreen<Room2Screen>(s => s.RoomState = _sharedState);
```

## Level Advancement

Pass the next level index when transitioning screens:

```csharp
if (levelComplete)
{
    int next = LevelIndex + 1;
    if (next < TotalLevels)
        MoveToScreen<GameScreen>(s => s.LevelIndex = next);
    else
        MoveToScreen<GameOverScreen>(s => s.Win = true);
}
```

