← Documentation Home

8) Build a Physics Game (Pro)

Back to Tutorials

Goal: build a playable physics scene with a grounded jump, a rotating crate, a goal, and collision filtering.

License: this tutorial requires the Pro GamePhysics feature.


Step 1: Create the World and Static Ground

Dim world As Integer
Dim ground As Integer

world = CreatePhysicsWorld(0.0, 900.0)
ground = CreatePhysicsBox(world, 400, 570, 800, 60, True)
SetBodyFriction(ground, 0.9)
SetBodyRestitution(ground, 0.0)

Positive Y gravity points downward. Body positions are centers, so a ground centered at Y 570 with height 60 reaches the bottom of a 600-pixel screen.

Step 2: Add a Controllable Body

Dim player As Integer
player = CreatePhysicsBox(world, 120, 120, 36, 48, False)
SetBodyFriction(player, 0.7)
SetBodyRestitution(player, 0.0)
SetBodyRotationLocked(player, True)

Rotation locking is useful for platform-game characters. The body still collides normally but will not tip over from contact impulses.

Step 3: Preserve Vertical Velocity While Moving

Dim horizontalSpeed As Double
horizontalSpeed = 0.0

If KeyDown(KEY_LEFT) Then
    horizontalSpeed = -220.0
End If
If KeyDown(KEY_RIGHT) Then
    horizontalSpeed = 220.0
End If

SetBodyVelocity( _
    player, _
    horizontalSpeed, _
    BodyVelocityY(player) _
)

Replacing only the X component keeps gravity and jumping in control of the Y component.

Step 4: Identify the Body Under the Player

Dim grounded As Boolean
Dim contactIndex As Integer
Dim otherBody As Integer

grounded = False
For contactIndex = 0 To BodyCollisionCount(player) - 1
    otherBody = BodyCollisionOther(player, contactIndex)
    If otherBody = ground Then
        grounded = True
    End If
Next contactIndex

If grounded And KeyHit(KEY_SPACE) Then
    SetBodyVelocity(player, BodyVelocityX(player), -430.0)
End If

Checking the other handle answers “what am I touching?” and prevents jumping merely because the player touched an unrelated body.

Step 5: Allow Other Bodies to Rotate

Dim crate As Integer
crate = CreatePhysicsBox(world, 440, 180, 64, 64, False)
SetBodyFriction(crate, 0.65)
SetBodyRestitution(crate, 0.05)
SetBodyRotation(crate, 12.0)

The crate can rotate naturally. While rendering, use BodyRotation with the draw-state transform so its visual shape stays aligned with its collision shape.

Step 6: Filter Collision Categories

Const CATEGORY_PLAYER As Integer = 1
Const CATEGORY_WORLD As Integer = 2
Const CATEGORY_GOAL As Integer = 4
Const CATEGORY_CRATE As Integer = 8

SetBodyCollisionFilter( _
    player, CATEGORY_PLAYER, _
    CATEGORY_WORLD + CATEGORY_GOAL + CATEGORY_CRATE _
)
SetBodyCollisionFilter( _
    ground, CATEGORY_WORLD, _
    CATEGORY_PLAYER + CATEGORY_CRATE _
)

Each body declares what it is and what it accepts. Distinct power-of-two category flags can be combined by addition.

Step 7: Advance Physics Before Rendering It

UpdatePhysics(world, FrameDelta())

PushDrawState()
SetDrawOffset(BodyX(crate), BodyY(crate))
SetDrawRotation(BodyRotation(crate))
DrawFilledRect(-32, -32, 64, 64)
PopDrawState()

UpdatePhysics uses an internal fixed 120 Hz accumulator. Call it once per game frame with FrameDelta(); do not write your own substep loop around it.

Checkpoint: Reach the Goal

Const CATEGORY_PLAYER As Integer = 1
Const CATEGORY_WORLD As Integer = 2
Const CATEGORY_GOAL As Integer = 4
Const CATEGORY_CRATE As Integer = 8

Graphics(800, 600, False)
SetClsColor(10, 15, 26)

Dim fontRef As Integer
Dim world As Integer
Dim player As Integer
Dim ground As Integer
Dim crate As Integer
Dim goal As Integer
Dim horizontalSpeed As Double
Dim grounded As Boolean
Dim reachedGoal As Boolean
Dim running As Boolean
Dim i As Integer
Dim otherBody As Integer

