some tileset changes and some preliminary collision code
Getty Ritter
6 years ago
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <tileset name="animaltransiro" tilewidth="24" tileheight="24" tilecount="1024" columns="32"> | |
| 3 | <image source="../tiles/spritesheet.png" width="768" height="768"/> | |
| 2 | <tileset version="1.2" tiledversion="1.2.3" name="animaltransiro" tilewidth="24" tileheight="24" tilecount="1024" columns="32"> | |
| 3 | <image source="spritesheet.png" width="768" height="768"/> | |
| 4 | 4 | <terraintypes> |
| 5 | 5 | <terrain name="Grass" tile="0"/> |
| 6 | 6 | <terrain name="Dirt" tile="70"/> |
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 |
<map version="1. |
|
| 2 | <map version="1.2" tiledversion="1.2.3" orientation="orthogonal" renderorder="right-down" width="16" height="12" tilewidth="24" tileheight="24" infinite="0" nextlayerid="5" nextobjectid="10"> | |
| 3 | 3 | <properties> |
| 4 | 4 | <property name="south" value="south"/> |
| 5 | 5 | <property name="west" value="west"/> |
| 6 | 6 | </properties> |
| 7 | 7 | <tileset firstgid="1" source="animaltransiro.tsx"/> |
| 8 |
<layer |
|
| 8 | <layer id="1" name="tiles" width="16" height="12"> | |
| 9 | 9 | <data encoding="csv"> |
| 10 | 10 | 1,1,1,1,1,1,33,1,1,1,1,1,33,1,1,1, |
| 11 | 11 | 1,1,1,1,1,43,45,1,1,1,1,1,1,43,44,45, |
| 15 | 15 | 44,79,77,1,1,1,75,76,77,1,70,73,40,75,77,1, |
| 16 | 16 | 76,76,78,45,1,33,107,108,109,1,70,71,72,107,109,1, |
| 17 | 17 | 76,76,46,109,1,1,38,39,39,39,74,71,72,33,1,1, |
| 18 | 108,108,109,1,1,38,74,41,103,42,71,41,104,1,1,1, | |
| 19 | 1,1,1,1,38,74,71,72,38,74,41,104,1,1,33,1, | |
| 20 |
1 |
|
| 18 | 108,108,109,1,1,38,74,103,42,71,71,41,104,1,1,1, | |
| 19 | 1,1,1,1,38,74,72,1,70,71,41,104,1,1,33,1, | |
| 20 | 1,1,33,1,102,103,73,39,74,41,104,1,1,1,1,1, | |
| 21 | 21 | 1,1,1,1,1,1,70,71,71,72,1,1,1,1,1,1 |
| 22 | 22 | </data> |
| 23 | 23 | </layer> |
| 24 |
<layer |
|
| 24 | <layer id="2" name="entities" width="16" height="12"> | |
| 25 | 25 | <data encoding="csv"> |
| 26 | 26 | 0,26,27,28,29,0,0,0,30,31,32,0,0,0,0,0, |
| 27 | 27 | 0,58,59,60,61,0,0,37,62,63,64,0,0,0,0,0, |
| 37 | 37 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
| 38 | 38 | </data> |
| 39 | 39 | </layer> |
| 40 |
<layer |
|
| 40 | <layer id="3" name="decoration" width="16" height="12"> | |
| 41 | 41 | <data encoding="csv"> |
| 42 | 42 | 0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0, |
| 43 | 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| 53 | 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
| 54 | 54 | </data> |
| 55 | 55 | </layer> |
| 56 |
<objectgroup |
|
| 56 | <objectgroup id="4" name="meta"> | |
| 57 | 57 | <object id="2" x="96" y="72" width="24" height="24"> |
| 58 | 58 | <properties> |
| 59 | 59 | <property name="signText" value="Bonvenon al la kafejo!"/> |
Binary diff not shown
| 1 | use specs::{Component, VecStorage, NullStorage}; | |
| 2 | ||
| 3 | /// Register all the components with the world. | |
| 4 | pub fn register(world: &mut specs::World) { | |
| 5 | world.register::<Position>(); | |
| 6 | world.register::<Velocity>(); | |
| 7 | world.register::<Sprite>(); | |
| 8 | world.register::<Background>(); | |
| 9 | world.register::<Foreground>(); | |
| 10 | world.register::<Decoration>(); | |
| 11 | world.register::<Controlled>(); | |
| 12 | world.register::<Collision>(); | |
| 13 | world.register::<Blocking>(); | |
| 14 | } | |
| 15 | ||
| 16 | /// The `Position` component represents (in world coordinates) a thing | |
| 17 | /// that has a position in the world, measured from the top-left of | |
| 18 | /// the thing. | |
| 19 | #[derive(Component, Debug)] | |
| 20 | #[storage(VecStorage)] | |
| 21 | pub struct Position { | |
| 22 | pub x: f32, | |
| 23 | pub y: f32, | |
| 24 | } | |
| 25 | ||
| 26 | impl Position { | |
| 27 | /// Convert a `Position` to a screen point | |
| 28 | pub fn to_point(&self) -> ggez::nalgebra::Point2<f32> { | |
| 29 | ggez::nalgebra::Point2::new(self.x * 3.0, self.y * 3.0) | |
| 30 | } | |
| 31 | ||
| 32 | pub fn to_grid(&self) -> (i32, i32) { | |
| 33 | ((self.x / 24.0) as i32, | |
| 34 | (self.y / 24.0) as i32, | |
| 35 | ) | |
| 36 | } | |
| 37 | ||
| 38 | pub fn moved(&self, vel: &Velocity) -> Position { | |
| 39 | Position { | |
| 40 | x: self.x + vel.dx, | |
| 41 | y: self.x + vel.dy, | |
| 42 | } | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | /// The `Velocity` componenent is present on any entity that moves | |
| 47 | /// through the world, and represents its rate of change per | |
| 48 | /// time-unit. | |
| 49 | #[derive(Component, Debug)] | |
| 50 | #[storage(VecStorage)] | |
| 51 | pub struct Velocity { | |
| 52 | pub dx: f32, | |
| 53 | pub dy: f32, | |
| 54 | } | |
| 55 | ||
| 56 | ||
| 57 | /// The `Sprite` components represents the current display location of | |
| 58 | /// a sprite in the spritesheet. | |
| 59 | #[derive(Component, Debug)] | |
| 60 | #[storage(VecStorage)] | |
| 61 | pub struct Sprite { | |
| 62 | pub u: u8, | |
| 63 | pub v: u8, | |
| 64 | } | |
| 65 | ||
| 66 | impl Sprite { | |
| 67 | /// Convert a `Sprite` into the rectangle that specifies the | |
| 68 | /// sprite location on the spritesheet | |
| 69 | pub fn to_rect(&self) -> ggez::graphics::Rect { | |
| 70 | ggez::graphics::Rect { | |
| 71 | x: (1.0 / 32.0) * self.u as f32, | |
| 72 | y: (1.0 / 32.0) * self.v as f32, | |
| 73 | w: 1.0 / 32.0, | |
| 74 | h: 1.0 / 32.0, | |
| 75 | } | |
| 76 | } | |
| 77 | } | |
| 78 | ||
| 79 | /// A drawing-phase component: represents tiles that appear in the | |
| 80 | /// background of everything. | |
| 81 | #[derive(Component, Default, Debug)] | |
| 82 | #[storage(NullStorage)] | |
| 83 | pub struct Background; | |
| 84 | ||
| 85 | /// A drawing-phase component: represents tiles which appear in the | |
| 86 | /// foreground, possibly entities. | |
| 87 | #[derive(Component, Default, Debug)] | |
| 88 | #[storage(NullStorage)] | |
| 89 | pub struct Foreground; | |
| 90 | ||
| 91 | /// A drawing-phase component: represents tiles which appear on top of | |
| 92 | /// everything else, such as the tops of trees or roofs of houses. | |
| 93 | #[derive(Component, Default, Debug)] | |
| 94 | #[storage(NullStorage)] | |
| 95 | pub struct Decoration; | |
| 96 | ||
| 97 | /// A component that represents entities which are controlled by the | |
| 98 | /// keyboard. | |
| 99 | #[derive(Component, Default, Debug)] | |
| 100 | #[storage(NullStorage)] | |
| 101 | pub struct Controlled; | |
| 102 | ||
| 103 | /// A component that represents entities which can collide with other | |
| 104 | /// things. | |
| 105 | #[derive(Component, Debug)] | |
| 106 | #[storage(VecStorage)] | |
| 107 | pub struct Collision { | |
| 108 | pub has_collision: bool | |
| 109 | } | |
| 110 | ||
| 111 | #[derive(Component, Default, Debug)] | |
| 112 | #[storage(NullStorage)] | |
| 113 | pub struct Blocking; |
| 1 | use specs::{Component, VecStorage, NullStorage}; | |
| 2 | ||
| 3 | /// Register all the components with the world. | |
| 4 | pub fn register(world: &mut specs::World) { | |
| 5 | world.register::<Position>(); | |
| 6 | world.register::<Velocity>(); | |
| 7 | world.register::<Sprite>(); | |
| 8 | world.register::<Background>(); | |
| 9 | world.register::<Foreground>(); | |
| 10 | world.register::<Decoration>(); | |
| 11 | world.register::<Controlled>(); | |
| 12 | } | |
| 13 | ||
| 14 | /// The `Position` component represents (in world coordinates) a thing | |
| 15 | /// that has a position in the world, measured from the top-left of | |
| 16 | /// the thing. | |
| 17 | #[derive(Component, Debug)] | |
| 18 | #[storage(VecStorage)] | |
| 19 | pub struct Position { | |
| 20 | pub x: f32, | |
| 21 | pub y: f32, | |
| 22 | } | |
| 23 | ||
| 24 | impl Position { | |
| 25 | /// Convert a `Position` to a screen point | |
| 26 | pub fn to_point(&self) -> ggez::nalgebra::Point2<f32> { | |
| 27 | ggez::nalgebra::Point2::new(self.x * 3.0, self.y * 3.0) | |
| 28 | } | |
| 29 | } | |
| 30 | ||
| 31 | /// The `Velocity` componenent is present on any entity that moves | |
| 32 | /// through the world, and represents its rate of change per | |
| 33 | /// time-unit. | |
| 34 | #[derive(Component, Debug)] | |
| 35 | #[storage(VecStorage)] | |
| 36 | pub struct Velocity { | |
| 37 | pub dx: f32, | |
| 38 | pub dy: f32, | |
| 39 | } | |
| 40 | ||
| 41 | ||
| 42 | /// The `Sprite` components represents the current display location of | |
| 43 | /// a sprite in the spritesheet. | |
| 44 | #[derive(Component, Debug)] | |
| 45 | #[storage(VecStorage)] | |
| 46 | pub struct Sprite { | |
| 47 | pub u: u8, | |
| 48 | pub v: u8, | |
| 49 | } | |
| 50 | ||
| 51 | impl Sprite { | |
| 52 | /// Convert a `Sprite` into the rectangle that specifies the | |
| 53 | /// sprite location on the spritesheet | |
| 54 | pub fn to_rect(&self) -> ggez::graphics::Rect { | |
| 55 | ggez::graphics::Rect { | |
| 56 | x: (1.0 / 32.0) * self.u as f32, | |
| 57 | y: (1.0 / 32.0) * self.v as f32, | |
| 58 | w: 1.0 / 32.0, | |
| 59 | h: 1.0 / 32.0, | |
| 60 | } | |
| 61 | } | |
| 62 | } | |
| 63 | ||
| 64 | /// A drawing-phase component: represents tiles that appear in the | |
| 65 | /// background of everything. | |
| 66 | #[derive(Component, Default, Debug)] | |
| 67 | #[storage(NullStorage)] | |
| 68 | pub struct Background; | |
| 69 | ||
| 70 | /// A drawing-phase component: represents tiles which appear in the | |
| 71 | /// foreground, possibly entities. | |
| 72 | #[derive(Component, Default, Debug)] | |
| 73 | #[storage(NullStorage)] | |
| 74 | pub struct Foreground; | |
| 75 | ||
| 76 | /// A drawing-phase component: represents tiles which appear on top of | |
| 77 | /// everything else, such as the tops of trees or roofs of houses. | |
| 78 | #[derive(Component, Default, Debug)] | |
| 79 | #[storage(NullStorage)] | |
| 80 | pub struct Decoration; | |
| 81 | ||
| 82 | /// A component that represents entities which are controlled by the | |
| 83 | /// keyboard. | |
| 84 | #[derive(Component, Default, Debug)] | |
| 85 | #[storage(NullStorage)] | |
| 86 | pub struct Controlled; | |
| 87 | ||
| 88 | /// A component that represents entities which can collide with other | |
| 89 | /// things. | |
| 90 | #[derive(Component, Debug)] | |
| 91 | #[storage(VecStorage)] | |
| 92 | pub struct Collision { | |
| 93 | pub has_collision: bool | |
| 94 | } |
| 10 | 10 | use sdl2::keyboard as sdl; |
| 11 | 11 | |
| 12 | 12 | pub mod consts; |
| 13 |
pub mod com |
|
| 13 | pub mod com; | |
| 14 | 14 | pub mod game; |
| 15 | 15 | pub mod res; |
| 16 | 16 | pub mod sys; |
| 18 | 18 | use game::MyGame; |
| 19 | 19 | |
| 20 | 20 | impl EventHandler for MyGame { |
| 21 | ||
| 22 | 21 | fn update(&mut self, _ctx: &mut Context) -> GameResult<()> { |
| 23 | 22 | sys::input::systems(self); |
| 24 | 23 | sys::physics::systems(self); |
| 55 | 54 | |
| 56 | 55 | fn main() -> Result<(), ggez::error::GameError> { |
| 57 | 56 | let mut world = specs::World::new(); |
| 58 |
com |
|
| 57 | com::register(&mut world); | |
| 59 | 58 | |
| 60 | 59 | res::world_from_file(&mut world, "assets/main.tmx"); |
| 61 | 60 | |
| 1 | 1 | use crate::consts; |
| 2 |
use crate::com |
|
| 2 | use crate::com::*; | |
| 3 | 3 | |
| 4 | 4 | use specs::world::Builder; |
| 5 | 5 | use std::path::Path; |
| 47 | 47 | pub fn world_from_file<P: AsRef<Path>>(w: &mut specs::World, path: P) { |
| 48 | 48 | let tiled::Map { |
| 49 | 49 | layers, |
| 50 | tilesets, | |
| 50 | 51 | .. |
| 51 | 52 | } = tiled::parse_file(path.as_ref()).unwrap(); |
| 52 | 53 | |
| 66 | 67 | DrawLayer::Foreground => e.with(Foreground), |
| 67 | 68 | DrawLayer::Decoration => e.with(Decoration), |
| 68 | 69 | }; |
| 70 | ||
| 71 | let e = if tilesets[0].tiles[n as usize].properties["pass"] == | |
| 72 | tiled::PropertyValue::BoolValue(false) { | |
| 73 | e.with(Blocking) | |
| 74 | } else { | |
| 75 | e | |
| 76 | }; | |
| 69 | 77 | e.build(); |
| 70 | 78 | } |
| 71 | 79 | } |
| 82 | 90 | .with(Velocity { dx: 0.0, dy: 0.0 }) |
| 83 | 91 | .with(Foreground) |
| 84 | 92 | .with(Controlled) |
| 93 | .with(Collision { has_collision: false }) | |
| 85 | 94 | .build(); |
| 86 | 95 | } |
| 1 | 1 | use crate::consts; |
| 2 |
use crate::com |
|
| 2 | use crate::com::{self,Position,Sprite}; | |
| 3 | 3 | use crate::game::MyGame; |
| 4 | 4 | |
| 5 | 5 | use ggez::{ |
| 53 | 53 | |
| 54 | 54 | Draw { |
| 55 | 55 | ctx, |
| 56 |
_phase: com |
|
| 56 | _phase: com::Background, | |
| 57 | 57 | }.run_now(&game.world.res); |
| 58 | 58 | Draw { |
| 59 | 59 | ctx, |
| 60 |
_phase: com |
|
| 60 | _phase: com::Foreground, | |
| 61 | 61 | }.run_now(&game.world.res); |
| 62 | 62 | Draw { |
| 63 | 63 | ctx, |
| 64 |
_phase: com |
|
| 64 | _phase: com::Decoration, | |
| 65 | 65 | }.run_now(&game.world.res); |
| 66 | 66 | |
| 67 | 67 | graphics::present(ctx); |
| 1 |
use crate::com |
|
| 1 | use crate::com::{Controlled, Velocity}; | |
| 2 | 2 | use crate::game::MyGame; |
| 3 | 3 | use crate::res::KeySet; |
| 4 | 4 |
| 1 |
use crate::com |
|
| 1 | use crate::com::{Blocking, Collision, Velocity, Position}; | |
| 2 | 2 | use crate::game::MyGame; |
| 3 | 3 | |
| 4 |
use specs:: |
|
| 4 | use specs::{Join,RunNow}; | |
| 5 | ||
| 6 | struct Intersection; | |
| 7 | ||
| 8 | impl<'a> specs::System<'a> for Intersection { | |
| 9 | type SystemData = ( | |
| 10 | specs::Entities<'a>, | |
| 11 | specs::ReadStorage<'a, Position>, | |
| 12 | specs::ReadStorage<'a, Velocity>, | |
| 13 | specs::ReadStorage<'a, Blocking>, | |
| 14 | specs::WriteStorage<'a, Collision>, | |
| 15 | ); | |
| 16 | ||
| 17 | fn run(&mut self, (entity, position, velocity, blocking, mut collision): Self::SystemData) { | |
| 18 | let mut spacemap = std::collections::HashMap::new(); | |
| 19 | for (e, pos, _) in (&entity, &position, &blocking).join() { | |
| 20 | spacemap.insert(pos.to_grid(), e); | |
| 21 | } | |
| 22 | ||
| 23 | for (pos, vel, col) in (&position, &velocity, &mut collision).join() { | |
| 24 | if let Some(_) = spacemap.get(&pos.moved(vel).to_grid()) { | |
| 25 | col.has_collision = true; | |
| 26 | } | |
| 27 | } | |
| 28 | } | |
| 29 | } | |
| 5 | 30 | |
| 6 | 31 | struct Physics; |
| 7 | 32 | |
| 8 | 33 | impl <'a> specs::System<'a> for Physics { |
| 9 | 34 | type SystemData = ( |
| 10 | 35 | specs::ReadStorage<'a, Velocity>, |
| 36 | specs::ReadStorage<'a, Collision>, | |
| 11 | 37 | specs::WriteStorage<'a, Position>, |
| 12 | 38 | ); |
| 13 | 39 | |
| 14 | fn run(&mut self, (velocity, mut position): Self::SystemData) { | |
| 15 | use specs::Join; | |
| 16 | ||
| 17 | for (vel, pos) in (&velocity, &mut position).join() { | |
| 18 | pos.x += vel.dx; | |
| 19 | pos.y += vel.dy; | |
| 40 | fn run(&mut self, (velocity, collision, mut position): Self::SystemData) { | |
| 41 | for (vel, col, pos) in (&velocity, &collision, &mut position).join() { | |
| 42 | if !col.has_collision { | |
| 43 | pos.x += vel.dx; | |
| 44 | pos.y += vel.dy; | |
| 45 | } | |
| 20 | 46 | } |
| 21 | 47 | } |
| 22 | 48 | } |
| 23 | 49 | |
| 50 | struct ResetCollision; | |
| 51 | ||
| 52 | impl<'a> specs::System<'a> for ResetCollision { | |
| 53 | type SystemData = | |
| 54 | specs::WriteStorage<'a, Collision>; | |
| 55 | ||
| 56 | fn run(&mut self, mut collision: Self::SystemData) { | |
| 57 | for mut e in (&mut collision).join() { | |
| 58 | e.has_collision = false; | |
| 59 | } | |
| 60 | } | |
| 61 | } | |
| 24 | 62 | |
| 25 | 63 | pub fn systems(game: &mut MyGame) { |
| 64 | Intersection.run_now(&game.world.res); | |
| 26 | 65 | Physics.run_now(&game.world.res); |
| 66 | ResetCollision.run_now(&game.world.res); | |
| 27 | 67 | } |