Bastien Sevajol 3 年之前
父節點
當前提交
9a56f6d927
共有 4 個文件被更改,包括 208 次插入50 次删除
  1. 7 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 二進制
      resources/test.png
  4. 200 50
      src/main.rs

+ 7 - 0
Cargo.lock 查看文件

@@ -689,6 +689,7 @@ name = "ggez_tests"
689 689
 version = "0.1.0"
690 690
 dependencies = [
691 691
  "ggez",
692
+ "glam",
692 693
 ]
693 694
 
694 695
 [[package]]
@@ -763,6 +764,12 @@ dependencies = [
763 764
 ]
764 765
 
765 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 773
 name = "gleam"
767 774
 version = "0.6.19"
768 775
 source = "registry+https://github.com/rust-lang/crates.io-index"

+ 1 - 0
Cargo.toml 查看文件

@@ -8,3 +8,4 @@ edition = "2018"
8 8
 
9 9
 [dependencies]
10 10
 ggez = "0.5.1"
11
+glam = "0.14.0"

二進制
resources/test.png 查看文件


+ 200 - 50
src/main.rs 查看文件

@@ -4,17 +4,33 @@ use ggez::graphics;
4 4
 use ggez::nalgebra as na;
5 5
 use ggez::timer::check_update_time;
6 6
 use ggez::{Context, GameResult};
7
+use glam::Vec2;
7 8
 use std::env;
8 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 29
 struct SpriteInfo {
13 30
     relative_start_y: f32,
14 31
     relative_tile_width: f32,
15 32
     relative_tile_height: f32,
16 33
     tile_count: u16,
17
-    current_frame: u16,
18 34
 }
19 35
 
20 36
 impl SpriteInfo {
@@ -29,80 +45,110 @@ impl SpriteInfo {
29 45
             relative_tile_width,
30 46
             relative_tile_height,
31 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 53
         let (start_y, tile_width, tile_height, tile_count) = match type_ {
42 54
             SpriteType::WalkingSoldier => (0.0, 24.0, 24.0, 7),
43 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 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 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 77
 enum SpriteType {
57 78
     WalkingSoldier,
58 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 108
 struct SceneItem {
72
-    sprite_info: SpriteInfo,
73 109
     position: na::Point2<f32>,
110
+    state: ItemState,
111
+    meta_events: Vec<MetaEvent>,
112
+    current_frame: u16,
74 113
 }
75 114
 
76 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 118
         Self {
84
-            sprite_info: SpriteInfo::from_type(
85
-                &sprite_type,
86
-                sprite_sheet_width,
87
-                sprite_sheet_height,
88
-            ),
89 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 147
 struct MainState {
148
+    frame_i: u32,
104 149
     scene_items_sprite_batch: graphics::spritebatch::SpriteBatch,
105 150
     scene_items: Vec<SceneItem>,
151
+    physics_events: Vec<PhysicEvent>,
106 152
 }
107 153
 
108 154
 impl MainState {
@@ -113,35 +159,136 @@ impl MainState {
113 159
         let mut scene_items = vec![];
114 160
         for x in 0..1 {
115 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 164
                 } else {
119
-                    SpriteType::JumpingSoldier
165
+                    ItemBehavior::Jumping
120 166
                 };
121 167
 
122 168
                 scene_items.push(SceneItem::new(
123
-                    sprite_type,
124 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 175
         let s = MainState {
176
+            frame_i: 0,
132 177
             scene_items_sprite_batch: batch,
133 178
             scene_items,
179
+            physics_events: vec![],
134 180
         };
135 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 253
 impl event::EventHandler for MainState {
140 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 294
         Ok(())
@@ -151,9 +298,12 @@ impl event::EventHandler for MainState {
151 298
         graphics::clear(ctx, graphics::BLACK);
152 299
 
153 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 308
         graphics::draw(
159 309
             ctx,
@@ -168,7 +318,7 @@ impl event::EventHandler for MainState {
168 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 322
 pub fn main() -> GameResult {
173 323
     let resource_dir = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
174 324
         let mut path = path::PathBuf::from(manifest_dir);