Logo Mibo

⚠ Archived V5-and-below docs. These pages cover the retired APIs and the earlier releases. They are frozen. The current docs live at the site root; the V6 map guides start at Level Design Overview.

Building Platformer Levels

Platformer games revolve around spatial challenges: jumps, gaps, vertical traversal, and hazards. The Platformer module provides stamps that make designing these challenges quick and composable.

Obsolete module. Mibo.Layout.Platformer is obsolete — its vocabulary (boxes, platforms, ledges, stairs) is subsumed by Flow styles. The page remains as a pattern reference.

Importing

open Mibo.Layout

Core Platformer Patterns

The Basic Level Structure

Most platformer levels start with a foundation - ground, walls, and platforms:

let basicLevel =
    CellGrid2D.create 100 20 (System.Numerics.Vector2(32f, 32f)) System.Numerics.Vector2.Zero
    |> Layout.run (fun section ->
        section
        // Ground floor
        |> Layout.fill 0 18 100 2 GroundTile
        
        // Back wall
        |> Layout.fill 0 0 2 20 WallTile
        
        // Floating platforms to jump between
        |> Layout.section 15 12 (Platformer.platform 8 PlatformTile)
        |> Layout.section 30 10 (Platformer.platform 6 PlatformTile)
        |> Layout.section 42 8 (Platformer.platform 6 PlatformTile)
    )

Creating Jump Challenges

Players need to jump across gaps of varying difficulty. The pit and platform stamps are your primary tools:

let jumpSection =
    section
    // Start platform
    |> Layout.section 5 12 (Platformer.platform 6 PlatformTile)
    
    // Gap to jump across (clears floor cells)
    |> Layout.section 11 18 (Platformer.pit 4 2)
    
    // Landing platform
    |> Layout.section 15 14 (Platformer.platform 4 PlatformTile)
    
    // Wider, lower gap
    |> Layout.section 19 16 (Platformer.pit 6 4)
    
    // Final landing
    |> Layout.section 25 12 (Platformer.platform 8 PlatformTile)

Design tip: Wider gaps with lower landing platforms are more challenging. Vary the Y position of platforms to create engaging vertical gameplay.

Vertical Traversal with Stairs and Ledges

Stairs provide controlled ascent while ledges create one-way platforms:

let verticalSection =
    section
    // Staircase going up and right
    |> Layout.section 0 10 (Platformer.stairs 8 StepTile Platformer.UpRight)
    
    // Ledge players can drop through
    |> Layout.section 12 4 (Platformer.ledge 4 Platformer.Right PlatformTile)
    
    // Platform to catch landing
    |> Layout.section 20 8 (Platformer.platform 6 PlatformTile)
    
    // Stairs going down
    |> Layout.section 30 4 (Platformer.stairs 6 StepTile Platformer.DownRight)

Use cases: - Stairs (stairs): Use when players need to go up or down multiple tiles in a controlled manner - Ledges (ledge): Create one-way barriers or platforms players can drop through

Sloped Surfaces

Slopes create smooth, gradual height changes:

let slopeSection =
    section
    // Slope down from elevated platform
    |> Layout.section 0 6 (Platformer.slope 12 6 SlopeTile Platformer.DownRight)
    
    // Flat area after slope
    |> Layout.section 12 12 (Platformer.platform 8 PlatformTile)
    
    // Slope up (ramp to next area)
    |> Layout.section 20 12 (Platformer.slope 10 8 SlopeTile Platformer.UpLeft)

Slope design: - Use moderate slopes (rise/run of 1:2 or 1:3) for comfortable play - Steep slopes (1:1) create slip-down mechanics if you implement them - Always provide flat landing areas after slopes

Building Enclosed Areas

Box Rooms

Fully enclosed rooms with walls, floor, and ceiling:

let boxRoom =
    section
    |> Layout.section 10 5 (Platformer.box 16 12 WallTile FloorTile)
    // Add platforms inside
    |> Layout.section 13 8 (Platformer.platform 4 PlatformTile)
    |> Layout.section 18 9 (Platformer.platform 4 PlatformTile)

Combining Boxes and Pits

Create complex obstacle courses:

let obstacleCourse =
    section
    // Starting area (box with floor)
    |> Layout.section 5 10 (Platformer.box 10 6 WallTile FloorTile)
    
    // Challenging jump section
    |> Layout.section 15 14 (Platformer.pit 6 6)
    
    // Small landing platform
    |> Layout.section 21 14 (Platformer.platform 3 PlatformTile)
    
    // Hazardous pit (spikes at bottom)
    |> Layout.section 24 14 (Platformer.pit 4 8)
    |> Layout.section 25 18 (Layout.fill 0 0 4 2 SpikeTile)
    
    // Safe landing area
    |> Layout.section 28 10 (Platformer.box 8 8 WallTile FloorTile)

Decorative Elements

Pillars and Columns

Pillars add visual interest and can serve as hiding spots or climbable surfaces:

let decoratedRoom =
    section
    |> Layout.fill 0 10 20 10 FloorTile
    |> Layout.fill 0 0 2 10 WallTile
    
    // Decorative pillars
    |> Layout.section 5 10 (Platformer.pillar 6 PillarBase PillarMid PillarTop)
    |> Layout.section 10 10 (Platformer.pillar 6 PillarBase PillarMid PillarTop)
    |> Layout.section 15 10 (Platformer.pillar 6 PillarBase PillarMid PillarTop)

Pillar usage: - 2-3 tiles high for visual pillars - 4-6 tiles high for climbable columns (if you implement climbing) - Vary the base/middle/top tiles for variety

Building Custom Stamps

Encapsulate common patterns in reusable stamps:

module MyPlatformer =
    /// A checkpoint platform with a flag
    let checkpoint width =
        Platformer.platform width PlatformTile
        >> Layout.set (width / 2) 1 FlagTile
    
    /// A pit with hazards at the bottom
    let hazardPit width depth hazardCount =
        Platformer.pit width depth
        >> Layout.section 0 (depth - 1) (Layout.repeatX 0 0 hazardCount SpikeTile)
    
    /// A floating platform with supports
    let floatingPlatform width =
        Platformer.platform width PlatformTile
        >> Layout.set 0 1 SupportTile
        >> Layout.set (width - 1) 1 SupportTile
    
    /// A room with a treasure chest
    let treasureRoom width height =
        Platformer.box width height WallTile FloorTile
        >> Layout.center 1 1 (Layout.set 0 0 ChestTile)

Using Custom Stamps

let customLevel =
    section
    |> Layout.section 5 10 (MyPlatformer.checkpoint 4)
    |> Layout.section 15 12 (MyPlatformer.hazardPit 6 8 4)
    |> Layout.section 25 10 (MyPlatformer.floatingPlatform 6)
    |> Layout.section 35 8 (MyPlatformer.treasureRoom 10 8)

Multi-Layer Platformer

Use LayeredGrid2D for separate collision, decoration, and entity layers:

let layeredPlatformer =
    LayeredGrid2D.create 100 25 (System.Numerics.Vector2(32f, 32f)) System.Numerics.Vector2.Zero
    |> LayeredLayout.layer 0 (fun section ->
        // Layer 0: Collision (walls, platforms, hazards)
        section
        |> Layout.fill 0 20 100 5 GroundTile
        
        // Platforms
        |> Layout.section 5 12 (Platformer.platform 10 PlatformTile)
        |> Layout.section 20 10 (Platformer.platform 8 PlatformTile)
        |> Layout.section 35 8 (Platformer.platform 6 PlatformTile)
        
        // Hazardous pit
        |> Layout.section 42 14 (Platformer.pit 6 8)
        |> Layout.section 43 18 (Layout.fill 0 0 6 2 SpikeTile)
        
        // Enclosed challenge room
        |> Layout.section 50 8 (Platformer.box 15 12 WallTile FloorTile)
        |> Layout.section 55 10 (Platformer.platform 4 PlatformTile)
        |> Layout.section 62 10 (Platformer.platform 4 PlatformTile)
    )
    |> LayeredLayout.layer 1 (fun section ->
        // Layer 1: Decorations (torches, flags, markers)
        section
        |> Layout.section 8 11 (Layout.set 0 0 TorchTile)
        |> Layout.section 24 9 (Layout.set 0 0 TorchTile)
        |> Layout.section 40 7 (Layout.set 0 0 TorchTile)
        |> Layout.section 55 11 (Layout.set 0 0 FlagTile)
        |> Layout.section 75 15 (Layout.set 0 0 ExitSignTile)
        
        // Rug in challenge room
        |> Layout.section 55 15 (Layout.fill 1 1 10 4 RugTile)
    )
    |> LayeredLayout.layer 2 (fun section ->
        // Layer 2: Entities (spawns, interactables)
        section
        |> Layout.section 8 16 (Layout.set 0 0 SpawnTile)
        |> Layout.section 55 14 (Layout.set 0 0 ChestTile)
        |> Layout.section 63 12 (Layout.set 0 0 EnemyTile)
        |> Layout.section 65 12 (Layout.set 0 0 EnemyTile)
        |> Layout.section 75 18 (Layout.set 0 0 NextLevelTriggerTile)
    )

