Bastien Sevajol 3 years ago
parent
commit
9a56f6d927
4 changed files with 208 additions and 50 deletions
  1. 7 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. BIN
      resources/test.png
  4. 200 50
      src/main.rs

+ 7 - 0
Cargo.lock View File

689
 version = "0.1.0"
689
 version = "0.1.0"
690
 dependencies = [
690
 dependencies = [
691
  "ggez",
691
  "ggez",
692
+ "glam",
692
 ]
693
 ]
693
 
694
 
694
 [[package]]
695
 [[package]]
763
 ]
764
 ]
764
 
765
 
765
 [[package]]
766
 [[package]]
767
+name = "glam"
768
+version = "0.14.0"
769
+source = "registry+https://github.com/rust-lang/crates.io-index"
770
+checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
771
+
772
+[[package]]
766
 name = "gleam"
773
 name = "gleam"
767
 version = "0.6.19"
774
 version = "0.6.19"
768
 source = "registry+https://github.com/rust-lang/crates.io-index"
775
 source = "registry+https://github.com/rust-lang/crates.io-index"

+ 1 - 0
Cargo.toml View File

8
 
8
 
9
 [dependencies]
9
 [dependencies]
10
 ggez = "0.5.1"
10
 ggez = "0.5.1"
11
+glam = "0.14.0"

BIN
resources/test.png View File


+ 200 - 50
src/main.rs View File

4
 use ggez::nalgebra as na;
4
 use ggez::nalgebra as na;
5
 use ggez::timer::check_update_time;
5
 use ggez::timer::check_update_time;
6
 use ggez::{Context, GameResult};
6
 use ggez::{Context, GameResult};
7
+use glam::Vec2;
7
 use std::env;
8
 use std::env;
8
 use std::path;
9
 use std::path;
9
 
10
 
10
-const TARGET_ANIMATION_FPS: u32 = 10;
11
+type Vector2 = Vec2;
12
+
13
+const TARGET_FPS: u32 = 60; // execute update code 60x per seconds
14
+const META_EACH: u32 = 20; // execute meta code each 20 frames
15
+const PHYSICS_EACH: u32 = 10; // execute physics code each 10 frames
16
+const ANIMATE_EACH: u32 = 60; // execute animate code each 30 frames
17
+const SPRITE_EACH: u32 = 10; // change sprite animation tile 30 frames
18
+const MAX_FRAME_I: u32 = 4294967295; // max of frame_i used to calculate ticks
19
+
20
+const SPRITE_SHEET_WIDTH: f32 = 168.0;
21
+const SPRITE_SHEET_HEIGHT: f32 = 72.0;
22
+
23
+fn vec_from_angle(angle: f32) -> Vector2 {
24
+    let vx = angle.sin();
25
+    let vy = angle.cos();
26
+    Vector2::new(vx, vy)
27
+}
11
 
28
 
12
 struct SpriteInfo {
29
 struct SpriteInfo {
13
     relative_start_y: f32,
30
     relative_start_y: f32,
14
     relative_tile_width: f32,
31
     relative_tile_width: f32,
15
     relative_tile_height: f32,
32
     relative_tile_height: f32,
16
     tile_count: u16,
33
     tile_count: u16,
17
-    current_frame: u16,
18
 }
34
 }
19
 
35
 
20
 impl SpriteInfo {
36
 impl SpriteInfo {
29
             relative_tile_width,
45
             relative_tile_width,
30
             relative_tile_height,
46
             relative_tile_height,
31
             tile_count,
47
             tile_count,
32
-            current_frame: 0,
33
         }
48
         }
34
     }
49
     }
35
 
50
 
