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.
Custom Plugins
Organize your code into reusable plugins.
Introduction
As your project grows, putting everything into one file becomes messy. Plugins let you group related systems, resources, and components into reusable modules.
A plugin is a class that implements build(self, app) and registers its
systems and resources on the app.
from pybevy.prelude import *
from pybevy.decorators import pluginDefining a Plugin
Use the @plugin decorator AND inherit from Plugin. In the build method,
register everything this plugin needs.
@resource
class Score(Resource):
def __init__(self):
self.value = 0
@component
class ScoreDisplay(Component):
pass
def setup_score_ui(commands: Commands):
commands.spawn(
Text("Score: 0"),
ScoreDisplay(),
)
def update_score_display(
score: Res[Score],
query: Query[Mut[Text], With[ScoreDisplay]],
):
for text in query:
text.text = f"Score: {score.value}"
@plugin
class ScorePlugin(Plugin):Manages score tracking and display."""
def build(self, app: App):
app.insert_resource(Score())
app.add_systems(Startup, setup_score_ui)
app.add_systems(Update, update_score_display)## Another Plugin: Enemy Spawning
Each plugin is self-contained with its own components, resources, and systems.@component class Enemy(Component): pass
@resource class EnemySpawnTimer(Resource): def init(self): from pybevy.time import Timer, TimerMode self.timer = Timer.from_seconds(3.0, TimerMode.REPEATING)
def spawn_enemies( commands: Commands, time: Res[Time], timer: ResMut[EnemySpawnTimer], meshes: ResMut[Assets[Mesh]], materials: ResMut[Assets[StandardMaterial]], ): timer.timer.tick(time.delta()) if timer.timer.just_finished(): import random x = random.uniform(-5.0, 5.0) commands.spawn( Enemy(), Mesh3d(meshes.add(Sphere(0.5))), MeshMaterial3d(materials.add(Color.srgb(0.8, 0.2, 0.2))), Transform.from_xyz(x, 0.5, -10.0), )
def move_enemies( commands: Commands, time: Res[Time], score: ResMut[Score], query: Query[tuple[Entity, Mut[Transform]], With[Enemy]], ): for entity, transform in query: transform.translation.z += 3.0 * time.delta_secs() # Despawn when past camera, add score if transform.translation.z > 5.0: commands.despawn(entity) score.value += 10
@plugin class EnemyPlugin(Plugin):
Manages enemy spawning and movement."""
def build(self, app: App):
app.insert_resource(EnemySpawnTimer())
app.add_systems(Update, (spawn_enemies, move_enemies))A Scene Setup Plugin
Plugins can also handle scene setup: cameras, lights, and environment.
def setup_scene(commands: Commands):
commands.spawn(Camera3d(), Transform.from_xyz(0.0, 5.0, 10.0).looking_at(Vec3.ZERO, Vec3.Y))
commands.spawn(DirectionalLight(illuminance=5000.0), Transform.IDENTITY.looking_at(Vec3(-1.0, -1.0, -1.0), Vec3.Y))
@plugin
class ScenePlugin(Plugin):
def build(self, app: App):
app.add_systems(Startup, setup_scene)Composing Plugins
Now the main app is clean: just add your plugins.
@entrypoint
def main(app: App) -> App:
return (
app
.add_plugins(DefaultPlugins)
.add_plugins(ScenePlugin())
.add_plugins(ScorePlugin())
.add_plugins(EnemyPlugin())
)
if __name__ == "__main__":
main().run()Each plugin can live in its own Python file and be imported. This keeps your codebase organized and makes it easy to reuse game systems across projects.