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.
View API In Depth
Complete guide to PyBevy's high-performance batch processing View API.
Introduction
The View API is PyBevy's batch processing system for entity components. It uses a bytecode VM to execute expressions in parallel across all entities, providing NumPy-like performance with pure Python syntax.
Instead of iterating over entities one at a time with Query, the View API compiles your
Python expressions into bytecode and runs them across all matching entities simultaneously.
System Signature
To use the View API, declare a View parameter in your system function. The type
parameters specify which components to access.
from pybevy.prelude import *
@component
class Cube(Component):
pass
@component
class Velocity(Component):
x: float = 0.0
y: float = 0.0
z: float = 0.0Column Access
Use column_mut() for mutable access (read and write) or column() for read-only:
def basic_view_system(view: View[Mut[Transform], With[Cube]]) -> None:
# Mutable access — can read and write
transform = view.column_mut(Transform)
transform.translation.y = 10.0Access nested fields naturally:
transform.translation.x = 5.0
transform.translation.y = 10.0
transform.scale.x = 2.0
transform.rotation.w = 1.0Tier 1: Arithmetic Operations
The View API supports all standard arithmetic and compound assignments.
| Operator | Description | Example |
|----------|-------------|---------|
| + | Addition | x + 5.0 |
| - | Subtraction | x - 2.0 |
| * | Multiplication | x * 3.0 |
| / | Division | x / 4.0 |
| ** | Power | x ** 2.0 |
| += | Add-assign | x += 5.0 |
| -= | Sub-assign | x -= 2.0 |
| *= | Mul-assign | x *= 3.0 |
| /= | Div-assign | x /= 4.0 |
def position_update(
view: View[tuple[Mut[Transform], Velocity], With[Cube]],
time: Res[Time],
) -> None:
transform = view.column_mut(Transform)
velocity = view.column(Velocity)
dt = time.delta_secs()
# Compound assignments — clean and fast
transform.translation.x += velocity.x * dt
transform.translation.y += velocity.y * dt
transform.translation.z += velocity.z * dtTier 2: Math Functions
Trigonometric and numeric functions are called as methods on expressions.
Trigonometric Functions
| Function | Description |
|----------|-------------|
| .sin() | Sine |
| .cos() | Cosine |
| .tan() | Tangent |
| .asin() | Arcsine |
| .acos() | Arccosine |
| .atan() | Arctangent |
Numeric Functions
| Function | Description |
|----------|-------------|
| .sqrt() | Square root |
| .abs() | Absolute value |
| .floor() | Round down |
| .ceil() | Round up |
| .round() | Round to nearest |
| .min(val) | Minimum |
| .max(val) | Maximum |
| .clamp(min, max) | Clamp to range |
def circular_motion(view: View[Mut[Transform], With[Cube]], time: Res[Time]) -> None:
transform = view.column_mut(Transform)
t = time.elapsed_secs()
# Circular orbit using trig functions
transform.translation.x = (t * 2.0).cos() * 10.0
transform.translation.z = (t * 2.0).sin() * 10.0
# Keep above ground
transform.translation.y = transform.translation.y.max(0.0)Tier 3: Logical Operations
Comparison Operators
| Operator | Description |
|----------|-------------|
| == | Equal |
| != | Not equal |
| < | Less than |
| <= | Less or equal |
| > | Greater than |
| >= | Greater or equal |
Boolean Operators
| Operator | Description |
|----------|-------------|
| & | Logical AND |
| | | Logical OR |
| ~ | Logical NOT |
Important: Use &, |, ~ instead of Python's and, or, not keywords.
Conditional Expressions with where()
The where() function provides per-entity conditional logic:
from pybevy import where
# where(condition, true_value, false_value)
result = where(x > 0.0, 10.0, -10.0)Example: Quadrant-Based Height
from pybevy import where
def classify_quadrants(view: View[Mut[Transform]]) -> None:
transform = view.column_mut(Transform)
x = transform.translation.x
z = transform.translation.z
# Different heights per quadrant
height = where(
x > 0.0,
where(z > 0.0, 4.0, 1.0),
where(z > 0.0, 3.0, 2.0),
)
transform.translation.y = heightTier 4: Reduction Operations
Compute aggregate statistics across all entities in a single parallel operation.
| Function | Description | Empty Result |
|----------|-------------|--------------|
| view.reduce_sum(expr) | Sum of all values | 0.0 |
| view.reduce_mean(expr) | Average | 0.0 |
| view.reduce_max(expr) | Maximum | -inf |
| view.reduce_min(expr) | Minimum | +inf |
| view.reduce_count() | Count entities | 0 |
| view.reduce_count(condition) | Count matching | 0 |
Example: Height Statistics
def compute_stats(view: View[Transform]) -> None:
transform = view.column(Transform)
max_height = view.reduce_max(transform.translation.y)
avg_height = view.reduce_mean(transform.translation.y)
above_ground = view.reduce_count(transform.translation.y > 0.0)
print(f"Max: {max_height:.2f}, Avg: {avg_height:.2f}, Above ground: {above_ground}")Vec3 Bulk Operations
Instead of updating x, y, z separately, perform Vec3-level operations:
transform = view.column_mut(Transform)
velocity = view.column(Velocity)
# Vec3 arithmetic
new_pos = transform.translation + velocity * dt
transform.translation.set(new_pos)
# Scalar multiplication
scaled = transform.scale * 2.0
transform.scale.set(scaled)Supported: Vec3 + Vec3, Vec3 - Vec3, Vec3 * scalar, scalar * Vec3, Vec3 / scalar.
Complete Example: Wave Animation
Here's a complete runnable example that animates thousands of cubes using the View API.
def setup(
commands: Commands,
meshes: ResMut[Assets[Mesh]],
materials: ResMut[Assets[StandardMaterial]],
) -> None:
cube_mesh = meshes.add(Cuboid.from_length(0.15))
# Spawn a grid of cubes
grid_size = 50
half = grid_size // 2
for i in range(grid_size * grid_size):
x = (i % grid_size - half) * 0.25
z = (i // grid_size - half) * 0.25
commands.spawn(
Cube(),
Mesh3d(cube_mesh),
MeshMaterial3d(materials.add(Color.srgb(0.3, 0.7, 0.9))),
Transform.from_xyz(x, 0.0, z),
)
commands.spawn(
DirectionalLight(illuminance=5000.0),
Transform.IDENTITY.looking_at(Vec3(-1.0, -1.0, -1.0), Vec3.Y),
)
commands.spawn(
Camera3d(),
Transform.from_xyz(-5.0, 10.0, 15.0).looking_at(Vec3.ZERO, Vec3.Y),
)
def wave_animation(view: View[Mut[Transform], With[Cube]], time: Res[Time]) -> None:
t = time.elapsed_secs()
transform = view.column_mut(Transform)
# Two overlapping sine waves
x_wave = (transform.translation.x * 0.5 + t * 3.0).sin()
z_wave = (transform.translation.z * 0.4 - t * 2.5).sin()
transform.translation.y = (x_wave + z_wave) * 2.0Running the Example
@entrypoint
def main(app: App) -> App:
return (
app
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, wave_animation)
)
if __name__ == "__main__":
main().run()