36
-    pub fn from_type(
37
-        type_: &SpriteType,
38
-        sprite_sheet_width: f32,
39
-        sprite_sheet_height: f32,
40
-    ) -> Self {
51
+    // TODO: ask on rust community if this is performant, or how to make it static
52
+    pub fn from_type(type_: &SpriteType) -> Self {
41
         let (start_y, tile_width, tile_height, tile_count) = match type_ {
53
         let (start_y, tile_width, tile_height, tile_count) = match type_ {
42
             SpriteType::WalkingSoldier => (0.0, 24.0, 24.0, 7),
54
             SpriteType::WalkingSoldier => (0.0, 24.0, 24.0, 7),
43
             SpriteType::JumpingSoldier => (24.0, 24.0, 24.0, 2),
55
             SpriteType::JumpingSoldier => (24.0, 24.0, 24.0, 2),
56
+            SpriteType::StandingSoldier => (48.0, 24.0, 24.0, 1),
44
         };
57
         };
45
 
58
 
46
         Self {
59
         Self {
47
-            relative_start_y: start_y / sprite_sheet_height,
48
-            relative_tile_width: tile_width / sprite_sheet_width,
49
-            relative_tile_height: tile_height / sprite_sheet_height,
60
+            relative_start_y: start_y / SPRITE_SHEET_HEIGHT,
61
+            relative_tile_width: tile_width / SPRITE_SHEET_WIDTH,
62
+            relative_tile_height: tile_height / SPRITE_SHEET_HEIGHT,
50
             tile_count,
63
             tile_count,
51
-            current_frame: 0,
52
         }
64
         }
53
     }
65
     }
66
+
67
+    pub fn as_draw_param(&self, current_frame: f32) -> graphics::DrawParam {
68
+        graphics::DrawParam::new().src(graphics::Rect::new(
69
+            current_frame as f32 * self.relative_tile_width,
70
+            self.relative_start_y,
71
+            self.relative_tile_width,
72
+            self.relative_tile_height,
73
+        ))
74
+    }
54
 }
75
 }
55
 
76
 
56
 enum SpriteType {
77
 enum SpriteType {
57
     WalkingSoldier,
78
     WalkingSoldier,
58
     JumpingSoldier,
79
     JumpingSoldier,
80
+    StandingSoldier,
81
+}
82
+
83
+enum ItemBehavior {
84
+    Standing(u32), // since
85
+    Jumping,
86
+    Running(Vector2),
59
 }
87
 }
60
 
88
 
61
-fn sprite_batch_part_from_sprite_info(sprite_info: &SpriteInfo) -> graphics::DrawParam {
62
-    let src = graphics::Rect::new(
63
-        sprite_info.current_frame as f32 * sprite_info.relative_tile_width,
64
-        sprite_info.relative_start_y,
65
-        sprite_info.relative_tile_width,
66
-        sprite_info.relative_tile_height,
67
-    );
68
-    graphics::DrawParam::new().src(src)
89
+struct ItemState {
90
+    current_behavior: ItemBehavior,
91
+}
92
+
93
+impl ItemState {
94
+    pub fn new(current_behavior: ItemBehavior) -> Self {
95
+        Self { current_behavior }
96
+    }
97
+
98
+    pub fn sprite_type(&self) -> SpriteType {
99
+        // Here some logical about state and current behavior to determine sprite type
100
+        match self.current_behavior {
101
+            ItemBehavior::Jumping => SpriteType::JumpingSoldier,
102
+            ItemBehavior::Running(_) => SpriteType::WalkingSoldier,
103
+            ItemBehavior::Standing(_) => SpriteType::StandingSoldier,
104
+        }
105
+    }
69
 }
106
 }
70
 
107
 
71
 struct SceneItem {
108
 struct SceneItem {
72
-    sprite_info: SpriteInfo,
73
     position: na::Point2<f32>,
109
     position: na::Point2<f32>,
110
+    state: ItemState,
111
+    meta_events: Vec<MetaEvent>,
112
+    current_frame: u16,
74
 }
113
 }
75
 
114
 
76
 impl SceneItem {
115
 impl SceneItem {
77
-    pub fn new(
78
-        sprite_type: SpriteType,
79
-        position: na::Point2<f32>,
80
-        sprite_sheet_width: f32,
81
-        sprite_sheet_height: f32,
82
-    ) -> Self {
116
+    pub fn new(position: na::Point2<f32>, state: ItemState) -> Self {
117
+        let sprite_type = state.sprite_type();
83
         Self {
118
         Self {
84
-            sprite_info: SpriteInfo::from_type(
85
-                &sprite_type,
86
-                sprite_sheet_width,
87
-                sprite_sheet_height,
88
-            ),
89
             position,
119
             position,
120
+            state,
121
+            meta_events: vec![],
122
+            current_frame: 0,
90
         }
123
         }
91
     }
124
     }
92
 
125
 
93
-    pub fn tick_frame(&mut self) {
94
-        self.sprite_info.current_frame += 1;
95
-        if self.sprite_info.current_frame >= self.sprite_info.tile_count {
96
-            self.sprite_info.current_frame = 0;
126
+    pub fn sprite_info(&self) -> SpriteInfo {
127
+        SpriteInfo::from_type(&self.state.sprite_type())
128
+    }
129
+
130
+    pub fn tick_sprite(&mut self) {
131
+        self.current_frame += 1;
132
+        // TODO: good way to have sprite info ? performant ?
133
+        if self.current_frame >= self.sprite_info().tile_count {
134
+            self.current_frame = 0;
97
         }
135
         }
98
     }
136
     }
99
 }
137
 }
