Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions examples/sprite_flip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// In this program, a sprite can be flipped with X and Y keys.

use bevy::prelude::*;
use seldom_pixel::prelude::*;

fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: Vec2::splat(512.).into(),
..default()
}),
..default()
}),
PxPlugin::<Layer>::new(UVec2::splat(16), "palette/palette_1.palette.png"),
))
.insert_resource(ClearColor(Color::BLACK))
.add_systems(Startup, init)
.add_systems(Update, on_key)
.run();
}

fn init(assets: Res<AssetServer>, mut commands: Commands) {
commands.spawn(Camera2d);

// Spawn a sprite
commands.spawn((
PxSprite(assets.load("sprite/mage.px_sprite.png")),
PxPosition(IVec2::splat(8)),
PxFlip::default(),
));
}

fn on_key(input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut PxFlip>) {
if input.just_pressed(KeyCode::KeyX) {
for mut flip in &mut query {
flip.x = !flip.x;
}
}

if input.just_pressed(KeyCode::KeyY) {
for mut flip in &mut query {
flip.y = !flip.y;
}
}
}

#[px_layer]
struct Layer;
63 changes: 63 additions & 0 deletions examples/tilemap_flip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// In this program, a tilemap can be flipped with x and y keys.

use bevy::prelude::*;
use rand::{thread_rng, Rng};
use seldom_pixel::prelude::*;

fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: Vec2::splat(512.).into(),
..default()
}),
..default()
}),
PxPlugin::<Layer>::new(UVec2::splat(16), "palette/palette_1.palette.png"),
))
.insert_resource(ClearColor(Color::BLACK))
.add_systems(Startup, init)
.add_systems(Update, on_key)
.run();
}

fn init(assets: Res<AssetServer>, mut commands: Commands) {
commands.spawn(Camera2d);

let mut tiles = PxTiles::new(UVec2::splat(4));
let mut rng = thread_rng();

for x in 0..4 {
for y in 0..4 {
tiles.set(
Some(commands.spawn((PxTile::from(rng.gen_range(0..4)),
PxFlip::default())).id()),
UVec2::new(x, y),
);
}
}

// Spawn the map
commands.spawn(PxMap {
tiles,
tileset: assets.load("tileset/tileset.px_tileset.png"),
});
}

fn on_key(input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut PxFlip>) {
if input.just_pressed(KeyCode::KeyX) {
for mut flip in &mut query {
flip.x = !flip.x;
}
}

if input.just_pressed(KeyCode::KeyY) {
for mut flip in &mut query {
flip.y = !flip.y;
}
}
}

#[px_layer]
struct Layer;
10 changes: 8 additions & 2 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,13 +301,13 @@ fn extract_maps<L: PxLayer>(
}
}

