Skip to content

Latest commit

 

History

History
245 lines (209 loc) · 12.7 KB

examples.adoc

File metadata and controls

245 lines (209 loc) · 12.7 KB

Example gallery

The repository’s examples/ directory holds complete apps and focused recipes. This chapter helps you choose which to read. It assumes you have run an example, as [run-an-example] shows.

Each example is a directory with a main.roc. Choose the one closest to what you want to build and copy its directory. For the released examples, download the examples zip described in [run-an-example]. The examples in the repository name the platform in the checkout instead, and may use APIs that have not been released yet; [development-checkout] shows how to run them.

Run every command from the directory that contains examples/, so the paths the examples use for their files resolve. To try an example, run it with roc:

roc examples/snake/main.roc

To build an executable you can run again, use roc build:

roc build examples/snake/main.roc --output=snake
./snake

Every example quits when you press Escape unless its header says otherwise.

Start here

Work through these in order. Each one adds a little to the one before it.

  1. Hello World (hello_world): the complete app loop (init!, update!, and render!) with drawing and the pointer, and nothing else.

  2. Move Box (move_box): a box moved with the arrow keys. It introduces moving by speed times elapsed time, and a pure function tested with expect. [first-game] grows the same idea into a whole game, one step at a time; Move Box shows the finished result on one page, ready to copy.

  3. Sprite and Sound (sprite_and_sound): a picture and a sound loaded from files. It introduces a permission, an asset store, and loaded resources.

  4. Pong (pong): one player against a computer paddle. It combines movement, collision, input, scoring, and sounds made in code in one approachable file.

  5. Snake (snake): a grid game split across a few modules, with a fixed simulation step and seeded randomness.

  6. Task Sleep (task_sleep): one task that waits, and the message it sends back. Read it before adding files, networking, or any other operation that waits.

Then pick a direction:

Small apps worth copying

Example What it is Patterns to reuse

Hello World
hello_world

A polished minimal interactive scene

App lifecycle, prepared text, pointer input

Move Box
move_box

A box you steer with the arrow keys

Movement by elapsed time, clamping the time step, keeping an object inside the window, testing a pure function

Sprite and Sound
sprite_and_sound

A pixel-art blob that hops and plays a sound

Declaring a directory, opening an asset store, loading a PNG texture and a WAV sound

Pong
pong

One player against a computer paddle

Movement by elapsed time, collision, scoring, sounds made in code

Snake
snake

A grid-based game with restartable state

Fixed simulation step, keyboard control, seeded randomness in the model

Breakout
breakout

A complete game that can record its own demo

Separating game logic from effects, sounds played from game events, a recording mode

Pixel Workshop
generated_assets

A tiny paint program with a texture it changes as you paint

Textures and sounds made in code, uploading one changed cell, a pure editor returning edits

Responsive Settings
responsive_ui

A resizable settings screen

Pure layout shared by update! and render!, mouse and keyboard navigation, minimum window size, display scale and monitors

Postcard Studio
postcard_studio

A generative postcard editor that exports at twice the window size

Drawing into an offscreen texture, exporting it from a task, output directories

Input Inspector
input_inspector

A live view of every input device, and the clipboard

Device snapshots, the ordered event record, clipboard, window changes, reading the pixel under the pointer, an app that keeps Escape for itself

Drop Viewer
drop_viewer

An image viewer whose only control is dropping a file on it

Accepting a dropped file, reading it in a task, choosing a decoder from the bytes

Tasks that wait

Use these when copying the Task.spawn!(input, || ...) pattern described in [tasks]. A task’s return value arrives as one Msg on a later input.messages.

Example What it is

Task Sleep
task_sleep

One task, one Task.sleep!, and one message back, which is the shape of every other task on this page. It also prints straight from update!

Async Read
async_read

Two file reads and a metadata! call running at once, each with its own Msg variant, so none of them needs an id

Capture Screenshot
capture_screenshot

A screenshot saved in a task, and the output directory refusing a path that climbs out of it

HTTP Fetch
http_fetch

An HTTP GET in a task, which you can restart while it is still running; each reply includes the id of the fetch it belongs to

UDP Cursor
udp_cursor

Two instances showing each other’s pointer: a receiving task that is started again each time it returns, and sends made straight from update!

SQLite Scores
sqlite_scores

A high-score board that survives a restart: the write and the re-read share one task, so the model always gets what the database actually contains

Larger showcases

