bevy_parallax/
layer.rs

1use std::time::Duration;
2
3use bevy::prelude::*;
4use serde::Deserialize;
5
6use crate::SpriteFrameUpdate;
7#[cfg(feature = "bevy-inspector-egui")]
8use bevy_inspector_egui::prelude::*;
9
10/// Layer speed type.
11/// Layers with horizontal or vertical speed are only able to travel in one direction,
12/// while bidirectional layers can be scrolled endlessly in both directions.
13#[derive(Debug, Deserialize, Clone)]
14#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
15pub enum LayerSpeed {
16    Horizontal(f32),
17    Vertical(f32),
18    Bidirectional(f32, f32),
19}
20
21#[derive(Debug, Deserialize, Clone)]
22#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
23pub enum RepeatStrategy {
24    Same,
25    MirrorHorizontally,
26    MirrorVertically,
27    MirrorBoth,
28}
29
30impl RepeatStrategy {
31    pub fn transform(&self, sprite_bundle: &mut SpriteBundle, pos: (i32, i32)) {
32        match self {
33            Self::Same => (),
34            Self::MirrorHorizontally => {
35                let (x, _) = pos;
36                sprite_bundle.sprite.flip_x ^= x % 2 != 0;
37            }
38            Self::MirrorVertically => {
39                let (_, y) = pos;
40                sprite_bundle.sprite.flip_y ^= y % 2 != 0;
41            }
42            Self::MirrorBoth => {
43                let (x, y) = pos;
44                sprite_bundle.sprite.flip_x ^= x % 2 != 0;
45                sprite_bundle.sprite.flip_y ^= y % 2 != 0;
46            }
47        }
48    }
49}
50
51#[derive(Debug, Deserialize, Clone)]
52#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
53#[cfg_attr(feature = "bevy-inspector-egui", reflect(InspectorOptions))]
54pub enum LayerRepeat {
55    Horizontal(RepeatStrategy),
56    Vertical(RepeatStrategy),
57    Bidirectional(RepeatStrategy),
58}
59
60impl LayerRepeat {
61    pub fn both(strategy: RepeatStrategy) -> Self {
62        Self::Bidirectional(strategy)
63    }
64
65    pub fn horizontally(strategy: RepeatStrategy) -> Self {
66        Self::Horizontal(strategy)
67    }
68
69    pub fn vertically(strategy: RepeatStrategy) -> Self {
70        Self::Vertical(strategy)
71    }
72
73    pub fn has_vertical(&self) -> bool {
74        match self {
75            Self::Horizontal(_) => false,
76            _ => true,
77        }
78    }
79
80    pub fn has_horizontal(&self) -> bool {
81        match self {
82            Self::Vertical(_) => false,
83            _ => true,
84        }
85    }
86
87    pub fn get_strategy(&self) -> RepeatStrategy {
88        match self {
89            Self::Horizontal(strategy) => strategy.clone(),
90            Self::Bidirectional(strategy) => strategy.clone(),
91            Self::Vertical(strategy) => strategy.clone(),
92        }
93    }
94}
95
96#[derive(Debug, Deserialize, Resource, Clone)]
97#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
98pub enum Animation {
99    FPS(f32),
100    FrameDuration(Duration),
101    TotalDuration(Duration),
102}
103
104impl Animation {
105    pub fn to_sprite_update(&self, layer_data: &LayerData) -> SpriteFrameUpdate {
106        let total = layer_data.cols * layer_data.rows;
107        let duration = match self {
108            Self::FPS(fps) => Duration::from_secs_f32(1. / fps),
109            Self::FrameDuration(duration) => duration.clone(),
110            Self::TotalDuration(duration) => duration.div_f32(total as f32),
111        };
112        SpriteFrameUpdate {
113            total,
114            index: layer_data.index,
115            timer: Timer::new(duration, TimerMode::Repeating),
116        }
117    }
118}
119
120/// Layer initialization data
121#[derive(Debug, Deserialize, Resource, Clone)]
122#[serde(default)]
123#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
124pub struct LayerData {
125    /// Relative speed of layer to the camera movement.
126    /// If the speed value is set to 1.0, the layer won't move in that direction.
127    pub speed: LayerSpeed,
128
129    pub repeat: LayerRepeat,
130
131    /// Path to layer texture file
132    pub path: String,
133    /// Size of a tile of the texture
134    pub tile_size: UVec2,
135    /// Columns in the texture file
136    pub cols: usize,
137    /// Rows in the texture file
138    pub rows: usize,
139    /// Scale of the texture
140    pub scale: Vec2,
141    /// Z position of the layer
142    pub z: f32,
143    /// Default initial position of the Entity container
144    pub position: Vec2,
145
146    pub color: Color,
147
148    pub index: usize,
149
150    pub flip: (bool, bool),
151
152    pub animation: Option<Animation>,
153}
154
155impl LayerData {
156    pub fn create_texture_atlas_layout(&self) -> TextureAtlasLayout {
157        TextureAtlasLayout::from_grid(self.tile_size, self.cols as u32, self.rows as u32, None, None)
158    }
159
160    pub fn create_sprite(&self) -> Sprite {
161        Sprite {
162            color: self.color,
163            flip_x: self.flip.0,
164            flip_y: self.flip.1,
165            ..Default::default()
166        }
167    }
168
169    pub fn crate_layer_texture(&self) -> LayerTextureComponent {
170        LayerTextureComponent {
171            width: self.tile_size.x as f32,
172            height: self.tile_size.y as f32,
173        }
174    }
175
176    pub fn create_animation_bundle(&self) -> Option<impl Bundle> {
177        self.animation.as_ref().map(|animation| animation.to_sprite_update(self))
178    }
179}
180
181impl Default for LayerData {
182    fn default() -> Self {
183        Self {
184            speed: LayerSpeed::Horizontal(1.0),
185            repeat: LayerRepeat::Bidirectional(RepeatStrategy::Same),
186            path: "".to_string(),
187            tile_size: UVec2::ZERO,
188            cols: 1,
189            rows: 1,
190            scale: Vec2::ONE,
191            z: 0.0,
192            position: Vec2::ZERO,
193            color: Color::WHITE,
194            index: 0,
195            flip: (false, false),
196            animation: None,
197        }
198    }
199}
200
201/// Core component for parallax layer
202#[derive(Component)]
203#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
204pub struct LayerComponent {
205    /// Relative speed of layer to the camera movement
206    pub speed: Vec2,
207    ///
208    pub repeat: LayerRepeat,
209    /// Number of rows (x) and columns (y) with the textures in the layer
210    pub texture_count: Vec2,
211
212    pub camera: Entity,
213}
214
215/// Core component for layer texture
216#[derive(Component)]
217#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
218pub struct LayerTextureComponent {
219    /// Width of the texture
220    pub width: f32,
221
222    /// Height of the texture
223    pub height: f32,
224}