Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/janhohenheim/spew
A simple helper for spawning objects in Bevy.
https://github.com/janhohenheim/spew
bevy game-development spawning
Last synced: 2 months ago
JSON representation
A simple helper for spawning objects in Bevy.
- Host: GitHub
- URL: https://github.com/janhohenheim/spew
- Owner: janhohenheim
- License: apache-2.0
- Created: 2023-03-19T02:53:39.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-03-27T00:00:50.000Z (9 months ago)
- Last Synced: 2024-04-27T06:54:33.270Z (8 months ago)
- Topics: bevy, game-development, spawning
- Language: Rust
- Homepage:
- Size: 61.5 KB
- Stars: 29
- Watchers: 3
- Forks: 10
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
- License: license-apache.txt
Awesome Lists containing this project
README
> [!IMPORTANT]
> Starting from Bevy 0.14, we now have [observers!](https://docs.rs/bevy/latest/bevy/ecs/prelude/struct.Observer.html).
> Because they can do everything spew set out to do, Bevy 0.14 will be the last version supported by the plugin.
> From now on, spew will no longer receive updates.
>
> For an example of how I recommend to do spawning logic now, see [this PR](https://github.com/TheBevyFlock/bevy-template/pull/70)
> on the new semi-official Bevy jam template.# Spew
[![crates.io](https://img.shields.io/crates/v/spew)](https://crates.io/crates/spew)
[![docs.rs](https://docs.rs/spew/badge.svg)](https://docs.rs/spew)A simple helper for spawning objects in Bevy.
## Usage
First, create an `enum` that holds objects you might want to spawn:
```rust
#[derive(Debug, Eq, PartialEq)]
enum Objects {
Player,
Monster,
Coin,
}
```Think about which data you want to pass to the spawning function. In this example, we will specify a `Transform` for the new object.
Next, add the plugin to your app, noting the two types we just mentioned:```rust,ignore
use spew::prelude::*;
use bevy::prelude::*;fn main() {
App::new()
// ...
.add_plugins(SpewPlugin::::default()) // <--- Add the plugin
// ...
.run();
}
```Now, we are ready to register our spawn functions. Each variant of the `enum` will be associated with its own spawn function that takes in a `&mut World` and the user provided data:
```rust,ignore
use spew::prelude::*;
use bevy::prelude::*;fn main() {
App::new()
// ...
.add_spawners( // <--- Register the spawn functions
(Objects::Player, spawn_player),
(Objects::Monster, spawn_monster),
(Objects::Coin, spawn_coin),
)
// ...
.run();
}fn spawn_player(In(transform): In, mut commands: Commands) {
commands.spawn((
Name::new("Spiffy the Adventurer"),
TransformBundle::from_transform(transform),
));
}fn spawn_monster(In(transform): In, mut commands: Commands) {
commands.spawn((
Name::new("Grumblor the Grumpy"),
TransformBundle::from_transform(transform),
));
}fn spawn_coin(In(transform): In, mut commands: Commands) {
commands.spawn((
Name::new("$1000"),
TransformBundle::from_transform(transform),
));
}
```Finally, we can set our spawn functions to work by sending a `SpawnEvent`:
```rust,ignore
use spew::prelude::*;
use bevy::prelude::*;fn main() {
App::new()
// ...
.add_systems(Startup, setup_map)
// ...
.run();
}fn setup_map(mut spawn_events: EventWriter>) {
spawn_events.send(SpawnEvent::with_data(
Objects::Player,
Transform::from_xyz(0.0, 0.0, 0.0),
));
spawn_events.send(SpawnEvent::with_data(
Objects::Monster,
Transform::from_xyz(5.0, 0.0, 0.0),
));
spawn_events.send(SpawnEvent::with_data(
Objects::Coin,
Transform::from_xyz(10.0, 0.0, 0.0),
));
}
```You can read through the [docs](https://docs.rs/spew) or peruse the [examples](https://github.com/janhohenheim/spew/tree/main/examples) for more use cases.
Other cool stuff you can do is delay the spawning by a certain amount of frames or time or organize your spawn lists into multiple enums.## Compatibility
| bevy | spew |
|-------------|----------|
| 0.14 | 0.6 |
| 0.13 | 0.5 |
| 0.12 | 0.4 |
| 0.11 | 0.3 |
| 0.10 | 0.2 |## Motivation
Bevy's `Commands` API allows you to spawn new entities with arbitrary components:
```rust
use bevy::prelude::*;fn spawn_player(commands: &mut Commands) {
commands.spawn((
Name::new("Adventurer"),
TransformBundle::from_transform(Transform::from_xyz(0.0, 0.0, 0.0)),
));
}
```This works great! We can spawn more complex objects by just adding more components like assets:
```rust
use std::f32::consts::TAU;
use bevy::prelude::*;fn spawn_bullet(commands: &mut Commands, asset_server: Res) {
commands.spawn((
Name::new("Bullet"),
SceneBundle {
scene: asset_server.load("models/bullet.gltf#Scene0"),
transform: Transform {
translation: Vec3::new(5.0, 4.0, 12.0),
scale: Vec3::splat(0.012),
rotation: Quat::from_rotation_y(TAU / 2.),
},
..default()
},
));
}
```but, in a real project, we would not spawn a bullet like that. The bullet would be spawned by a weapon at a certain translation.
We might thus encapsulate the bullet spawning like this:```rust,ignore
use bevy::prelude::*;
fn handle_input(...) {
// ...
if should_fire_bullet {
let position = player_transform.translation;
spawn_bullet(&mut commands, &asset_server, position);
}
}fn spawn_bullet(commands: &mut Commands, asset_server: &AssetServer, position: Vec3) {
commands.spawn((
Name::new("Bullet"),
SceneBundle {
scene: asset_server.load("models/bullet.gltf#Scene0"),
transform: Transform {
translation: position,
scale: Vec3::splat(0.012),
rotation: Quat::from_rotation_y(TAU / 2.),
},
..default()
},
));
}
```As you can see, this works but is quite ugly. `handle_input` has to pass around an asset server we might otherwise not even need in the system,
and `spawn_bullet` has a jumble of seemingly unrelated parameters that will grow and grow over time. Growing parameter lists are not a problem
when writing a system, but notice how here `spawn_bullet` is no longer a system but a helper function. Thus, its call will get longer and uglier over time,
with all its parameters leaking into `handle_input`.The solution to this is to move the spawning of the bullet into an own system that is accessed indirectly by `handle_input` via events, which is just what this crate helps you with! :)