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