When to use layers in platformer games: - Layer 0: Solid collision (walls, floors, platforms, spikes) - Layer 1: Non-colliding decorations (torches, flags, particles) - Layer 2: Entities and triggers (player spawns, enemies, checkpoints)

This separation allows you to: - Render decorations without affecting collision - Clear entity layer between level transitions - Different tile types per layer without conflicts

Complete Level Example

Here's a full level showcasing multiple patterns:

let levelSection section =
    section
    // === AREA 1: Start ===
    // Spawn room
    |> Layout.fill 0 18 20 2 GroundTile
    |> Layout.fill 0 10 2 10 WallTile
    |> Layout.section 2 12 (Layout.set 0 0 SpawnTile)
    
    // === AREA 2: Basic Jumps ===
    // Series of platforms
    |> Layout.section 20 14 (Platformer.platform 5 PlatformTile)
    |> Layout.section 25 12 (Platformer.platform 4 PlatformTile)
    |> Layout.section 29 10 (Platformer.platform 4 PlatformTile)
    
    // === AREA 3: Vertical Ascent ===
    // Staircase
    |> Layout.section 33 6 (Platformer.stairs 8 StepTile Platformer.UpRight)
    
    // Ledge section
    |> Layout.section 45 4 (Platformer.ledge 6 Platformer.Right PlatformTile)
    |> Layout.section 55 4 (Platformer.platform 6 PlatformTile)
    
    // === AREA 4: Hazardous Descent ===
    // Slope down
    |> Layout.section 63 6 (Platformer.slope 10 6 SlopeTile Platformer.DownRight)
    
    // Pit with spikes
    |> Layout.section 73 12 (Platformer.pit 4 8)
    |> Layout.section 74 18 (Layout.fill 0 0 4 2 SpikeTile)
    
    // === AREA 5: Enclosed Challenge ===
    // Box room with internal platforms
    |> Layout.section 77 2 (Platformer.box 12 10 WallTile FloorTile)
    |> Layout.section 80 4 (Platformer.platform 3 PlatformTile)
    |> Layout.section 85 6 (Platformer.platform 3 PlatformTile)
    
    // Pillar decorations
    |> Layout.section 80 2 (Platformer.pillar 8 PillarBase PillarMid PillarTop)
    |> Layout.section 86 2 (Platformer.pillar 8 PillarBase PillarMid PillarTop)
    
    // Treasure
    |> Layout.section 83 5 (Layout.set 0 0 ChestTile)

let level =
    CellGrid2D.create 100 20 (System.Numerics.Vector2(32f, 32f)) System.Numerics.Vector2.Zero
    |> Layout.run levelSection

Common Platformer Level Patterns

The "Gauntlet" Run

Platforms get smaller and further apart:

let gauntletSection =
    section
    |> Layout.section 5 14 (Platformer.platform 8 PlatformTile)
    |> Layout.section 13 12 (Platformer.platform 6 PlatformTile)
    |> Layout.section 19 10 (Platformer.platform 4 PlatformTile)
    |> Layout.section 23 8 (Platformer.platform 3 PlatformTile)
    |> Layout.section 26 6 (Platformer.platform 2 PlatformTile)

