bevy_parallax/
parallax.rs

1use crate::layer;
2use bevy::{prelude::*, render::view::RenderLayers};
3
4#[cfg(feature = "bevy-inspector-egui")]
5use bevy_inspector_egui::prelude::*;
6
7/// Event to setup and create parallax
8#[derive(Event, Debug)]
9pub struct CreateParallaxEvent {
10    pub layers_data: Vec<layer::LayerData>,
11    pub camera: Entity,
12}
13
14impl CreateParallaxEvent {
15    /// Create layers from layer data
16    pub fn create_layers(
17        &self,
18        commands: &mut Commands,
19        window_size: Vec2,
20        asset_server: &AssetServer,
21        texture_atlases: &mut Assets<TextureAtlasLayout>,
22        render_layer: u8,
23    ) {
24        // Spawn new layers using layer_data
25        for (i, layer) in self.layers_data.iter().enumerate() {
26            let texture: Handle<Image> = asset_server.load(&layer.path);
27            let texture_atlas = layer.create_texture_atlas_layout();
28            let texture_atlas_handle = texture_atlases.add(texture_atlas);
29
30            let sprite_bundle = SpriteBundle {
31                texture,
32                sprite: layer.create_sprite(),
33                ..default()
34            };
35
36            // Spawn a grid of textures, so that they convincingly wrap around the screen when scrolling.
37            // For no repeat layers, only spawn a single row or column in direction of their movement.
38
39            // In every row of the grid, our goal is to have a central texture and at least two that surround it,
40            // plus as much as it would take to fill the rest of the window space in both directions.
41            // The grid should have a pair number so the mirror repeat can work correctly
42            // Same logic applies to vertical placement.
43
44            let max_length = window_size.length();
45
46            let y_max_index = match layer.repeat.has_vertical() {
47                true => f32::ceil(max_length / (layer.tile_size.y as f32 * layer.scale.y)) as i32,
48                false => 0,
49            };
50
51            let x_max_index = match layer.repeat.has_horizontal() {
52                true => f32::ceil(max_length / (layer.tile_size.x as f32 * layer.scale.x)) as i32,
53                false => 0,
54            };
55
56            let texture_count = Vec2::new(f32::max(2.0 * x_max_index as f32, 1.), f32::max(2.0 * y_max_index as f32, 1.));
57
58            let x_range = if layer.repeat.has_horizontal() {
59                (-x_max_index + 1)..=x_max_index
60            } else {
61                0..=0
62            };
63            let y_range = if layer.repeat.has_vertical() {
64                (-y_max_index + 1)..=y_max_index
65            } else {
66                0..=0
67            };
68
69            // Spawn parallax layer entity
70            let mut entity_commands = commands.spawn_empty();
71            entity_commands
72                .insert(Name::new(format!("Parallax Layer ({})", i)))
73                .insert(RenderLayers::from_layers(&[render_layer.into()]))
74                .insert(SpatialBundle {
75                    transform: Transform {
76                        translation: Vec3::new(layer.position.x, layer.position.y, layer.z),
77                        scale: layer.scale.extend(1.0),
78                        ..default()
79                    },
80                    ..default()
81                })
82                .with_children(|parent| {
83                    for x in x_range {
84                        for y in y_range.clone() {
85                            let repeat_strategy = layer.repeat.get_strategy();
86                            let mut adjusted_sprite_bundle = sprite_bundle.clone();
87                            repeat_strategy.transform(&mut adjusted_sprite_bundle, (x, y));
88                            adjusted_sprite_bundle.transform.translation.x = layer.tile_size.x as f32 * x as f32;
89                            adjusted_sprite_bundle.transform.translation.y = layer.tile_size.y as f32 * y as f32;
90                            let mut child_commands = parent.spawn((
91                                adjusted_sprite_bundle,
92                                TextureAtlas {
93                                    layout: texture_atlas_handle.clone(),
94                                    index: 0,
95                                },
96                            ));
97                            child_commands
98                                .insert(RenderLayers::from_layers(&[render_layer.into()]))
99                                .insert(layer.crate_layer_texture());
100                            if let Some(animation_bundle) = layer.create_animation_bundle() {
101                                child_commands.insert(animation_bundle);
102                            }
103                        }
104                    }
105                });
106
107            // Add layer component to entity
108            entity_commands
109                .insert(layer::LayerComponent {
110                    speed: match layer.speed {
111                        layer::LayerSpeed::Horizontal(vx) => Vec2::new(vx, 0.0),
112                        layer::LayerSpeed::Vertical(vy) => Vec2::new(0.0, vy),
113                        layer::LayerSpeed::Bidirectional(vx, vy) => Vec2::new(vx, vy),
114                    },
115                    repeat: layer.repeat.clone(),
116                    texture_count,
117                    camera: self.camera,
118                })
119                .insert(RenderLayers::from_layers(&[render_layer.into()]));
120        }
121    }
122}
123
124/// Event used to update parallax
125#[derive(Event, Debug)]
126pub struct ParallaxMoveEvent {
127    /// camera translation
128    pub translation: Vec2,
129
130    /// camera rotation
131    pub rotation: f32,
132
133    pub camera: Entity,
134}
135
136impl ParallaxMoveEvent {
137    pub fn has_translation(&self) -> bool {
138        self.translation != Vec2::ZERO
139    }
140
141    pub fn has_right_translation(&self) -> bool {
142        self.translation.x > 0.
143    }
144
145    pub fn has_left_translation(&self) -> bool {
146        self.translation.x < 0.
147    }
148
149    pub fn has_up_translation(&self) -> bool {
150        self.translation.y > 0.
151    }
152
153    pub fn has_down_translation(&self) -> bool {
154        self.translation.y < 0.
155    }
156}
157
158/// Attach to a single camera to be used with parallax
159#[derive(Component)]
160#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
161pub struct ParallaxCameraComponent {
162    pub render_layer: u8,
163    pub limits: Vec2Limit,
164}
165
166#[derive(Debug, Clone, Copy)]
167#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
168#[cfg_attr(feature = "bevy-inspector-egui", reflect(InspectorOptions))]
169pub struct Limit {
170    pub min: f32,
171    pub max: f32,
172}
173
174impl Default for Limit {
175    fn default() -> Self {
176        Self {
177            min: f32::NEG_INFINITY,
178            max: f32::INFINITY,
179        }
180    }
181}
182
183impl Limit {
184    pub fn new(min: f32, max: f32) -> Self {
185        Self { min: min, max: max }
186    }
187
188    pub fn zero_to_infinity() -> Self {
189        Self {
190            min: 0.,
191            max: f32::INFINITY,
192        }
193    }
194
195    pub fn zero_to(max: f32) -> Self {
196        Self { min: 0., max: max }
197    }
198
199    pub fn fix(&self, value: f32) -> f32 {
200        f32::min(f32::max(value, self.min), self.max)
201    }
202}
203
204#[derive(Debug, Clone, Copy)]
205#[cfg_attr(feature = "bevy-inspector-egui", derive(Reflect, InspectorOptions))]
206#[cfg_attr(feature = "bevy-inspector-egui", reflect(InspectorOptions))]
207pub struct Vec2Limit {
208    pub x: Limit,
209    pub y: Limit,
210}
211
212impl Vec2Limit {
213    pub fn new(x: Limit, y: Limit) -> Self {
214        Self { x: x, y: y }
215    }
216
217    pub fn fix(&self, vec: Vec2) -> Vec2 {
218        Vec2::new(self.x.fix(vec.x), self.y.fix(vec.y))
219    }
220}
221
222impl Default for Vec2Limit {
223    fn default() -> Self {
224        Self {
225            x: default(),
226            y: default(),
227        }
228    }
229}
230
231impl ParallaxCameraComponent {
232    pub fn inside_limits(&self, translation: Vec2) -> Vec2 {
233        self.limits.fix(translation)
234    }
235
236    pub fn new(render_layer: u8) -> Self {
237        Self {
238            render_layer: render_layer,
239            ..default()
240        }
241    }
242}
243
244impl Default for ParallaxCameraComponent {
245    fn default() -> Self {
246        Self {
247            render_layer: 0,
248            limits: default(),
249        }
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use bevy::prelude::*;
256
257    use crate::ParallaxMoveEvent;
258
259    #[test]
260    fn test_check() {
261        assert_eq!(true, true);
262    }
263
264    #[test]
265    fn test_parallax_event() {
266        let camera = Entity::from_raw(0);
267
268        let no_movement = ParallaxMoveEvent {
269            translation: Vec2::ZERO,
270            rotation: 0.,
271            camera: camera,
272        };
273        assert_eq!(no_movement.has_translation(), false);
274        assert_eq!(no_movement.has_up_translation(), false);
275        assert_eq!(no_movement.has_down_translation(), false);
276        assert_eq!(no_movement.has_left_translation(), false);
277        assert_eq!(no_movement.has_right_translation(), false);
278
279        let up = ParallaxMoveEvent {
280            translation: Vec2::new(0., 1.),
281            rotation: 0.,
282            camera: camera,
283        };
284        assert_eq!(up.has_translation(), true);
285        assert_eq!(up.has_up_translation(), true);
286        assert_eq!(up.has_down_translation(), false);
287        assert_eq!(up.has_left_translation(), false);
288        assert_eq!(up.has_right_translation(), false);
289
290        let down = ParallaxMoveEvent {
291            translation: Vec2::new(0., -1.),
292            rotation: 0.,
293            camera: camera,
294        };
295        assert_eq!(down.has_translation(), true);
296        assert_eq!(down.has_up_translation(), false);
297        assert_eq!(down.has_down_translation(), true);
298        assert_eq!(down.has_left_translation(), false);
299        assert_eq!(down.has_right_translation(), false);
300
301        let left = ParallaxMoveEvent {
302            translation: Vec2::new(-1., 0.),
303            rotation: 0.,
304            camera: camera,
305        };
306        assert_eq!(left.has_translation(), true);
307        assert_eq!(left.has_up_translation(), false);
308        assert_eq!(left.has_down_translation(), false);
309        assert_eq!(left.has_left_translation(), true);
310        assert_eq!(left.has_right_translation(), false);
311
312        let right = ParallaxMoveEvent {
313            translation: Vec2::new(1., 0.),
314            rotation: 0.,
315            camera: camera,
316        };
317        assert_eq!(right.has_translation(), true);
318        assert_eq!(right.has_up_translation(), false);
319        assert_eq!(right.has_down_translation(), false);
320        assert_eq!(right.has_left_translation(), false);
321        assert_eq!(right.has_right_translation(), true);
322
323        let left_down = ParallaxMoveEvent {
324            translation: Vec2::new(-1., -1.),
325            rotation: 0.,
326            camera: camera,
327        };
328        assert_eq!(left_down.has_translation(), true);
329        assert_eq!(left_down.has_up_translation(), false);
330        assert_eq!(left_down.has_down_translation(), true);
331        assert_eq!(left_down.has_left_translation(), true);
332        assert_eq!(left_down.has_right_translation(), false);
333
334        let up_right = ParallaxMoveEvent {
335            translation: Vec2::new(1., 1.),
336            rotation: 0.,
337            camera: camera,
338        };
339        assert_eq!(up_right.has_translation(), true);
340        assert_eq!(up_right.has_up_translation(), true);
341        assert_eq!(up_right.has_down_translation(), false);
342        assert_eq!(up_right.has_left_translation(), false);
343        assert_eq!(up_right.has_right_translation(), true);
344    }
345}