pub struct SpriteAnimation { /* private fields */ }Expand description
Animation state for cycling through sprite sheet frames.
Implementations§
Source§impl SpriteAnimation
impl SpriteAnimation
Sourcepub fn new(total_frames: u32, fps: f32) -> Self
pub fn new(total_frames: u32, fps: f32) -> Self
Create a new animation for all frames.
Examples found in repository?
examples/sprite_sheet.rs (line 195)
152fn main() {
153 logging::init();
154
155 run_app(|ctx| {
156 let graphics_ctx =
157 GraphicsContext::new_owned_sync().expect("Failed to create graphics context");
158 let mut windows = HashMap::new();
159
160 let scale = Window::platform_dpi() as f32;
161 let window = ctx
162 .create_window(WindowDescriptor {
163 title: "Sprite Sheet Animation Example".to_string(),
164 size: Some(WinitPhysicalSize::new(400.0 * scale, 400.0 * scale)),
165 ..Default::default()
166 })
167 .expect("Failed to create window");
168
169 let renderable_window = RenderWindowBuilder::new()
170 .color_format(wgpu::TextureFormat::Bgra8UnormSrgb)
171 .with_depth_default()
172 .build(window, graphics_ctx.clone())
173 .expect("Failed to create render window");
174
175 let window_id = renderable_window.id();
176 windows.insert(window_id, renderable_window);
177
178 // Generate sprite sheet
179 let (sprite_data, tex_width, tex_height) = generate_sprite_sheet_data();
180 let sprite_sheet = SpriteSheet::from_data(
181 &graphics_ctx,
182 &sprite_data,
183 tex_width,
184 tex_height,
185 SpriteSheetDescriptor {
186 sprite_width: 64,
187 sprite_height: 64,
188 columns: 4,
189 rows: 1,
190 ..Default::default()
191 },
192 );
193
194 // Create animation (4 frames at 8 fps)
195 let animation = SpriteAnimation::new(4, 8.0);
196
197 // Create shader module
198 let shader = graphics_ctx
199 .device()
200 .create_shader_module(wgpu::ShaderModuleDescriptor {
201 label: Some("Sprite Shader"),
202 source: wgpu::ShaderSource::Wgsl(SHADER.into()),
203 });
204
205 // Create bind group layout
206 let bind_group_layout =
207 graphics_ctx
208 .device()
209 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
210 label: Some("Sprite Bind Group Layout"),
211 entries: &[
212 wgpu::BindGroupLayoutEntry {
213 binding: 0,
214 visibility: wgpu::ShaderStages::VERTEX,
215 ty: wgpu::BindingType::Buffer {
216 ty: wgpu::BufferBindingType::Uniform,
217 has_dynamic_offset: false,
218 min_binding_size: None,
219 },
220 count: None,
221 },
222 wgpu::BindGroupLayoutEntry {
223 binding: 1,
224 visibility: wgpu::ShaderStages::FRAGMENT,
225 ty: wgpu::BindingType::Texture {
226 sample_type: wgpu::TextureSampleType::Float { filterable: true },
227 view_dimension: wgpu::TextureViewDimension::D2,
228 multisampled: false,
229 },
230 count: None,
231 },
232 wgpu::BindGroupLayoutEntry {
233 binding: 2,
234 visibility: wgpu::ShaderStages::FRAGMENT,
235 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
236 count: None,
237 },
238 ],
239 });
240
241 // Create pipeline layout
242 let pipeline_layout =
243 graphics_ctx
244 .device()
245 .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
246 label: Some("Sprite Pipeline Layout"),
247 bind_group_layouts: &[&bind_group_layout],
248 push_constant_ranges: &[],
249 });
250
251 // Create render pipeline
252 let pipeline =
253 graphics_ctx
254 .device()
255 .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
256 label: Some("Sprite Pipeline"),
257 layout: Some(&pipeline_layout),
258 vertex: wgpu::VertexState {
259 module: &shader,
260 entry_point: Some("vs_main"),
261 buffers: &[wgpu::VertexBufferLayout {
262 array_stride: std::mem::size_of::<Vertex>() as u64,
263 step_mode: wgpu::VertexStepMode::Vertex,
264 attributes: &[
265 wgpu::VertexAttribute {
266 offset: 0,
267 shader_location: 0,
268 format: wgpu::VertexFormat::Float32x2,
269 },
270 wgpu::VertexAttribute {
271 offset: 8,
272 shader_location: 1,
273 format: wgpu::VertexFormat::Float32x2,
274 },
275 ],
276 }],
277 compilation_options: wgpu::PipelineCompilationOptions::default(),
278 },
279 fragment: Some(wgpu::FragmentState {
280 module: &shader,
281 entry_point: Some("fs_main"),
282 targets: &[Some(wgpu::ColorTargetState {
283 format: wgpu::TextureFormat::Bgra8UnormSrgb,
284 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
285 write_mask: wgpu::ColorWrites::ALL,
286 })],
287 compilation_options: wgpu::PipelineCompilationOptions::default(),
288 }),
289 primitive: wgpu::PrimitiveState {
290 topology: wgpu::PrimitiveTopology::TriangleList,
291 ..Default::default()
292 },
293 depth_stencil: None,
294 multisample: wgpu::MultisampleState::default(),
295 multiview: None,
296 cache: None,
297 });
298
299 // Create uniform buffer
300 let uniforms = Uniforms {
301 mvp: [
302 [1.0, 0.0, 0.0, 0.0],
303 [0.0, 1.0, 0.0, 0.0],
304 [0.0, 0.0, 1.0, 0.0],
305 [0.0, 0.0, 0.0, 1.0],
306 ],
307 };
308 let uniform_buffer =
309 graphics_ctx
310 .device()
311 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
312 label: Some("Uniform Buffer"),
313 contents: bytemuck::cast_slice(&[uniforms]),
314 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
315 });
316
317 // Create sampler
318 let sampler = graphics_ctx
319 .device()
320 .create_sampler(&wgpu::SamplerDescriptor {
321 label: Some("Sprite Sampler"),
322 mag_filter: wgpu::FilterMode::Linear,
323 min_filter: wgpu::FilterMode::Linear,
324 ..Default::default()
325 });
326
327 // Create bind group
328 let bind_group = graphics_ctx
329 .device()
330 .create_bind_group(&wgpu::BindGroupDescriptor {
331 label: Some("Sprite Bind Group"),
332 layout: &bind_group_layout,
333 entries: &[
334 wgpu::BindGroupEntry {
335 binding: 0,
336 resource: uniform_buffer.as_entire_binding(),
337 },
338 wgpu::BindGroupEntry {
339 binding: 1,
340 resource: wgpu::BindingResource::TextureView(sprite_sheet.view()),
341 },
342 wgpu::BindGroupEntry {
343 binding: 2,
344 resource: wgpu::BindingResource::Sampler(&sampler),
345 },
346 ],
347 });
348
349 // Initial vertex buffer (will be updated each frame with new UVs)
350 let vertices = create_quad_vertices(0.0, 0.0, 1.0, 1.0);
351 let vertex_buffer =
352 graphics_ctx
353 .device()
354 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
355 label: Some("Vertex Buffer"),
356 contents: bytemuck::cast_slice(&vertices),
357 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
358 });
359
360 Box::new(App {
361 _context: graphics_ctx,
362 windows,
363 pipeline,
364 bind_group,
365 vertex_buffer,
366 _uniform_buffer: uniform_buffer,
367 sprite_sheet,
368 animation,
369 last_update: Instant::now(),
370 })
371 });
372}Sourcepub fn with_range(start: u32, end: u32, fps: f32) -> Self
pub fn with_range(start: u32, end: u32, fps: f32) -> Self
Create an animation for a range of frames.
Sourcepub fn update(&mut self, dt: f32) -> bool
pub fn update(&mut self, dt: f32) -> bool
Update the animation with elapsed time.
Returns true if the frame changed.
Examples found in repository?
examples/sprite_sheet.rs (line 414)
404 fn update(
405 &mut self,
406 _ctx: &mut astrelis_winit::app::AppCtx,
407 _time: &astrelis_winit::FrameTime,
408 ) {
409 let now = Instant::now();
410 let dt = now.duration_since(self.last_update).as_secs_f32();
411 self.last_update = now;
412
413 // Update animation
414 if self.animation.update(dt) {
415 // Frame changed - update vertex buffer with new UVs
416 let frame = self.animation.current_frame();
417 let uv = self.sprite_sheet.sprite_uv(frame);
418 let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
419
420 // Get context from first window
421 if let Some(window) = self.windows.values().next() {
422 window.context().graphics_context().queue().write_buffer(
423 &self.vertex_buffer,
424 0,
425 bytemuck::cast_slice(&vertices),
426 );
427 }
428 }
429 }Sourcepub fn current_frame(&self) -> u32
pub fn current_frame(&self) -> u32
Get the current frame index.
Examples found in repository?
examples/sprite_sheet.rs (line 416)
404 fn update(
405 &mut self,
406 _ctx: &mut astrelis_winit::app::AppCtx,
407 _time: &astrelis_winit::FrameTime,
408 ) {
409 let now = Instant::now();
410 let dt = now.duration_since(self.last_update).as_secs_f32();
411 self.last_update = now;
412
413 // Update animation
414 if self.animation.update(dt) {
415 // Frame changed - update vertex buffer with new UVs
416 let frame = self.animation.current_frame();
417 let uv = self.sprite_sheet.sprite_uv(frame);
418 let vertices = create_quad_vertices(uv.u_min, uv.v_min, uv.u_max, uv.v_max);
419
420 // Get context from first window
421 if let Some(window) = self.windows.values().next() {
422 window.context().graphics_context().queue().write_buffer(
423 &self.vertex_buffer,
424 0,
425 bytemuck::cast_slice(&vertices),
426 );
427 }
428 }
429 }Sourcepub fn is_playing(&self) -> bool
pub fn is_playing(&self) -> bool
Check if the animation is playing.
Sourcepub fn is_finished(&self) -> bool
pub fn is_finished(&self) -> bool
Check if the animation has finished (only relevant for non-looping).
Trait Implementations§
Source§impl Clone for SpriteAnimation
impl Clone for SpriteAnimation
Source§fn clone(&self) -> SpriteAnimation
fn clone(&self) -> SpriteAnimation
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreAuto Trait Implementations§
impl Freeze for SpriteAnimation
impl RefUnwindSafe for SpriteAnimation
impl Send for SpriteAnimation
impl Sync for SpriteAnimation
impl Unpin for SpriteAnimation
impl UnsafeUnpin for SpriteAnimation
impl UnwindSafe for SpriteAnimation
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more