← Documentation Home

3) Your First Interactive Program

Back to Tutorials

Goal: open a graphics window, create a responsive game loop, move a player, and render a heads-up display.

Time: about 30 minutes. No external assets are required.


Step 1: Create the Window

SetAppTitle("Movement Tutorial")
Graphics(800, 600, False)
SetClsColor(12, 18, 32)
SetVSync(True)

Graphics establishes a virtual 800 × 600 coordinate system. Windowed and fullscreen builds use those same game coordinates, which keeps layout code predictable.

Step 2: Add the Frame Loop

Dim running As Boolean
running = True

Do
    Cls()

    ' Update and drawing will go here.

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

Cls begins a clean frame. Flip presents the queued drawing and processes window events. The loop stops after Escape is pressed once.

Step 3: Distinguish Held and Pressed Input

  • KeyDown remains True while a key is held, so use it for movement.
  • KeyHit reports a new press, so use it for pause, menus, or single actions.
If KeyDown(KEY_LEFT) Then
    playerX = playerX - moveAmount
End If
If KeyDown(KEY_RIGHT) Then
    playerX = playerX + moveAmount
End If
If KeyDown(KEY_UP) Then
    playerY = playerY - moveAmount
End If
If KeyDown(KEY_DOWN) Then
    playerY = playerY + moveAmount
End If

Step 4: Make Movement Frame-Rate Independent

FrameDelta returns elapsed seconds. Multiplying speed by it makes motion cover approximately the same distance at different frame rates.

Dim delta As Double
Dim moveAmount As Integer

delta = FrameDelta()
moveAmount = Round(240.0 * delta)

Positions stay integer-based at the LunarBasic game boundary. Round converts the fractional movement result into a whole number.

Step 5: Keep the Player On Screen

If playerX < 16 Then
    playerX = 16
End If
If playerX > ScreenWidth() - 16 Then
    playerX = ScreenWidth() - 16
End If
If playerY < 16 Then
    playerY = 16
End If
If playerY > ScreenHeight() - 16 Then
    playerY = ScreenHeight() - 16
End If

ScreenWidth and ScreenHeight return the virtual dimensions, so this code remains correct if you later change the resolution.

Step 6: Draw the Player and HUD

SetDrawColor(70, 210, 255)
DrawFilledRect(playerX - 16, playerY - 16, 32, 32)

SetDrawColor(255, 255, 255)
DrawCircle(playerX, playerY, 24, 32)

SetTextColor(235, 240, 255)
DrawText(fontRef, 16, 16, "Arrow keys move. ESC exits.")
DrawText(fontRef, 16, 42, _
    "Position: " + Str(playerX) + ", " + Str(playerY))

Drawing calls queue work for the next Flip. Draw the world first and UI last so the UI remains visible.

Checkpoint: Complete Movement Demo

SetAppTitle("Movement Tutorial")
Graphics(800, 600, False)
SetClsColor(12, 18, 32)
SetVSync(True)

Dim fontRef As Integer
Dim playerX As Integer
Dim playerY As Integer
Dim moveAmount As Integer
Dim delta As Double
Dim running As Boolean

fontRef = LoadFont("", 20)
playerX = ScreenWidth() / 2
playerY = ScreenHeight() / 2
running = True

Do
    delta = FrameDelta()
    moveAmount = Round(240.0 * delta)

    If KeyDown(KEY_LEFT) Then
        playerX = playerX - moveAmount
    End If
    If KeyDown(KEY_RIGHT) Then
        playerX = playerX + moveAmount
    End If
    If KeyDown(KEY_UP) Then
        playerY = playerY - moveAmount
    End If
    If KeyDown(KEY_DOWN) Then
        playerY = playerY + moveAmount
    End If

    If playerX < 16 Then
        playerX = 16
    End If
    If playerX > ScreenWidth() - 16 Then
        playerX = ScreenWidth() - 16
    End If
    If playerY < 16 Then
        playerY = 16
    End If
    If playerY > ScreenHeight() - 16 Then
        playerY = ScreenHeight() - 16
    End If

    Cls()
    SetDrawColor(70, 210, 255)
    DrawFilledRect(playerX - 16, playerY - 16, 32, 32)
    SetDrawColor(255, 255, 255)
    DrawCircle(playerX, playerY, 24, 32)

    SetTextColor(235, 240, 255)
    DrawText(fontRef, 16, 16, "Arrow keys move. ESC exits.")
    DrawText(fontRef, 16, 42, _
        "Position: " + Str(playerX) + ", " + Str(playerY))

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

FreeFont(fontRef)

Common Mistakes

  • Calling Graphics, LoadFont, or other setup functions every frame. Initialize once before the loop.
  • Using KeyHit for movement. It only reports the initial press.
  • Forgetting Flip. Queued drawing will not be presented and events will not advance.
  • Forgetting to free loaded handles after the loop.

Try It Yourself

Add Shift-to-sprint, change the player color while sprinting, and use MouseX()/MouseY() to draw a target under the pointer.

Functions Introduced

Graphics, Cls, Flip, FrameDelta, KeyDown, KeyHit, DrawFilledRect, DrawCircle, and DrawText.


Navigation: Previous | Course outline | Next: Drawing, Camera, and Buffers