pub(crate) type TileComponents = (&'static PxTile, Option<&'static PxFilter>);
pub(crate) type TileComponents = (&'static PxTile, Option<&'static PxFilter>, Option<&'static PxFlip>);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this non-optional and add PxFlip as a required component on PxTile

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do.


fn extract_tiles(
tiles: Extract<Query<(TileComponents, &InheritedVisibility, RenderEntity)>>,
mut cmd: Commands,
) {
for ((tile, filter), visibility, entity) in &tiles {
for ((tile, filter, flip), visibility, entity) in &tiles {
if !visibility.get() {
continue;
}
Expand All @@ -320,5 +320,11 @@ fn extract_tiles(
} else {
entity.remove::<PxFilter>();
}

if let Some(flip) = flip {
entity.insert(flip.clone());
} else {
entity.remove::<PxFlip>();
}
}
}
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use crate::{
math::{Diagonal, Orthogonal},
position::{PxAnchor, PxLayer, PxPosition, PxSubPosition, PxVelocity},
screen::ScreenSize,
sprite::{PxSprite, PxSpriteAsset},
sprite::{PxSprite, PxSpriteAsset, PxFlip},
text::{PxText, PxTypeface},
ui::PxRect,
PxPlugin,
Expand Down
16 changes: 8 additions & 8 deletions src/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,17 +394,17 @@ impl<L: PxLayer> ViewNode for PxRenderNode<L> {
// }
// }

for (sprite, position, anchor, layer, canvas, animation, filter) in
for (sprite, position, anchor, layer, canvas, animation, filter, flip) in
self.sprites.iter_manual(world)
{
if let Some((_, sprites, _, _, _, _, _)) = layer_contents.get_mut(layer) {
sprites.push((sprite, position, anchor, canvas, animation, filter));
sprites.push((sprite, position, anchor, canvas, animation, filter, flip));
} else {
layer_contents.insert(
layer.clone(),
(
default(),
vec![(sprite, position, anchor, canvas, animation, filter)],
vec![(sprite, position, anchor, canvas, animation, filter, flip)],
default(),
default(),
default(),
Expand Down Expand Up @@ -578,7 +578,7 @@ impl<L: PxLayer> ViewNode for PxRenderNode<L> {
continue;
};

let Ok((&PxTile { texture }, tile_filter)) =
let Ok((&PxTile { texture }, tile_filter, tile_flip)) =
self.tiles.get_manual(world, tile)
else {
continue;
Expand All @@ -591,7 +591,7 @@ impl<L: PxLayer> ViewNode for PxRenderNode<L> {

draw_spatial(
tile,
(),
tile_flip.cloned().unwrap_or_default(),
&mut layer_image,
(**position + pos.as_ivec2() * tileset.tile_size().as_ivec2()).into(),
PxAnchor::BottomLeft,
Expand Down Expand Up @@ -748,14 +748,14 @@ impl<L: PxLayer> ViewNode for PxRenderNode<L> {
// );
// }

for (sprite, position, anchor, canvas, animation, filter) in sprites {
for (sprite, position, anchor, canvas, animation, filter, flip) in sprites {
let Some(sprite) = sprite_assets.get(&**sprite) else {
continue;
};

draw_spatial(
sprite,
(),
flip.cloned().unwrap_or_default(),
&mut layer_image,
*position,
*anchor,
Expand Down Expand Up @@ -896,7 +896,7 @@ impl<L: PxLayer> ViewNode for PxRenderNode<L> {

draw_spatial(
character,
(),
PxFlip::default(),
&mut text_image,
IVec2::new(character_x as i32, line_y as i32).into(),
PxAnchor::BottomLeft,
Expand Down
33 changes: 28 additions & 5 deletions src/sprite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,31 +108,38 @@ impl RenderAsset for PxSpriteAsset {
}

impl Animation for PxSpriteAsset {
type Param = ();
type Param = PxFlip;

fn frame_count(&self) -> usize {
self.data.area() / self.frame_size
}

fn draw(
&self,
_: (),
flip: PxFlip,
image: &mut PxImageSliceMut<impl Pixel>,
frame: impl Fn(UVec2) -> usize,
filter: impl Fn(u8) -> u8,
) {
let width = self.data.width();
let image_width = image.image_width();
image.for_each_mut(|slice_i, image_i, pixel| {
if let Some(Some(value)) = self.data.get_pixel(IVec2::new(
let mut v = IVec2::new(
(slice_i % width) as i32,
((frame(UVec2::new(
(image_i % image_width) as u32,
(image_i / image_width) as u32,
)) * self.frame_size
+ slice_i)
/ width) as i32,
)) {
);
if flip.x {
v.x = width as i32 - v.x - 1;
}
if flip.y {
v.y = self.data.height() as i32 - v.y - 1;
}
if let Some(Some(value)) = self.data.get_pixel(v) {
pixel.set_value(filter(value));
}
});
Expand All @@ -148,6 +155,15 @@ impl Spatial for PxSpriteAsset {
}
}

/// Flips the sprite or tile on the x-axis, y-axis, or both.
#[derive(Component, Default, Clone, Copy, Debug)]
pub struct PxFlip {
/// If true, flips the x-axis.
pub x: bool,
/// If true, flips the y-axis.
pub y: bool,
}

/// A sprite
#[derive(Component, Deref, DerefMut, Default, Clone, Debug)]
#[require(PxPosition, PxAnchor, DefaultLayer, PxCanvas, Visibility)]
Expand Down Expand Up @@ -400,14 +416,15 @@ pub(crate) type SpriteComponents<L> = (
&'static PxCanvas,
Option<&'static PxAnimation>,
Option<&'static PxFilter>,
Option<&'static PxFlip>,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as earlier comment, but for PxSprite

);

fn extract_sprites<L: PxLayer>(
// TODO Maybe calculate `ViewVisibility`
sprites: Extract<Query<(SpriteComponents<L>, &InheritedVisibility, RenderEntity)>>,
mut cmd: Commands,
) {
for ((sprite, &position, &anchor, layer, &canvas, animation, filter), visibility, id) in
for ((sprite, &position, &anchor, layer, &canvas, animation, filter, flip), visibility, id) in
&sprites
{
if !visibility.get() {
Expand All @@ -428,6 +445,12 @@ fn extract_sprites<L: PxLayer>(
} else {
entity.remove::<PxFilter>();
}

if let Some(flip) = flip {
entity.insert(flip.clone());
} else {
entity.remove::<PxFlip>();
}
}
}

Expand Down
Loading