diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/constants.rs | 9 | ||||
-rw-r--r-- | src/layer.rs | 37 | ||||
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/map.rs | 91 | ||||
-rw-r--r-- | src/math.rs | 5 | ||||
-rw-r--r-- | src/player.rs | 6 | ||||
-rw-r--r-- | src/state.rs | 12 | ||||
-rw-r--r-- | src/tile.rs | 90 | ||||
-rw-r--r-- | src/tileset.rs | 64 | ||||
-rw-r--r-- | src/xmlelements.rs | 113 |
10 files changed, 337 insertions, 93 deletions
diff --git a/src/constants.rs b/src/constants.rs index 367e595..c154066 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -4,8 +4,7 @@ pub const TILE_SCALE: f32 = 3.0; pub const PLAYER_SPEED: f32 = 5.0; -pub const FLIP_HORIZONTALLY_FLAG: u32 = 0x8000_0000; -pub const FLIP_VERTICALLY_FLAG: u32 = 0x4000_0000; -pub const FLIP_DIAGONALLY_FLAG: u32 = 0x2000_0000; -pub const ALL_FLIP_FLAGS: u32 = - FLIP_DIAGONALLY_FLAG | FLIP_HORIZONTALLY_FLAG | FLIP_VERTICALLY_FLAG; +pub const FLIP_HORIZONTAL_FLAG: usize = 0x8000_0000; +pub const FLIP_VERTICAL_FLAG: usize = 0x4000_0000; +pub const FLIP_DIAGONAL_FLAG: usize = 0x2000_0000; +pub const ALL_FLIP_FLAGS: usize = FLIP_DIAGONAL_FLAG | FLIP_HORIZONTAL_FLAG | FLIP_VERTICAL_FLAG; diff --git a/src/layer.rs b/src/layer.rs new file mode 100644 index 0000000..c9f32e8 --- /dev/null +++ b/src/layer.rs @@ -0,0 +1,37 @@ +use ggez::graphics::spritebatch::SpriteBatch; + +use crate::tile::Tile; +use crate::tileset::Tileset; + +pub struct Layer { + pub tiles: Vec<Tile>, + pub width: usize, + pub height: usize, +} + +impl Layer { + pub fn new(text: &str, tileset: &Tileset, width: usize, height: usize) -> Layer { + Layer { + tiles: text + .replace("\n", "") + .split(',') + .enumerate() + .map(|(i, s)| Tile::new(s, i, tileset, width, height)) + .collect(), + width, + height, + } + } + + pub fn update(&mut self) { + for tile in self.tiles.iter_mut() { + tile.update(); + } + } + + pub fn draw(&self, spritebatch: &mut SpriteBatch) { + for tile in self.tiles.iter() { + tile.draw(spritebatch); + } + } +} @@ -1,7 +1,10 @@ pub mod camera; pub mod constants; +pub mod layer; pub mod map; +pub mod math; pub mod player; pub mod state; +pub mod tile; pub mod tileset; pub mod xmlelements; @@ -1,10 +1,9 @@ use ggez::filesystem::File; -use ggez::graphics::{spritebatch::SpriteBatch, DrawParam}; -use ggez::nalgebra::{Point2, Vector2}; -use std::f32::consts::PI; +use ggez::graphics::spritebatch::SpriteBatch; use xml::reader::XmlEvent::Characters; use crate::constants; +use crate::layer::Layer; use crate::tileset::Tileset; use crate::xmlelements::XMLElements; @@ -15,17 +14,26 @@ pub struct Map { } impl Map { - pub fn new(file: File) -> Map { + pub fn new(file: File, tileset: &Tileset) -> Map { let elements = XMLElements::new(file); - let width = elements.get_element_attribute("map", "width").unwrap(); - let height = elements.get_element_attribute("map", "height").unwrap(); + let width = elements + .get_element_attribute("map", "width") + .unwrap() + .parse() + .unwrap(); + let height = elements + .get_element_attribute("map", "height") + .unwrap() + .parse() + .unwrap(); + let layers = elements .events .iter() .filter_map(|e| { if let Characters(text) = e { - Some(Layer::new(text.to_string())) + Some(Layer::new(text, tileset, width, height)) } else { None } @@ -39,28 +47,15 @@ impl Map { } } - pub fn draw(&self, spritebatch: &mut SpriteBatch, tileset: &Tileset) { + pub fn draw(&self, spritebatch: &mut SpriteBatch) { for layer in self.layers.iter() { - for x in 0..self.width { - for y in 0..self.height { - let tile_id = layer.data[x + (y * self.height)]; - let (tile_id, rotate) = self.decode(tile_id as u32); - - let offset = (constants::TILE_WIDTH / 2.0) * constants::TILE_SCALE; - - let draw_param = DrawParam::default() - .src(tileset.tiles[tile_id as usize]) - .rotation(rotate) - .offset(Point2::new(0.5, 0.5)) - .dest(Point2::new( - (constants::TILE_WIDTH * constants::TILE_SCALE * x as f32) + offset, - (constants::TILE_HEIGHT * constants::TILE_SCALE * y as f32) + offset, - )) - .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)); + layer.draw(spritebatch); + } + } - spritebatch.add(draw_param); - } - } + pub fn update(&mut self) { + for layer in self.layers.iter_mut() { + layer.update(); } } @@ -70,46 +65,4 @@ impl Map { (constants::TILE_HEIGHT * constants::TILE_SCALE) * self.height as f32, ) } - - fn decode(&self, tile_id: u32) -> (u32, f32) { - //let flip_d = (tile_id & constants::FLIP_DIAGONALLY_FLAG) == constants::FLIP_DIAGONALLY_FLAG; - let flip_h = - (tile_id & constants::FLIP_HORIZONTALLY_FLAG) == constants::FLIP_HORIZONTALLY_FLAG; - let flip_v = (tile_id & constants::FLIP_VERTICALLY_FLAG) == constants::FLIP_VERTICALLY_FLAG; - - let new_tile_id = if flip_h | flip_v { - tile_id & !constants::ALL_FLIP_FLAGS - } else { - tile_id - }; - - let rotate = match (flip_h, flip_v) { - (true, false) => self.convert_angle_to_rad(90.0), - (true, true) => self.convert_angle_to_rad(180.0), - (false, true) => self.convert_angle_to_rad(270.0), - (false, false) => 0.0, - }; - - (new_tile_id, rotate) - } - - fn convert_angle_to_rad(&self, angle: f32) -> f32 { - angle * (PI / 180.0) - } -} - -pub struct Layer { - pub data: Vec<usize>, -} - -impl Layer { - pub fn new(text: String) -> Layer { - Layer { - data: text - .replace("\n", "") - .split(',') - .map(|s| s.parse().unwrap()) - .collect(), - } - } } diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..c7ea7f6 --- /dev/null +++ b/src/math.rs @@ -0,0 +1,5 @@ +use std::f32::consts::PI; + +pub fn convert_angle_to_rad(angle: f32) -> f32 { + angle * (PI / 180.0) +} diff --git a/src/player.rs b/src/player.rs index bc019b9..e9c3a75 100644 --- a/src/player.rs +++ b/src/player.rs @@ -11,8 +11,8 @@ pub struct Player { pub position: Point2<f32>, state: PlayerState, tile: Rect, - animation: Vec<(u32, Rect)>, - animations: HashMap<PlayerState, Vec<(u32, Rect)>>, + animation: Vec<(usize, Rect)>, + animations: HashMap<PlayerState, Vec<(usize, Rect)>>, map_height: f32, map_width: f32, } @@ -106,7 +106,7 @@ impl Player { let index = match self.animation.iter().position(|a| a.1 == self.tile) { Some(index) => { - if check_update_time(context, self.animation[index].0) { + if check_update_time(context, self.animation[index].0 as u32) { index + 1 } else { index diff --git a/src/state.rs b/src/state.rs index b0a497c..a981924 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,5 +1,5 @@ use ggez::event::{EventHandler, KeyCode, KeyMods}; -use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image}; +use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image, WrapMode}; use ggez::{filesystem, Context, GameResult}; use crate::camera::Camera; @@ -9,7 +9,6 @@ use crate::tileset::Tileset; pub struct State { map: Map, - tileset: Tileset, spritebatch: SpriteBatch, camera: Camera, player: Player, @@ -19,13 +18,15 @@ impl State { pub fn new(context: &mut Context) -> GameResult<State> { let mut image = Image::new(context, "/tileset.png")?; image.set_filter(FilterMode::Nearest); + image.set_wrap(WrapMode::Mirror, WrapMode::Mirror); - let map = Map::new(filesystem::open(context, "/map.tmx")?); + let tileset = Tileset::new(filesystem::open(context, "/tileset.tsx")?); + + let map = Map::new(filesystem::open(context, "/map.tmx")?, &tileset); let map_dimensions = map.get_dimensions(); Ok(State { map, - tileset: Tileset::new(filesystem::open(context, "/tileset.tsx")?), spritebatch: SpriteBatch::new(image), camera: Camera::new(context, map_dimensions), player: Player::new(map_dimensions), @@ -35,6 +36,7 @@ impl State { impl EventHandler for State { fn update(&mut self, context: &mut Context) -> GameResult { + self.map.update(); self.player.update(context); self.camera.give_center(self.player.position); Ok(()) @@ -43,7 +45,7 @@ impl EventHandler for State { fn draw(&mut self, context: &mut Context) -> GameResult { graphics::clear(context, graphics::BLACK); - self.map.draw(&mut self.spritebatch, &self.tileset); + self.map.draw(&mut self.spritebatch); self.player.draw(&mut self.spritebatch); graphics::draw( diff --git a/src/tile.rs b/src/tile.rs new file mode 100644 index 0000000..cb2aa83 --- /dev/null +++ b/src/tile.rs @@ -0,0 +1,90 @@ +use ggez::graphics::{spritebatch::SpriteBatch, DrawParam, Rect}; +use ggez::nalgebra::{Point2, Vector2}; +use std::time::Instant; + +use crate::constants; +use crate::math::convert_angle_to_rad; +use crate::tileset::Tileset; + +pub struct Tile { + source: Rect, + animations: Option<Vec<(usize, Rect)>>, + timer: Instant, + destination: Point2<f32>, + rotation: f32, +} + +impl Tile { + pub fn new(text: &str, i: usize, tileset: &Tileset, width: usize, height: usize) -> Tile { + let id = text.parse::<usize>().unwrap(); + + let flip_d = (id & constants::FLIP_DIAGONAL_FLAG) == constants::FLIP_DIAGONAL_FLAG; + let flip_h = (id & constants::FLIP_HORIZONTAL_FLAG) == constants::FLIP_HORIZONTAL_FLAG; + let flip_v = (id & constants::FLIP_VERTICAL_FLAG) == constants::FLIP_VERTICAL_FLAG; + + let id = if flip_h | flip_v | flip_d { + id & !constants::ALL_FLIP_FLAGS + } else { + id + }; + + let (source, rotation) = match (flip_d, flip_h, flip_v) { + (true, true, true) => (Tile::flip(tileset.get(id)), convert_angle_to_rad(90.0)), + (true, true, false) => (tileset.get(id), convert_angle_to_rad(90.0)), + (true, false, true) => (tileset.get(id), convert_angle_to_rad(270.0)), + //(true, false, false) => (), + (false, true, true) => (tileset.get(id), convert_angle_to_rad(180.0)), + (false, true, false) => (Tile::flip(tileset.get(id)), 0.0), + //(false, false, true) => (), + //(false, false, false) => (), + _ => (tileset.get(id), 0.0), + }; + + let x = i as f32 % width as f32; + let y = (i as f32 / height as f32).floor(); + let offset = (constants::TILE_WIDTH / 2.0) * constants::TILE_SCALE; + + let destination = Point2::new( + (constants::TILE_WIDTH * constants::TILE_SCALE * x) + offset, + (constants::TILE_HEIGHT * constants::TILE_SCALE * y) + offset, + ); + + Tile { + source, + animations: tileset.get_animations(id), + timer: Instant::now(), + destination, + rotation, + } + } + + fn flip(rect: Rect) -> Rect { + let mut r = rect; + r.x *= -1.0; + r.x -= rect.w; + r + } + + pub fn update(&mut self) { + if let Some(animations) = &self.animations { + if let Some(mut i) = animations.iter().position(|a| a.1 == self.source) { + if self.timer.elapsed().as_millis() > animations[i].0 as u128 { + i = if i == animations.len() - 1 { 0 } else { i + 1 }; + self.source = animations[i].1; + self.timer = Instant::now() + } + } + } + } + + pub fn draw(&self, spritebatch: &mut SpriteBatch) { + let draw_param = DrawParam::default() + .src(self.source) + .dest(self.destination) + .offset(Point2::new(0.5, 0.5)) + .rotation(self.rotation) + .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)); + + spritebatch.add(draw_param); + } +} diff --git a/src/tileset.rs b/src/tileset.rs index ae65097..548cde6 100644 --- a/src/tileset.rs +++ b/src/tileset.rs @@ -1,37 +1,87 @@ use ggez::filesystem::File; use ggez::graphics::Rect; +use std::collections::HashMap; use crate::constants; -use crate::xmlelements::XMLElements; +use crate::xmlelements::{Property, XMLElements}; pub struct Tileset { - pub tiles: Vec<Rect>, + tiles: HashMap<usize, Rect>, + properties: HashMap<usize, Property>, } impl Tileset { pub fn new(file: File) -> Tileset { let elements = XMLElements::new(file); - let height = elements.get_element_attribute("image", "height").unwrap(); + let height = elements + .get_element_attribute("image", "height") + .unwrap() + .parse::<usize>() + .unwrap(); let columns = elements .get_element_attribute("tileset", "columns") + .unwrap() + .parse::<usize>() .unwrap(); let rows = height / (constants::TILE_HEIGHT as usize); - let mut tiles = Vec::new(); - tiles.push(Rect::zero()); + let mut tiles = HashMap::new(); + tiles.insert(0, Rect::zero()); let w = 1.0 / columns as f32; let h = 1.0 / rows as f32; + let mut key = 1; for r in 0..rows { for c in 0..columns { let x = c as f32 / columns as f32; let y = r as f32 / rows as f32; - tiles.push(Rect::new(x, y, w, h)); + tiles.insert(key, Rect::new(x, y, w, h)); + key += 1; } } - Tileset { tiles } + let mut properties = HashMap::new(); + + for tile_element in elements.get_elements("tile") { + let tile_id = XMLElements::get_attribute(&tile_element, "id") + .unwrap() + .parse::<usize>() + .unwrap(); + + let property_elements = elements.get_children(&tile_element, "property"); + + properties.insert(tile_id + 1, Property::new(property_elements)); + } + + Tileset { tiles, properties } + } + + pub fn get(&self, id: usize) -> Rect { + *self.tiles.get(&id).unwrap() + } + + pub fn get_animations(&self, id: usize) -> Option<Vec<(usize, Rect)>> { + if let Some(property) = self.properties.get(&id) { + let entitys_properties: HashMap<usize, Property> = self + .properties + .clone() + .into_iter() + .filter(|(_, p)| p.entity == property.entity) + .collect(); + Some( + entitys_properties + .iter() + .map(|(id, p)| (p.delay, self.get(*id))) + .collect(), + ) + } else { + None + } + } + + pub fn get_property(&self, id: usize) -> Option<&Property> { + self.properties.get(&id) } } diff --git a/src/xmlelements.rs b/src/xmlelements.rs index 86d7c77..ea880c8 100644 --- a/src/xmlelements.rs +++ b/src/xmlelements.rs @@ -2,9 +2,39 @@ use ggez::filesystem::File; use std::io::BufReader; use xml::reader::{ EventReader, - XmlEvent::{self, StartElement}, + XmlEvent::{self, EndElement, StartElement}, }; +#[derive(Debug, Clone)] +pub struct Property { + pub entity: String, + pub keyframe: usize, + pub delay: usize, +} + +impl Property { + pub fn new(property_elements: Vec<XmlEvent>) -> Property { + let entity = XMLElements::get_attribute_value(&property_elements, "entity") + .unwrap() + .parse() + .unwrap(); + let keyframe = XMLElements::get_attribute_value(&property_elements, "keyframe") + .unwrap() + .parse() + .unwrap(); + let delay = XMLElements::get_attribute_value(&property_elements, "delay") + .unwrap() + .parse() + .unwrap(); + + Property { + entity, + keyframe, + delay, + } + } +} + pub struct XMLElements { pub events: Vec<XmlEvent>, } @@ -33,20 +63,95 @@ impl XMLElements { .unwrap() } + pub fn get_children(&self, element: &XmlEvent, children_name: &str) -> Vec<XmlEvent> { + let start_index = self.events.iter().position(|e| e == element).unwrap(); + + let element_name = if let StartElement { name, .. } = element { + &name.local_name + } else { + "" + }; + + let end_index = self.events[start_index..] + .iter() + .position(|e| { + if let EndElement { name } = e { + element_name == name.local_name + } else { + false + } + }) + .unwrap() + + start_index; + + self.events[start_index..end_index] + .iter() + .cloned() + .filter(|e| { + if let StartElement { name, .. } = e { + name.local_name == children_name + } else { + false + } + }) + .collect() + } + + pub fn get_elements(&self, element_name: &str) -> Vec<XmlEvent> { + self.events + .clone() + .into_iter() + .filter(|e| { + if let StartElement { name, .. } = e { + name.local_name == element_name + } else { + false + } + }) + .collect() + } + pub fn get_element_attribute( &self, element_name: &str, attribute_name: &str, - ) -> Result<usize, ()> { + ) -> Result<String, ()> { let element = self.get_element(element_name); + XMLElements::get_attribute(&element, attribute_name) + } + + pub fn get_attribute(element: &XmlEvent, attribute_name: &str) -> Result<String, ()> { if let StartElement { attributes, .. } = element { Ok(attributes .iter() .find(|a| a.name.local_name == attribute_name) .unwrap() .value - .parse() - .unwrap()) + .clone()) + } else { + Err(()) + } + } + + pub fn get_attribute_value(elements: &[XmlEvent], attribute_name: &str) -> Result<String, ()> { + let element = elements + .iter() + .find(|e| { + if let StartElement { attributes, .. } = e { + attributes.iter().any(|a| a.value == attribute_name) + } else { + false + } + }) + .unwrap(); + + if let StartElement { attributes, .. } = element { + Ok(attributes + .iter() + .find(|a| a.name.local_name == "value") + .unwrap() + .value + .clone()) } else { Err(()) } |