Skip to content

forge3d.App

High-level game-loop abstraction using decorators.

App

High-level forge3d application with a managed physics + render loop.

Parameters

title : Window title. width : Render width in pixels. height : Render height in pixels. fps : Physics step rate and target render rate. gravity : World gravity vector (x, y, z) in m/s².

Examples

app = f3d.App("My World", fps=60) @app.on_start ... def setup(world): ... world.add_ground() ... world.add_box(position=(0, 0, 5)) @app.on_update ... def update(world, dt, inp): ... if inp.key_held(f3d.Key.W): ... world.apply_impulse(world.bodies[0], (0, 5*dt, 0)) app.run(max_frames=120) # doctest: +SKIP

world property

world: Any

The managed :class:~forge3d.facade.World instance.

fps property writable

fps: float

Target frames per second.

on_start

on_start(func: Callable) -> Callable

Register a callback called once before the game loop begins.

Signature: fn() or fn(world)

on_update

on_update(func: Callable) -> Callable

Register a callback called every frame, before :meth:World.step.

Signature: one of: - fn() - fn(world) - fn(world, dt) - fn(world, dt, inp)

on_render

on_render(func: Callable) -> Callable

Register a callback called after :meth:Viewer.draw each frame.

Signature: fn() or fn(world) or fn(world, viewer)

run

run(max_frames: int | None = None) -> None

Open a window and start the game loop.

Fires on_start once, then each frame:

  1. Read input from the OS window (keyboard / mouse).
  2. Call on_update(world, dt, inp).
  3. world.step(dt)
  4. viewer.draw()
  5. Call on_render(world, viewer).

The loop ends when the window is closed, ESC is pressed, or max_frames frames have been rendered.

Parameters

max_frames : Stop after this many frames. None (default) runs until the user closes the window.


Example

import forge3d as f3d

app = f3d.App("My World", width=1280, height=720, fps=60)
ball = None

@app.on_start
def setup(world: f3d.World) -> None:
    global ball
    world.add_ground()
    ball = world.add_sphere(radius=0.4, position=(0, 0, 6))

@app.on_update
def update(world: f3d.World, dt: float, inp: f3d.Input) -> None:
    if inp.key_pressed(f3d.Key.SPACE):
        world.apply_impulse(ball, (0, 0, 8))

@app.on_render
def render(world: f3d.World, viewer: f3d.Viewer) -> None:
    pass  # custom overlays if needed

app.run()