Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PxFlip for sprite and tile. #8

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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>);

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>,
);

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