Skip to content

Commit

Permalink
Migrate to Bevy 0.11 (#28)
Browse files Browse the repository at this point in the history
* Prepare for Bevy 0.11

* Implement automatic viewport
This also deprecates `PixelBorderPlugin`.

* Switch to official release of Bevy 0.11

* Improve README

* Add mini migration guide
  • Loading branch information
drakmaniso committed Jul 10, 2023
1 parent 51fdb73 commit 17b2f6c
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 104 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_pixel_camera"
version = "0.4.1"
version = "0.5.0"
authors = ["drakmaniso <moussault.laurent@gmail.com>"]
edition = "2021"
description = "A simple pixel-perfect camera plugin for Bevy, suitable for pixel-art"
Expand All @@ -12,10 +12,10 @@ exclude = ["assets/**", ".vscode/**"]
license = "MIT OR Apache-2.0"

[dependencies]
bevy = { version = "0.10", default-features = false, features = ["bevy_core_pipeline", "bevy_render", "bevy_sprite"] }
bevy = { version = "0.11", default-features = false, features = ["bevy_core_pipeline", "bevy_render", "bevy_sprite"] }

[dev-dependencies]
bevy = { version = "0.10", default-features = false, features = ["bevy_winit", "bevy_asset", "png", "x11"] }
bevy = { version = "0.11", default-features = false, features = ["bevy_winit", "bevy_asset", "png", "x11"] }

[[example]]
name = "flappin"
Expand Down
54 changes: 33 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ the default_sampler on the ImagePlugin:
...
```

The crate also includes a separate plugin to put an opaque border around the
desired resolution. This way, if the window size is not an exact multiple of
the virtual resolution, anything out of bounds will still be hidden.
You can also ask the plugin to automatically set and resize the viewport of
the camera. This way, if the window size is not an exact multiple of the
virtual resolution, anything out of bounds will be hidden.

A small example is included in the crate. Run it with:

Expand All @@ -39,34 +39,39 @@ cargo run --example flappin

## Comparison with other methods

There is several possible methods to render pixel-art based games. This
crate simply upscale each sprite, and correctly align them on a virtual
pixel grid. Another option would be to render the sprites to an offscreen
texture, and then upscale only this texture. There is advantages and
drawbacks to both approaches:
There is two main methods to render pixel-art games: upscale each sprite
independently, or render everything to an offscreen texture and only upscale
this texture. This crate use the first method. There is advantages and
drawbacks to both approaches.

- the offscreen texture method is probably more efficient in most cases;
- the method in this crate allows for smoother scrolling and movement of
sprites, if you're willing to temporarily break the alignment on virtual
pixels (this would be even more effective with a dedicated upscaling
shader).
Advantages of the "upscale each sprite independently" method:

- allows for smoother scrolling and movement of sprites, if you're willing
to temporarily break the alignment on virtual pixels (this would be even
more effective with a dedicated upscaling shader);
- easier to mix pixel-art and high resolution graphics (for example for
text, particles or effects).

Advantages of the "offscreen texture" method:

- always ensure perfect alignment on virtual pixels (authentic "retro"
look);
- probably more efficient (in most cases, the difference is probably
negligible on modern computers).

## Example code

```rust
use bevy::prelude::*;
use bevy::sprite::Anchor;
use bevy_pixel_camera::{
PixelBorderPlugin, PixelCameraBundle, PixelCameraPlugin
PixelCameraBundle, PixelCameraPlugin
};

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_plugin(PixelCameraPlugin)
.add_plugin(PixelBorderPlugin {
color: Color::rgb(0.1, 0.1, 0.1),
})
.add_startup_system(setup)
.run();
}
Expand All @@ -75,7 +80,7 @@ fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
commands.spawn(PixelCameraBundle::from_resolution(320, 240));
commands.spawn(PixelCameraBundle::from_resolution(320, 240, true));