fontRef = LoadFont("", 18)
world = CreatePhysicsWorld(0.0, 900.0)
ground = CreatePhysicsBox(world, 400, 570, 800, 60, True)
player = CreatePhysicsBox(world, 100, 180, 36, 48, False)
crate = CreatePhysicsBox(world, 410, 180, 64, 64, False)
goal = CreatePhysicsCircle(world, 720, 510, 24, True)
running = True

SetBodyFriction(ground, 0.9)
SetBodyFriction(player, 0.7)
SetBodyFriction(crate, 0.65)
SetBodyRestitution(player, 0.0)
SetBodyRestitution(crate, 0.05)
SetBodyRotationLocked(player, True)
SetBodyRotation(crate, 12.0)

SetBodyCollisionFilter(player, CATEGORY_PLAYER, _
    CATEGORY_WORLD + CATEGORY_GOAL + CATEGORY_CRATE)
SetBodyCollisionFilter(ground, CATEGORY_WORLD, _
    CATEGORY_PLAYER + CATEGORY_CRATE)
SetBodyCollisionFilter(crate, CATEGORY_CRATE, _
    CATEGORY_PLAYER + CATEGORY_WORLD + CATEGORY_CRATE)
SetBodyCollisionFilter(goal, CATEGORY_GOAL, CATEGORY_PLAYER)

Do
    horizontalSpeed = 0.0
    If KeyDown(KEY_LEFT) Then
        horizontalSpeed = -220.0
    End If
    If KeyDown(KEY_RIGHT) Then
        horizontalSpeed = 220.0
    End If
    SetBodyVelocity(player, horizontalSpeed, BodyVelocityY(player))

    grounded = False
    For i = 0 To BodyCollisionCount(player) - 1
        otherBody = BodyCollisionOther(player, i)
        If otherBody = ground Then
            grounded = True
        End If
        If otherBody = goal Then
            reachedGoal = True
        End If
    Next i

    If grounded And KeyHit(KEY_SPACE) Then
        SetBodyVelocity(player, BodyVelocityX(player), -430.0)
    End If

    UpdatePhysics(world, FrameDelta())

    Cls()
    SetDrawColor(75, 100, 140)
    DrawFilledRect(0, 540, 800, 60)

    SetDrawColor(255, 185, 65)
    PushDrawState()
    SetDrawOffset(BodyX(crate), BodyY(crate))
    SetDrawRotation(BodyRotation(crate))
    DrawFilledRect(-32, -32, 64, 64)
    PopDrawState()

    SetDrawColor(80, 220, 120)
    DrawCircle(BodyX(goal), BodyY(goal), 24, 32)

    SetDrawColor(80, 200, 255)
    DrawFilledRect(BodyX(player) - 18, BodyY(player) - 24, 36, 48)

    SetTextColor(240, 244, 255)
    If reachedGoal Then
        DrawText(fontRef, 20, 20, "Goal reached! ESC exits.")
    Else
        DrawText(fontRef, 20, 20, "Arrows move, Space jumps. Reach the ring.")
    End If
    Flip()

    If KeyHit(KEY_ESC) Then
        running = False
    End If
Loop Until Not running

FreePhysicsBody(goal)
FreePhysicsBody(crate)
FreePhysicsBody(player)
FreePhysicsBody(ground)
FreePhysicsWorld(world)
FreeFont(fontRef)

Stability and Performance Rules

  • Use static bodies for terrain and objects that should not move.
  • Use realistic sizes and velocities; extremely deep overlaps require stronger correction.
  • Keep restitution near zero for resting stacks and increase it only for intentionally bouncy objects.
  • Use collision masks to exclude pairs that can never interact.
  • Let sleeping bodies rest. Avoid rewriting positions or velocities every frame unless gameplay requires it.
  • Free destroyed bodies and worlds when leaving a level.

Try It Yourself

Add a sloped static platform with SetBodyRotation, toggle the crate between dynamic and static with SetBodyStatic, and make a second category that the player ignores.

Functions Introduced

CreatePhysicsWorld, CreatePhysicsBox, UpdatePhysics, SetBodyRotationLocked, BodyCollisionOther, and SetBodyCollisionFilter.


Navigation: Previous | Course outline | 2D Physics Guide