100
 
138
 
101
-enum Message {}
139
+enum PhysicEvent {
140
+    Explosion,
141
+}
142
+
143
+enum MetaEvent {
144
+    FearAboutExplosion,
145
+}
102
 
146
 
103
 struct MainState {
147
 struct MainState {
148
+    frame_i: u32,
104
     scene_items_sprite_batch: graphics::spritebatch::SpriteBatch,
149
     scene_items_sprite_batch: graphics::spritebatch::SpriteBatch,
105
     scene_items: Vec<SceneItem>,
150
     scene_items: Vec<SceneItem>,
151
+    physics_events: Vec<PhysicEvent>,
106
 }
152
 }
107
 
153
 
108
 impl MainState {
154
 impl MainState {
113
         let mut scene_items = vec![];
159
         let mut scene_items = vec![];
114
         for x in 0..1 {
160
         for x in 0..1 {
115
             for y in 0..4 {
161
             for y in 0..4 {
116
-                let sprite_type = if y % 2 == 0 {
117
-                    SpriteType::WalkingSoldier
162
+                let current_behavior = if y % 2 == 0 {
163
+                    ItemBehavior::Running(vec_from_angle(90.0))
118
                 } else {
164
                 } else {
119
-                    SpriteType::JumpingSoldier
165
+                    ItemBehavior::Jumping
120
                 };
166
                 };
121
 
167
 
122
                 scene_items.push(SceneItem::new(
168
                 scene_items.push(SceneItem::new(
123
-                    sprite_type,
124
                     na::Point2::new(x as f32 * 24.0, y as f32 * 24.0),
169
                     na::Point2::new(x as f32 * 24.0, y as f32 * 24.0),
125
-                    168.0,
126
-                    48.0,
170
+                    ItemState::new(current_behavior),
127
                 ));
171
                 ));
128
             }
172
             }
129
         }
173
         }
130
 
174
 
131
         let s = MainState {
175
         let s = MainState {
176
+            frame_i: 0,
132
             scene_items_sprite_batch: batch,
177
             scene_items_sprite_batch: batch,
133
             scene_items,
178
             scene_items,
179
+            physics_events: vec![],
134
         };
180
         };
135
         Ok(s)
181
         Ok(s)
136
     }
182
     }
183
+
184
+    // TODO: manage errors
185
+    fn physics(&mut self) {
186
+        // Scene items movements
187
+        for scene_item in self.scene_items.iter_mut() {
188
+            match scene_item.state.current_behavior {
189
+                ItemBehavior::Running(vector) => {
190
+                    // TODO ici il faut calculer le déplacement réél (en fonction des ticks, etc ...)
191
+                    scene_item.position.x += 1.0;
192
+                }
193
+                _ => {}
194
+            }
195
+        }
196
+
197
+        // (FAKE) Drop a bomb to motivate stop move
198
+        if self.frame_i % 300 == 0 && self.frame_i != 0 {
199
+            self.physics_events.push(PhysicEvent::Explosion);
200
+        }
201
+    }
202
+
203
+    fn metas(&mut self) {
204
+        for physic_event in &self.physics_events {
205
+            match physic_event {
206
+                PhysicEvent::Explosion => {
207
+                    for scene_item in self.scene_items.iter_mut() {
208
+                        scene_item.meta_events.push(MetaEvent::FearAboutExplosion);
209
+                    }
210
+                }
211
+            }
212
+        }
213
+    }
214
+
215
+    fn animate(&mut self) {
216
+        // TODO: ici il faut reflechir a comment organiser les comportements
217
+
218
+        for scene_item in self.scene_items.iter_mut() {
219
+            for meta_event in &scene_item.meta_events {
220
+                match meta_event {
221
+                    MetaEvent::FearAboutExplosion => {
222
+                        scene_item.state = ItemState::new(ItemBehavior::Standing(self.frame_i));
223
+                    }
224
+                }
225
+            }
226
+
227
+            match scene_item.state.current_behavior {
228
+                ItemBehavior::Jumping => {
229
+                    scene_item.state = ItemState::new(ItemBehavior::Running(vec_from_angle(90.0)));
230
+                }
231
+                ItemBehavior::Running(_) => {
232
+                    scene_item.state = ItemState::new(ItemBehavior::Jumping);
233
+                }
234
+                ItemBehavior::Standing(since) => {
235
+                    if self.frame_i - since >= 120 {
236
+                        scene_item.state =
237
+                            ItemState::new(ItemBehavior::Running(vec_from_angle(90.0)));
238
+                    }
239
+                }
240
+            }
241
+
242
+            scene_item.meta_events.drain(..);
243
+        }
244
+    }
245
+
246
+    fn tick_sprites(&mut self) {
247
+        for scene_item in self.scene_items.iter_mut() {
248
+            scene_item.tick_sprite();
249
+        }
250
+    }
137
 }