commands.spawn(SpriteBundle {
texture: asset_server.load("my-pixel-art-sprite.png"),
Expand All @@ -88,15 +93,22 @@ fn setup(
}
```

### Bevy versions supported
## Bevy versions supported

| bevy | bevy_pixel_camera |
|------|-------------------|
| 0.10 | 0.4 |
| 0.11 | 0.5 |
| 0.10 | 0.4.1 |
| 0.9 | 0.3 |
| 0.8 | 0.2 |

### License
### Migration guide: 0.4 to 0.5 (Bevy 0.10 to 0.11)

The `PixelBorderPlugin` has been deprecated. If you want a border around
your virtual resolution, pass `true` to the `set_viewport` argument when
creating the camera bundle (see example above).

## License

Licensed under either of

Expand Down
41 changes: 17 additions & 24 deletions examples/flappin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use bevy::prelude::*;
use bevy::sprite::Anchor;
use bevy::{prelude::*, window::WindowResolution};
use bevy_pixel_camera::{PixelBorderPlugin, PixelCameraBundle, PixelCameraPlugin};
use bevy::window::WindowResolution;
use bevy_pixel_camera::{PixelCameraBundle, PixelCameraPlugin};

// GAME CONSTANTS /////////////////////////////////////////////////////////////

Expand Down Expand Up @@ -54,43 +55,42 @@ fn main() {
..default()
}),
)
.add_plugin(PixelCameraPlugin)
.add_plugin(PixelBorderPlugin {
color: Color::rgb(0.1, 0.1, 0.1),
})
.add_plugins(PixelCameraPlugin)
.insert_resource(Rng { mz: 0, mw: 0 })
.insert_resource(ClearColor(Color::rgb(0.000001, 0.000001, 0.000001)))
.insert_resource(FlapTimer(Timer::from_seconds(0.5, TimerMode::Once)))
.insert_resource(Action {
just_pressed: false,
})
.add_startup_system(setup)
.add_startup_systems((spawn_bird, spawn_clouds).after(setup))
.add_system(bevy::window::close_on_esc)
.add_system(on_press)
.add_systems(Startup, setup)
.add_systems(Startup, (spawn_bird, spawn_clouds).after(setup))
.add_systems(Update, bevy::window::close_on_esc)
.add_systems(Update, on_press)
.add_systems(
Update,
(
press_to_start,
animate_flying_bird,
animate_pillars,
animate_clouds,
)
.in_set(OnUpdate(GameState::StartScreen)),
.run_if(in_state(GameState::StartScreen)),
)
.add_system(spawn_pillars.in_schedule(OnEnter(GameState::Playing)))
.add_systems(OnEnter(GameState::Playing), spawn_pillars)
.add_systems(
Update,
(
flap,
animate_flappin_bird,
collision_detection,
animate_pillars,
animate_clouds,
)
.in_set(OnUpdate(GameState::Playing)),
.run_if(in_state(GameState::Playing)),
)
.add_system(game_over.in_schedule(OnEnter(GameState::GameOver)))
.add_system(press_to_start.in_set(OnUpdate(GameState::GameOver)))
.add_system(despawn_pillars.in_schedule(OnExit(GameState::GameOver)))
.add_systems(OnEnter(GameState::GameOver), game_over)
.add_systems(Update, press_to_start.run_if(in_state(GameState::GameOver)))
.add_systems(OnExit(GameState::GameOver), despawn_pillars)
.run();
}

Expand All @@ -103,6 +103,7 @@ fn setup(mut commands: Commands, time: Res<Time>, mut rng: ResMut<Rng>) {
commands.spawn(PixelCameraBundle::from_resolution(
WIDTH as i32,
HEIGHT as i32,
true,
));
}

Expand Down Expand Up @@ -330,12 +331,8 @@ fn spawn_pillars(
fn animate_pillars(
time: Res<Time>,
mut rng: ResMut<Rng>,
state: Res<State<GameState>>,
mut query: Query<&mut Transform, With<Pillar>>,
) {
if state.0 == GameState::GameOver {
return;
}
let dt = time.delta().as_secs_f32();
for mut transform in query.iter_mut() {
*transform = transform.mul_transform(Transform::from_xyz(-60.0 * dt, 0.0, 0.0));
Expand Down Expand Up @@ -396,12 +393,8 @@ fn spawn_clouds(
fn animate_clouds(
time: Res<Time>,
mut rng: ResMut<Rng>,
state: Res<State<GameState>>,
mut query: Query<(&mut Transform, &mut TextureAtlasSprite), With<Cloud>>,
) {
if state.0 == GameState::GameOver {
return;
}
let dt = time.delta().as_secs_f32();
for (mut transform, mut sprite) in query.iter_mut() {
*transform = transform.mul_transform(Transform::from_xyz(-30.0 * dt, 0.0, 0.0));
Expand Down
8 changes: 3 additions & 5 deletions examples/mire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ fn main() {
App::new()
.insert_resource(ClearColor(Color::rgb(0.000001, 0.000001, 0.000001)))
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.add_plugin(PixelCameraPlugin)
.add_startup_system(setup)
.add_plugins(PixelCameraPlugin)
.add_systems(Startup, setup)
.run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Add a camera that will always fit the virtual resolution 320x180 inside
// the window.
commands.spawn(PixelCameraBundle::from_resolution(320, 180));
// commands.spawn(OrthographicCameraBundle::new_2d());
commands.spawn(PixelCameraBundle::from_resolution(320, 180, false));

let mire_32x32_handle = asset_server.load("mire-32x32.png");
// let mire_31x31_handle = asset_server.load("mire-31x31.png");

// Add a 31x31 sprite in the center of the window.
commands.spawn(SpriteBundle {
Expand Down
57 changes: 35 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
//! not what you want for pixel art. The easiest way to change this is to set
//! the default_sampler on the ImagePlugin:
//!
//! ```rust
//! ```ignore
//! App::new()
//! .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
//! ...
//! ```
//!
//! The crate also includes a separate plugin to put an opaque border around the
//! desired resolution. This way, if the window size is not an exact multiple of
//! the virtual resolution, anything out of bounds will still be hidden.
//! You can also ask the plugin to automatically set and resize the viewport of
//! the camera. This way, if the window size is not an exact multiple of the
//! virtual resolution, anything out of bounds will be hidden.
//!
//! A small example is included in the crate. Run it with:
//!
Expand All @@ -37,34 +37,39 @@
//!
//! # Comparison with other methods
//!
//! There is several possible methods to render pixel-art based games. This
//! crate simply upscale each sprite, and correctly align them on a virtual
//! pixel grid. Another option would be to render the sprites to an offscreen
//! texture, and then upscale only this texture. There is advantages and
//! drawbacks to both approaches:
//! There is two main methods to render pixel-art games: upscale each sprite
//! independently, or render everything to an offscreen texture and only upscale
//! this texture. This crate use the first method. There is advantages and
//! drawbacks to both approaches.
//!
//! - the offscreen texture method is probably more efficient in most cases;
//! - the method in this crate allows for smoother scrolling and movement of
//! sprites, if you're willing to temporarily break the alignment on virtual
//! pixels (this would be even more effective with a dedicated upscaling
//! shader).
//! Advantages of the "upscale each sprite independently" method:
//!
//! - allows for smoother scrolling and movement of sprites, if you're willing
//! to temporarily break the alignment on virtual pixels (this would be even
//! more effective with a dedicated upscaling shader);
//! - easier to mix pixel-art and high resolution graphics (for example for
//! text, particles or effects).
//!
//! Advantages of the "offscreen texture" method:
//!
//! - always ensure perfect alignment on virtual pixels (authentic "retro"
//! look);
//! - probably more efficient (in most cases, the difference is probably
//! negligible on modern computers).
//!
//! # Example code
//!
//! ```no_run
//! use bevy::prelude::*;
//! use bevy::sprite::Anchor;
//! use bevy_pixel_camera::{
//! PixelBorderPlugin, PixelCameraBundle, PixelCameraPlugin
//! PixelCameraBundle, PixelCameraPlugin
//! };
//!
//! fn main() {
//! App::new()
//! .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
//! .add_plugin(PixelCameraPlugin)
//! .add_plugin(PixelBorderPlugin {
//! color: Color::rgb(0.1, 0.1, 0.1),
//! })
//! .add_startup_system(setup)
//! .run();
//! }
Expand All @@ -73,7 +78,7 @@
//! mut commands: Commands,
//! asset_server: Res<AssetServer>,
//! ) {
//! commands.spawn(PixelCameraBundle::from_resolution(320, 240));
//! commands.spawn(PixelCameraBundle::from_resolution(320, 240, true));
//!
//! commands.spawn(SpriteBundle {
//! texture: asset_server.load("my-pixel-art-sprite.png"),
Expand All @@ -86,15 +91,22 @@
//! }
//! ```
//!
//! ## Bevy versions supported
//! # Bevy versions supported
//!
//! | bevy | bevy_pixel_camera |
//! |------|-------------------|
//! | 0.10 | 0.4 |
//! | 0.11 | 0.5 |
//! | 0.10 | 0.4.1 |
//! | 0.9 | 0.3 |
//! | 0.8 | 0.2 |
//!
//! ## License
//! ## Migration guide: 0.4 to 0.5 (Bevy 0.10 to 0.11)
//!
//! The `PixelBorderPlugin` has been deprecated. If you want a border around
//! your virtual resolution, pass `true` to the `set_viewport` argument when
//! creating the camera bundle (see example above).
//!
//! # License
//!
//! Licensed under either of
//!
Expand All @@ -111,6 +123,7 @@ mod pixel_plugin;
#[cfg(test)]
mod tests;

#[allow(deprecated)]
pub use pixel_border::*;
pub use pixel_camera::*;
pub use pixel_plugin::*;
14 changes: 11 additions & 3 deletions src/pixel_border.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![deprecated(since = "0.5.0", note = "please use the `set_viewport` flag instead")]
#![allow(deprecated)]

use bevy::prelude::*;
use bevy::sprite::Anchor;

Expand All @@ -11,8 +14,13 @@ pub struct PixelBorderPlugin {
impl Plugin for PixelBorderPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(BorderColor(self.color))
.add_startup_system(spawn_borders)
.add_system(resize_borders.in_base_set(CoreSet::PostUpdate));
.add_systems(Startup, spawn_borders)
.add_systems(
PostUpdate,
resize_borders
.after(bevy::render::camera::camera_system::<PixelProjection>)
.before(bevy::render::view::visibility::update_frusta::<PixelProjection>),
);
}
}

Expand Down Expand Up @@ -67,7 +75,7 @@ fn resize_borders(
>,
mut borders: Query<(&mut Sprite, &mut Transform, &Border), Without<PixelProjection>>,
) {
if let Some((projection, transform)) = cameras.iter().next() {
for (projection, transform) in cameras.iter() {
let z = projection.far - 0.2;
let width = projection.desired_width.map(|w| w as f32).unwrap_or(0.0);
let height = projection.desired_height.map(|h| h as f32).unwrap_or(0.0);
Expand Down
Loading

0 comments on commit 17b2f6c

Please sign in to comment.