Cameras and Views

Pyglet provides camera scopes in pyglet.window.camera for 2D and 3D rendering. For common 2D workflows, use Camera2D. By default, the window is created with a 2D camera.

Default camera behavior

Every window has a default camera at pyglet.window.Window.camera. The projection, view, and viewport properties proxy directly to the camera’s root view.

The default camera is initialized with the window context creation and applies itself automatically.

Creating a camera

Create cameras from a window, and optionally configure scroll/zoom behavior:

window = pyglet.window.Window()

world_camera = pyglet.window.camera.Camera2D(
    window,
    scroll_speed=5,
    min_zoom=1,
    max_zoom=4,
)
gui_camera = pyglet.window.camera.Camera2D(window)

world_camera.zoom += 0.25
world_camera.move(1, 0)
world_camera.position = (50, 0)

Creating views

A camera can have multiple views. Views let you scope additional transforms and scissor areas inside one camera.

Use camera.create_view() for additional view scopes. This is useful for parallax layers, minimaps, and nested local transforms:

window = pyglet.window.Window()

ui_camera = pyglet.window.camera.Camera2D(window)
inherited_view = ui_camera.create_view(inherit=True)      # Inherits parent transform chain.
independent_view = ui_camera.create_view(inherit=False)   # Parented to camera root view.

Views share the camera’s projection behavior and can be used anywhere a camera scope is expected. Each view has its own viewport, inherited from its parent view unless explicitly overridden.

Views can also be created from other views. For example:

ui_camera = pyglet.window.camera.Camera2D(window)
scroll_box_a = ui_camera.create_view()
scroll_box_b = scroll_box_a.create_view()

If scroll_box_a moves or zooms, scroll_box_b inherits those changes.

Using camera scopes with batches

Cameras can be used to target a specific batch instead of groups:

@window.event
def on_draw():
    window.clear()

    with world_batch.draw_with_options() as options:
        options.camera = world_camera

    with gui_batch.draw_with_options() as options:
        options.camera = gui_camera

Using camera scopes in groups

Attach camera scopes to groups with Group.set_camera:

world_group = pyglet.graphics.Group(order=0)
world_group.set_camera(world_camera)

ui_group = pyglet.graphics.Group(order=1)
ui_group.set_camera(window.default_camera)

pyglet.sprite.Sprite(world_image, batch=batch, group=world_group)
pyglet.text.Label("HUD", batch=batch, group=ui_group)

Note

Group.set_camera applies camera state at draw entry by calling begin(). It does not call end() during group unsetting.

Viewport

Each camera view has a viewport in framebuffer coordinates:

world_camera.viewport = (0, 0, window.width // 2, window.height)

camera.viewport remains as a convenience proxy for camera.view.viewport. Set view.viewport directly when a child view needs a different viewport:

minimap_view = world_camera.create_view()
minimap_view.viewport = (16, 16, 256, 256)

Viewport belongs to the view because the viewport determines how that specific view maps drawn content into framebuffer pixels. Child views can represent split screens, minimaps, editor panels, or nested render regions while still sharing the same camera projection and storage behavior.

By default, the root view’s viewport follows the full framebuffer and updates on resize/scale events. A child view inherits its parent viewport until it is given an explicit value. Setting a tuple makes that view’s viewport explicit, and it will no longer update automatically. In those cases you will have to update your viewport coordinates explicitly.

Coordinate conversion

Cameras and views provide helpers for moving points through the same coordinate spaces used when drawing:

@window.event
def on_mouse_press(x, y, button, modifiers):
    world_position = world_camera.screen_to_world(x, y)
    minimap_position = minimap_view.screen_to_world(x, y)

Use the camera methods for the root view, or call the same methods on a child view when viewport inheritance or view-local transforms matter. The helpers include conversions between screen, viewport, view, and world spaces, such as screen_to_viewport, viewport_to_world, screen_to_world, and world_to_screen.

Scissor

Scissor clipping can be set on both cameras and views.

Use set_scissor_area(...) for fixed window-space clipping:

world_camera.set_scissor_area(0, 0, window.width // 2, window.height)

Use set_scissor_area_relative(...) when clipping should move with the camera/view transform:

moving_panel = ui_camera.create_view(inherit=True)
moving_panel.set_scissor_area_relative(40, 40, 280, 160)
moving_panel.offset_x += 16   # Scissor follows the moved view

When a camera/view is applied through Group.set_camera, pyglet automatically applies matching scissor state for that group.

For nested views, the effective scissor is the intersection of all scissor areas in the view chain.