The "Climb and Fall" Sequence

Staircase up followed by a controlled drop:

let climbAndFall =
    section
    |> Layout.section 0 10 (Platformer.stairs 10 StepTile Platformer.UpRight)
    |> Layout.section 12 2 (Platformer.ledge 4 Platformer.Left PlatformTile)
    |> Layout.section 20 6 (Platformer.platform 6 PlatformTile)
    |> Layout.section 26 10 (Platformer.stairs 8 StepTile Platformer.DownRight)

The "Hazard Valley"

Pits with hazards between safe platforms:

let hazardValley =
    section
    |> Layout.section 5 14 (Platformer.platform 6 PlatformTile)
    |> Layout.section 11 14 (Platformer.pit 4 8)
    |> Layout.section 12 18 (Layout.fill 0 0 4 2 SpikeTile)
    |> Layout.section 15 12 (Platformer.platform 5 PlatformTile)
    |> Layout.section 20 12 (Platformer.pit 4 8)
    |> Layout.section 21 18 (Layout.fill 0 0 4 2 SpikeTile)
    |> Layout.section 25 14 (Platformer.platform 6 PlatformTile)

Design Tips

Difficulty Progression

Start with easy jumps and gradually increase challenge: - Early levels: Wider platforms, smaller gaps - Mid levels: Mix of slopes, smaller platforms - Late levels: Hazardous gaps, vertical sections

Visual Clarity

Make hazards and playable areas visually distinct: - Use different tiles for hazardous pits - Add visual markers for spawn points and checkpoints - Use consistent wall/floor colors for readability

Playtesting

Always playtest your sections: - Are jumps too wide/narrow? - Are hazard pits fair or frustrating? - Is the level flow obvious to players?

See also: 2D Layout Engine for the layout primitives this module builds on.

val basicLevel: obj
namespace System
namespace System.Numerics
Multiple items
type Vector2 = new: value: float32 -> unit + 2 overloads member CopyTo: array: float32 array -> unit + 2 overloads member Equals: other: Vector2 -> bool + 2 overloads member GetHashCode: unit -> int member Length: unit -> float32 member LengthSquared: unit -> float32 member ToString: unit -> string + 2 overloads member TryCopyTo: destination: Span<float32> -> bool static member (&&&) : left: Vector2 * right: Vector2 -> Vector2 static member ( * ) : left: Vector2 * right: Vector2 -> Vector2 + 2 overloads ...
<summary>Represents a vector with two single-precision floating-point values.</summary>

--------------------
System.Numerics.Vector2 ()
System.Numerics.Vector2(value: float32) : System.Numerics.Vector2
System.Numerics.Vector2(values: System.ReadOnlySpan<float32>) : System.Numerics.Vector2
System.Numerics.Vector2(x: float32, y: float32) : System.Numerics.Vector2
property System.Numerics.Vector2.Zero: System.Numerics.Vector2 with get
<summary>Returns a vector whose 2 elements are equal to zero.</summary>
<returns>A vector whose two elements are equal to zero (that is, it returns the vector <code data-dev-comment-type="c">(0,0)</code>).</returns>
val jumpSection: obj
val verticalSection: obj
val slopeSection: obj
val boxRoom: obj
val obstacleCourse: obj
val decoratedRoom: obj
val checkpoint: width: 'a -> ('b -> 'c)
 A checkpoint platform with a flag
val width: 'a
val hazardPit: width: 'a -> depth: 'b -> hazardCount: 'c -> ('d -> 'e)
 A pit with hazards at the bottom
val depth: 'b
val hazardCount: 'c
val floatingPlatform: width: 'a -> ('b -> 'c)
 A floating platform with supports
val treasureRoom: width: 'a -> height: 'b -> ('c -> 'd)
 A room with a treasure chest
val height: 'b
val customLevel: obj
module MyPlatformer from platformer
val layeredPlatformer: obj
val levelSection: section: 'a -> 'b
val section: 'a
val level: obj
val gauntletSection: obj
val climbAndFall: obj
val hazardValley: obj

Type something to start searching.