use crate::error::{GameError, TextureError}; use crate::map::builder::Map; use crate::systems::components::{DeltaTime, DirectionalAnimated, Position, Renderable, Velocity}; use crate::texture::sprite::SpriteAtlas; use bevy_ecs::entity::Entity; use bevy_ecs::event::EventWriter; use bevy_ecs::system::{NonSendMut, Query, Res}; use sdl2::render::{Canvas, Texture}; use sdl2::video::Window; /// Updates the directional animated texture of an entity. /// /// This runs before the render system so it can update the sprite based on the current direction of travel, as well as whether the entity is moving. pub fn directional_render_system( dt: Res, mut renderables: Query<(&Velocity, &mut DirectionalAnimated, &mut Renderable, &Position)>, mut errors: EventWriter, ) { for (velocity, mut texture, mut renderable, position) in renderables.iter_mut() { let stopped = matches!(position, Position::AtNode(_)); let texture = if stopped { texture.stopped_textures[velocity.direction.as_usize()].as_mut() } else { texture.textures[velocity.direction.as_usize()].as_mut() }; if let Some(texture) = texture { if !stopped { texture.tick(dt.0); } renderable.sprite = *texture.current_tile(); } else { errors.write(TextureError::RenderFailed(format!("Entity has no texture")).into()); continue; } } } /// A non-send resource for the map texture. This just wraps the texture with a type so it can be differentiated when exposed as a resource. pub struct MapTextureResource(pub Texture<'static>); /// A non-send resource for the backbuffer texture. This just wraps the texture with a type so it can be differentiated when exposed as a resource. pub struct BackbufferResource(pub Texture<'static>); pub fn render_system( mut canvas: NonSendMut<&mut Canvas>, map_texture: NonSendMut, mut backbuffer: NonSendMut, mut atlas: NonSendMut, map: Res, renderables: Query<(Entity, &mut Renderable, &Position)>, mut errors: EventWriter, ) { // Clear the main canvas first canvas.set_draw_color(sdl2::pixels::Color::BLACK); canvas.clear(); // Render to backbuffer canvas .with_texture_canvas(&mut backbuffer.0, |backbuffer_canvas| { // Clear the backbuffer backbuffer_canvas.set_draw_color(sdl2::pixels::Color::BLACK); backbuffer_canvas.clear(); // Copy the pre-rendered map texture to the backbuffer backbuffer_canvas .copy(&map_texture.0, None, None) .err() .map(|e| errors.write(TextureError::RenderFailed(e.to_string()).into())); // Render all entities to the backbuffer for (_, mut renderable, position) in renderables // .iter_mut() // .sort_by_key::<&mut Renderable, _, _>(|(renderable, renderable, renderable)| renderable.layer) // .collect() { let pos = position.get_pixel_pos(&map.graph); match pos { Ok(pos) => { let dest = crate::helpers::centered_with_size( glam::IVec2::new(pos.x as i32, pos.y as i32), glam::UVec2::new(renderable.sprite.size.x as u32, renderable.sprite.size.y as u32), ); renderable .sprite .render(backbuffer_canvas, &mut atlas, dest) .err() .map(|e| errors.write(TextureError::RenderFailed(e.to_string()).into())); } Err(e) => { errors.write(e.into()); } } } }) .err() .map(|e| errors.write(TextureError::RenderFailed(e.to_string()).into())); // Copy backbuffer to main canvas and present canvas .copy(&backbuffer.0, None, None) .err() .map(|e| errors.write(TextureError::RenderFailed(e.to_string()).into())); canvas.present(); }