⚠️ Beta State

PyBevy is in an early and experimental stage. The API is incomplete, subject to breaking changes without notice, and you should expect bugs. Many features are still under development.

Camera Plugins

Add fly and orbit cameras to your 3D scenes with zero setup.

Introduction

Navigating 3D scenes is tedious to implement from scratch. pybevy ships two camera plugins that handle all the input logic for you:

  • FlyCameraPlugin: Free-flying first-person camera (WASD + mouse look).
  • OrbitCameraPlugin: Orbit around a target point (click-drag + scroll zoom).

Just add the plugin and attach the component to your camera entity.

from pybevy.prelude import *
from pybevy.plugins import FlyCameraPlugin, FlyCamera, OrbitCameraPlugin, OrbitCamera

Fly Camera

The fly camera moves freely through the scene. Hold right mouse button to look around, and use WASD (or arrow keys) to move. Q/E moves down/up, and Shift sprints.

You can customize speed and sensitivity via the FlyCamera component fields.

def setup_fly_camera(commands: Commands, meshes: ResMut[Assets[Mesh]], materials: ResMut[Assets[StandardMaterial]]):
    # Spawn the camera with the FlyCamera component
    commands.spawn(
        Camera3d(),
        Transform.from_xyz(0.0, 5.0, 15.0).looking_at(Vec3.ZERO, Vec3.Y),
        FlyCamera(move_speed=15.0, sprint_multiplier=3.0),
    )
 
    # A simple scene to fly around
    commands.spawn(
        Mesh3d(meshes.add(Plane3d(Vec3.Y, Vec2(50.0, 50.0)))),
        MeshMaterial3d(materials.add(Color.srgb(0.3, 0.5, 0.3))),
    )
    for i in range(5):
        commands.spawn(
            Mesh3d(meshes.add(Cuboid.from_length(2.0))),
            MeshMaterial3d(materials.add(Color.srgb(0.2 + i * 0.15, 0.3, 0.8))),
            Transform.from_xyz(i * 4.0 - 8.0, 1.0, 0.0),
        )
    commands.spawn(
        DirectionalLight(illuminance=5000.0),
        Transform.IDENTITY.looking_at(Vec3(-1.0, -1.0, -1.0), Vec3.Y),
    )

Orbit Camera

The orbit camera rotates around a target point. Left-click drag rotates, Shift + left-click drag pans, and scroll wheel zooms.

def setup_orbit_camera(commands: Commands, meshes: ResMut[Assets[Mesh]], materials: ResMut[Assets[StandardMaterial]]):
    # Spawn camera with OrbitCamera component
    commands.spawn(
        Camera3d(),
        Transform.from_xyz(0.0, 10.0, 20.0).looking_at(Vec3.ZERO, Vec3.Y),
        OrbitCamera(
            distance=25.0,
            yaw=0.0,
            pitch=0.4,
            target=Vec3.ZERO,
            zoom_sensitivity=0.15,
        ),
    )
 
    # Scene to orbit around
    commands.spawn(
        Mesh3d(meshes.add(Plane3d(Vec3.Y, Vec2(50.0, 50.0)))),
        MeshMaterial3d(materials.add(Color.srgb(0.3, 0.5, 0.3))),
    )
    commands.spawn(
        Mesh3d(meshes.add(Cuboid.from_length(3.0))),
        MeshMaterial3d(materials.add(Color.srgb(0.8, 0.3, 0.2))),
        Transform.from_xyz(0.0, 1.5, 0.0),
    )
    commands.spawn(
        DirectionalLight(illuminance=5000.0),
        Transform.IDENTITY.looking_at(Vec3(-1.0, -1.0, -1.0), Vec3.Y),
    )

Running the App

Choose one of the two setups below. Both plugins can coexist, but you'll typically want one camera controller per camera entity.

This example uses the orbit camera. Switch setup_orbit_camera to setup_fly_camera and OrbitCameraPlugin to FlyCameraPlugin to try the other.

@entrypoint
def main(app: App) -> App:
    return (
        app
        .add_plugins(DefaultPlugins)
        .add_plugins(OrbitCameraPlugin())
        .add_systems(Startup, setup_orbit_camera)
    )
 
if __name__ == "__main__":
    main().run()