Use these to see several systems working together in one app.

Example What it demonstrates

Spark Run
top_down

A top-down game: a level made in the Tiled map editor, sprites, collision, music, sound effects, a following camera, and several game states

Cave Climb
cave_climb

A platformer with a mirror-bouncing laser and a spring grapple: tilemaps, collision, and camera tracking, split across two modules

Live Plot
live_plot

A whole source tree listed, read, and parsed through a bounded queue of tasks, and drawn as a scrolling strip of a quarter of a million points

Focused recipes

Use these as focused references, not starter projects.

Example Use it when you need…​

Camera World
camera

World and screen coordinates, a following camera, zoom, and rotation

Projective Texture
projective_texture

A draggable perspective-correct quad and points placed with the same projection

Post Process
post_process

Offscreen textures, blend modes, shaders, and shader settings

Particles
particles

Thousands of sprites drawn in one batch rather than one call each

Capture Plot
capture_plot

A repeatable WebM recording made in a hidden window

Capture UI Demo
capture_ui_demo

A repeatable interface recording driven through the real pointer and keyboard

Conventions used here

  • Each app names its three functions' parameters the same way: init! takes io, update! takes model, input, io and reads devices through devices = input.devices, and render! takes model, frame.

  • Keep application state in Model; compute the next model from App.Input in pure helper functions that update! calls.

  • Load and prepare long-lived resources once in init!, then keep them in the model instead of recreating them every host cycle.

  • Derive layout and view-only values with pure helpers. Use frame.size!() for the surface being drawn to instead of copying input.window.size into the model.

  • Move by elapsed time, and clamp it: after a pause, one host cycle can report a second or more.

  • Call host effects directly from update! when they return immediately; use Task.spawn!(input, || ...) for anything that waits, and update the model with its result when it arrives as a message on a later input.messages.

  • Give each kind of task its own Msg variant. Add an id only when several tasks of the same kind may be running at once.

  • Keep game logic in pure functions so roc test can check them without calling effectful update!.

  • Let App.default handle Escape. An app that needs Escape for itself sets with_exit_key(NoExitKey) and says why, as Input Inspector does.

  • Keep capture, scripted input, and headless runs out of ordinary interaction: an example never saves, records, or quits by itself during an interactive run.

Exhaustive API probes and invalid-input cases belong in the repository’s test/ directory, where they can be explicit without making an example harder to understand.

The animations in the repository README are recorded by the examples themselves. Breakout, Pixel Workshop, Responsive Settings, Postcard Studio, Particles, Spark Run, and Live Plot each take a --record-demo argument:

roc examples/breakout/main.roc -- --record-demo

The window stays hidden, a scripted demo plays through the app’s normal input path, and the recording is written to examples/gallery/<directory>.gif. The app exits once the file is written. Every one of these apps follows the same shape: a demo_recording value, started in init! when the argument is present, and a pure demo_finished that ends the run when input.capture reports the recording Finished or Failed.

Permissions in the examples

An example declares, in its startup config, anything it accesses beyond its own resources, as [permissions] describes. Nothing is passed on the command line.

Example Declares Why

sprite_and_sound

Directory("examples/sprite_and_sound/assets", ReadOnly)

Loads its picture and sound from that directory

post_process

Directory("examples/post_process/assets", ReadOnly)

Loads its shader from that directory

top_down

Directory("examples/top_down/assets", ReadOnly)

Loads its map, textures, music, and sounds

cave_climb

Directory("examples/cave_climb/assets", ReadOnly)

Loads its map and textures

async_read

WorkingDirectory(ReadOnly)

Reads files beneath the directory it runs from

live_plot

WorkingDirectory(ReadOnly)

Lists and reads the whole tree it runs from

input_inspector

ClipboardRead, ClipboardWrite

Copies and pastes text

http_fetch

HttpOrigin(url) for the one URL it fetches

Fetches that URL, and nothing else

udp_cursor

UdpLoopback

Sends and receives datagrams on this machine only

sqlite_scores

No permission; names itself with with_app_id("dev.roc-ray.sqlite-scores")

Keeps its database in its own private data directory, which needs no declaration

Every other example declares nothing. Printing, drawing, audio, sounds and textures made in code, a dropped file, and captures written beneath the output directory all need no declaration.

The recording examples list PermissionDenied among the errors init! can return. It comes from Capture.Writer.start!, whose result type includes it; the io that init! receives always has permission to record, so in these examples it does not occur.