ui_examples/f_sprites.rs
1use std::time::Duration;
2
3use mooeye::{scene_manager, sprite, ui, ui::UiContent};
4
5use ggez::{
6 context::Context,
7 glam::Vec2,
8 graphics::{Color, DrawParam, Rect},
9 *,
10};
11
12// # Sprites
13// In this example, we have a look at the sprite class that can be used for drawing animated images.
14
15/// A struct of 'game data' for scene F.
16/// In addition to our GUI, this also contains sprite and position data for a game entity.
17pub struct FScene {
18 /// The root element of FScene's GUI.
19 gui: ui::UiElement<()>,
20 /// A sprite for a wizard.
21 /// A sprite can be used as an UI element, but also simply as part of your game state separated from the UI
22 sprite: sprite::Sprite,
23 /// Another sprite displaying a cow.
24 /// It will graze next to the wizard.
25 sprite2: sprite::Sprite,
26 /// The position of the wizard sprite on the screen.
27 pos: Vec2,
28 /// The speed of the wizard sprite on the screen
29 v: Vec2,
30}
31
32impl FScene {
33 /// Creates a new FScene in the mooeye-idiomatic way.
34 pub fn new(ctx: &Context) -> Result<Self, GameError> {
35 // Reusing the visuals from E.
36
37 let vis = ui::Visuals::new(
38 Color::from_rgb(180, 120, 60),
39 Color::from_rgb(18, 12, 6),
40 1.,
41 0.,
42 );
43
44 let hover_vis = ui::Visuals::new(
45 Color::from_rgb(160, 100, 40),
46 Color::from_rgb(18, 12, 6),
47 3.,
48 0.,
49 );
50
51 let cont_vis = ui::Visuals::new(
52 Color::from_rgb(60, 120, 180),
53 Color::from_rgb(180, 180, 190),
54 1.,
55 0.,
56 );
57
58 // A sprite can be loaded by specifying a path, just like an Image.
59 // Additionaly, you need to inform the sprite of the grid size of its sheet and the duration each frame is displayed.
60 let ui_sprite = sprite::Sprite::from_path(
61 "/moo-sheet_16_16.png",
62 ctx,
63 16,
64 24,
65 Duration::from_secs_f32(0.25),
66 )?
67 // Just like any UI element, a sprite can have visuals, tooltip, ect.
68 .to_element_builder(1, ctx)
69 .scaled(5., 5.)
70 .with_visuals(vis)
71 .with_hover_visuals(hover_vis)
72 .with_tooltip(
73 graphics::Text::new("This is a sprite! Click it to end the scene.")
74 .set_scale(28.)
75 .set_font("Bahnschrift")
76 .to_owned()
77 .to_element_builder(0, ctx)
78 .with_visuals(cont_vis)
79 .build(),
80 )
81 .as_shrink()
82 .build();
83
84 // Sprites can also be initiated from a sprite pool, to make repeated file system access unneccessary
85 // and streamline loading of multiple sprites. This requires sprites in the folder to be formatted appropriately.
86
87 let sprite_pool = sprite::SpritePool::new()
88 // with_folder loads all .png/.bmp/.jpg/.jpeg files from the passed folder and optionally its subfolders
89 .with_folder(ctx, "/", true);
90
91 // We can now init a sprite from the pool. Sprites are saved in the pool with a key corresponding to their relative path
92 // (from the resource folder) with the format information and file ending removed.
93 let non_ui_sprite = sprite_pool.init_sprite("/mage-sheet", Duration::from_secs_f32(0.2))?;
94
95 // you can also initialize a sprite pool without any folder at all
96 let mut sprite_pool2 = sprite::SpritePool::new();
97
98 // in this case, you can use lazy initialisation of sprites to fill the sprite pool only with those sprites currently needed.
99 // Lazy initilisation draws from the pool if possible, from the file system if needed (and loads into the pool in this case) and panics if it can't find anything in the fs.
100 // Requires a mutable sprite pool!
101 // For testing purposes, we are loading a sprite we have already loaded - this should be drawn from the pool.
102 let lazy_sprite =
103 sprite_pool2.init_sprite_lazy(ctx, "/mage-sheet", Duration::from_secs_f32(0.5))?;
104 // now load the correct sprite
105 let lazy_sprite =
106 sprite_pool2.init_sprite_lazy(ctx, "/moo-sheet", lazy_sprite.get_frame_time())?;
107
108 Ok(Self {
109 gui: ui_sprite,
110 sprite: non_ui_sprite,
111 sprite2: lazy_sprite,
112 pos: Vec2::new(50., 200.),
113 v: Vec2::new(4., 4.),
114 })
115 }
116}
117
118impl scene_manager::Scene for FScene {
119 fn update(&mut self, ctx: &mut Context) -> Result<scene_manager::SceneSwitch, GameError> {
120 // Actually implementing some game state logic.
121
122 // Pressing space changes the variant of the sprite.
123 if ctx
124 .keyboard
125 .is_key_just_pressed(winit::event::VirtualKeyCode::Space)
126 {
127 self.sprite.set_variant(self.sprite.get_variant() + 1);
128 }
129
130 // Move the sprite.
131 self.pos += self.v;
132
133 // Make the sprite bounce off the screen edges.
134 let scaling = 5.;
135
136 if self.pos.x - scaling * 4. < 0. || self.pos.x + scaling * 4. >= ctx.gfx.drawable_size().0
137 {
138 self.v.x *= -1.;
139 }
140
141 if self.pos.y - scaling * 8. < 0. || self.pos.y + scaling * 8. >= ctx.gfx.drawable_size().1
142 {
143 self.v.y *= -1.;
144 }
145
146 // And handle messages as usual
147
148 let messages = self.gui.manage_messages(ctx, None);
149
150 if messages.contains(&ui::UiMessage::Triggered(1)) {
151 return Ok(scene_manager::SceneSwitch::pop(1));
152 }
153
154 Ok(scene_manager::SceneSwitch::None)
155 }
156
157 fn draw(&mut self, ctx: &mut Context, mouse_listen: bool) -> Result<(), GameError> {
158 // Once again, we first create a canvas and set a pixel sampler. Note that this time, we dont clear the background.
159
160 let mut canvas = ggez::graphics::Canvas::from_frame(ctx, None);
161 canvas.set_sampler(ggez::graphics::Sampler::nearest_clamp());
162
163 // Drawing of our (limited) game state.
164
165 // see if we need to mirror our sprite if it moves left
166 let mirroring = if self.v.x > 0. { 1. } else { -1. };
167 let scaling = 5.;
168
169 self.sprite.draw_sprite(
170 ctx,
171 &mut canvas,
172 DrawParam::new().dest_rect(Rect::new(
173 self.pos.x - scaling * 4. * mirroring,
174 self.pos.y - scaling * 8.,
175 scaling * mirroring,
176 scaling,
177 )),
178 );
179
180 let scaling = 2.;
181
182 self.sprite2.draw_sprite(
183 ctx,
184 &mut canvas,
185 DrawParam::new().dest_rect(Rect::new(
186 self.pos.x - scaling * 4. + 32.,
187 self.pos.y - scaling * 8. + 32.,
188 scaling,
189 scaling,
190 )),
191 );
192
193 // And once again drawing the GUI.
194
195 self.gui.draw_to_screen(ctx, &mut canvas, mouse_listen);
196
197 canvas.finish(ctx)?;
198
199 Ok(())
200 }
201}