251
 }
138
 
252
 
139
 impl event::EventHandler for MainState {
253
 impl event::EventHandler for MainState {
140
     fn update(&mut self, ctx: &mut Context) -> GameResult {
254
     fn update(&mut self, ctx: &mut Context) -> GameResult {
141
-        while check_update_time(ctx, TARGET_ANIMATION_FPS) {
142
-            for scene_item in self.scene_items.iter_mut() {
143
-                scene_item.tick_frame();
255
+        while check_update_time(ctx, TARGET_FPS) {
256
+            // FIXME: gérer ici la maj des physics, animate, meta etc
257
+            // meta: calculer par ex qui voit qui (soldat voit un ennemi: ajouter l'event a vu
258
+            // ennemi, dans animate il se mettra a tirer)
259
+            let tick_sprite = self.frame_i % SPRITE_EACH == 0;
260
+            let tick_animate = self.frame_i % ANIMATE_EACH == 0;
261
+            let tick_physics = self.frame_i % PHYSICS_EACH == 0;
262
+            let tick_meta = self.frame_i % META_EACH == 0;
263
+
264
+            // Apply moves, explosions, etc
265
+            if tick_physics {
266
+                self.physics();
267
+            }
268
+
269
+            // Generate meta events according to physics events and current physic state
270
+            if tick_meta {
271
+                self.metas();
144
             }
272
             }
273
+
274
+            // Animate scene items according to meta events
275
+            if tick_animate {
276
+                self.animate();
277
+            };
278
+
279
+            // Change scene items tiles
280
+            if tick_sprite {
281
+                self.tick_sprites();
282
+            }
283
+
284
+            // Increment frame counter
285
+            self.frame_i += 1;
286
+            if self.frame_i >= MAX_FRAME_I {
287
+                self.frame_i = 0;
288
+            }
289
+
290
+            // Empty physics event
291
+            self.physics_events.drain(..);
145
         }
292
         }
146
 
293
 
147
         Ok(())
294
         Ok(())
151
         graphics::clear(ctx, graphics::BLACK);
298
         graphics::clear(ctx, graphics::BLACK);
152
 
299
 
153
         for scene_item in self.scene_items.iter() {
300
         for scene_item in self.scene_items.iter() {
154
-            let sprite_batch_part = sprite_batch_part_from_sprite_info(&scene_item.sprite_info)
155
-                .dest(scene_item.position.clone());
156
-            self.scene_items_sprite_batch.add(sprite_batch_part);
301
+            self.scene_items_sprite_batch.add(
302
+                scene_item
303
+                    .sprite_info()
304
+                    .as_draw_param(scene_item.current_frame as f32)
305
+                    .dest(scene_item.position.clone()),
306
+            );
157
         }
307
         }
158
         graphics::draw(
308
         graphics::draw(
159
             ctx,
309
             ctx,
168
         Ok(())
318
         Ok(())
169
     }
319
     }
170
 }
320
 }
171
-// TODO: spite i par objet, fabrication des sprite_info qu'une fois; channel pour modifs des objets ds update
321
+
172
 pub fn main() -> GameResult {
322
 pub fn main() -> GameResult {
173
     let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
323
     let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
174
         let mut path = path::PathBuf::from(manifest_dir);
324
         let mut path = path::PathBuf::from(manifest_dir);