Browse Source

select by click and area

Bastien Sevajol 3 years ago
parent
commit
45412649f5
1 changed files with 241 additions and 26 deletions
  1. 241 26
      src/main.rs

+ 241 - 26
src/main.rs View File

1
 use ggez;
1
 use ggez;
2
-use ggez::event::KeyCode;
2
+use ggez::event::{KeyCode, MouseButton};
3
 use ggez::graphics;
3
 use ggez::graphics;
4
-use ggez::graphics::{DrawMode, MeshBuilder};
4
+use ggez::graphics::{Color, DrawMode, FillOptions, MeshBuilder, StrokeOptions};
5
 use ggez::timer::check_update_time;
5
 use ggez::timer::check_update_time;
6
 use ggez::{event, input};
6
 use ggez::{event, input};
7
 use ggez::{Context, GameResult};
7
 use ggez::{Context, GameResult};
8
 use glam::Vec2;
8
 use glam::Vec2;
9
+use std::cmp;
10
+use std::collections::HashMap;
9
 use std::env;
11
 use std::env;
10
 use std::path;
12
 use std::path;
11
 
13
 
18
 const ANIMATE_EACH: u32 = 60; // execute animate code each 30 frames
20
 const ANIMATE_EACH: u32 = 60; // execute animate code each 30 frames
19
 const SPRITE_EACH: u32 = 10; // change sprite animation tile 30 frames
21
 const SPRITE_EACH: u32 = 10; // change sprite animation tile 30 frames
20
 const MAX_FRAME_I: u32 = 4294967295; // max of frame_i used to calculate ticks
22
 const MAX_FRAME_I: u32 = 4294967295; // max of frame_i used to calculate ticks
21
-const DISPLAY_OFFSET_BY: f32 = 3.0;
22
-const DISPLAY_OFFSET_BY_SPEED: f32 = 10.0;
23
-const SPRITE_SHEET_WIDTH: f32 = 800.0;
24
-const SPRITE_SHEET_HEIGHT: f32 = 600.0;
23
+const DISPLAY_OFFSET_BY: f32 = 3.0; // pixel offset by tick when player move screen display
24
+const DISPLAY_OFFSET_BY_SPEED: f32 = 10.0; // pixel offset by tick when player move screen display with speed
25
+const SPRITE_SHEET_WIDTH: f32 = 800.0; // Width of sprite sheet
26
+const SPRITE_SHEET_HEIGHT: f32 = 600.0; // Height of sprite sheet
27
+const GRID_TILE_WIDTH: f32 = 5.0; // Width of one grid tile
28
+const GRID_TILE_HEIGHT: f32 = 5.0; // Height of one grid tile
29
+const DEFAULT_SELECTED_SQUARE_SIDE: f32 = 14.0;
30
+const DEFAULT_SELECTED_SQUARE_SIDE_HALF: f32 = DEFAULT_SELECTED_SQUARE_SIDE / 2.0;
31
+
32
+#[derive(Eq, PartialEq, Hash)]
33
+pub struct GridPosition {
34
+    x: i32,
35
+    y: i32,
36
+}
37
+
38
+impl GridPosition {
39
+    pub fn new(x: i32, y: i32) -> Self {
40
+        Self { x, y }
41
+    }
42
+}
25
 
43
 
26
 fn vec_from_angle(angle: f32) -> Vector2 {
44
 fn vec_from_angle(angle: f32) -> Vector2 {
27
     let vx = angle.sin();
45
     let vx = angle.sin();
29
     Vector2::new(vx, vy)
47
     Vector2::new(vx, vy)
30
 }
48
 }
31
 
49
 
50
+fn grid_position_from_position(position: &Point2) -> GridPosition {
51
+    GridPosition::new(
52
+        (position.x / GRID_TILE_WIDTH) as i32,
53
+        (position.y / GRID_TILE_HEIGHT) as i32,
54
+    )
55
+}
56
+
32
 struct SpriteInfo {
57
 struct SpriteInfo {
33
     relative_start_y: f32,
58
     relative_start_y: f32,
34
     relative_tile_width: f32,
59
     relative_tile_width: f32,
36
     tile_count: u16,
61
     tile_count: u16,
37
     tile_width: f32,
62
     tile_width: f32,
38
     tile_height: f32,
63
     tile_height: f32,
64
+    _half_tile_width: f32,
65
+    _half_tile_height: f32,
39
 }
66
 }
40
 
67
 
41
 impl SpriteInfo {
68
 impl SpriteInfo {
54
             tile_count,
81
             tile_count,
55
             tile_width,
82
             tile_width,
56
             tile_height,
83
             tile_height,
84
+            _half_tile_width: tile_width / 2.0,
85
+            _half_tile_height: tile_height / 2.0,
57
         }
86
         }
58
     }
87
     }
59
 }
88
 }
74
     current_behavior: ItemBehavior,
103
     current_behavior: ItemBehavior,
75
 }
104
 }
76
 
105
 
106
+enum SceneItemType {
107
+    Soldier,
108
+}
109
+
77
 impl ItemState {
110
 impl ItemState {
78
     pub fn new(current_behavior: ItemBehavior) -> Self {
111
     pub fn new(current_behavior: ItemBehavior) -> Self {
79
         Self { current_behavior }
112
         Self { current_behavior }
80
     }
113
     }
81
-
82
-    pub fn sprite_type(&self) -> SpriteType {
83
-        // Here some logical about state and current behavior to determine sprite type
84
-        match self.current_behavior {
85
-            ItemBehavior::Crawling => SpriteType::CrawlingSoldier,
86
-            ItemBehavior::Walking(_) => SpriteType::WalkingSoldier,
87
-            ItemBehavior::Standing(_) => SpriteType::StandingSoldier,
88
-        }
89
-    }
90
 }
114
 }
91
 
115
 
92
 struct SceneItem {
116
 struct SceneItem {
117
+    type_: SceneItemType,
93
     position: Point2,
118
     position: Point2,
94
     state: ItemState,
119
     state: ItemState,
95
     meta_events: Vec<MetaEvent>,
120
     meta_events: Vec<MetaEvent>,
97
 }
122
 }
98
 
123
 
99
 impl SceneItem {
124
 impl SceneItem {
100
-    pub fn new(position: Point2, state: ItemState) -> Self {
101
-        let sprite_type = state.sprite_type();
125
+    pub fn new(type_: SceneItemType, position: Point2, state: ItemState) -> Self {
102
         Self {
126
         Self {
127
+            type_,
103
             position,
128
             position,
104
             state,
129
             state,
105
             meta_events: vec![],
130
             meta_events: vec![],
108
     }
133
     }
109
 
134
 
110
     pub fn sprite_info(&self) -> SpriteInfo {
135
     pub fn sprite_info(&self) -> SpriteInfo {
111
-        SpriteInfo::from_type(&self.state.sprite_type())
136
+        SpriteInfo::from_type(&self.sprite_type())
112
     }
137
     }
113
 
138
 
114
     pub fn tick_sprite(&mut self) {
139
     pub fn tick_sprite(&mut self) {
131
             .rotation(90.0f32.to_radians())
156
             .rotation(90.0f32.to_radians())
132
             .offset(Point2::new(0.5, 0.5))
157
             .offset(Point2::new(0.5, 0.5))
133
     }
158
     }
159
+
160
+    pub fn sprite_type(&self) -> SpriteType {
161
+        // Here some logical about state, nature (soldier, tank, ...) and current behavior to
162
+        // determine sprite type
163
+        match self.state.current_behavior {
164
+            ItemBehavior::Crawling => SpriteType::CrawlingSoldier,
165
+            ItemBehavior::Walking(_) => SpriteType::WalkingSoldier,
166
+            ItemBehavior::Standing(_) => SpriteType::StandingSoldier,
167
+        }
168
+    }
134
 }
169
 }
135
 
170
 
171
+#[derive(Debug)]
136
 enum PhysicEvent {
172
 enum PhysicEvent {
137
     Explosion,
173
     Explosion,
138
 }
174
 }
139
 
175
 
176
+#[derive(Debug)]
140
 enum MetaEvent {
177
 enum MetaEvent {
141
     FearAboutExplosion,
178
     FearAboutExplosion,
142
 }
179
 }
143
 
180
 
181
+#[derive(Debug)]
182
+enum UserEvent {
183
+    Click(Point2),                 // Window coordinates
184
+    AreaSelection(Point2, Point2), // Window coordinates
185
+}
186
+
144
 struct MainState {
187
 struct MainState {
188
+    // time
145
     frame_i: u32,
189
     frame_i: u32,
190
+
191
+    // display
192
+    display_offset: Point2,
146
     sprite_sheet_batch: graphics::spritebatch::SpriteBatch,
193
     sprite_sheet_batch: graphics::spritebatch::SpriteBatch,
147
     map_batch: graphics::spritebatch::SpriteBatch,
194
     map_batch: graphics::spritebatch::SpriteBatch,
195
+
196
+    // scene items
148
     scene_items: Vec<SceneItem>,
197
     scene_items: Vec<SceneItem>,
198
+    scene_items_by_grid_position: HashMap<GridPosition, Vec<usize>>,
199
+
200
+    // events
149
     physics_events: Vec<PhysicEvent>,
201
     physics_events: Vec<PhysicEvent>,
150
-    display_offset: Point2,
202
+
203
+    // user interactions
204
+    left_click_down: Option<Point2>,
205
+    current_cursor_position: Point2,
206
+    user_events: Vec<UserEvent>,
207
+    selected_scene_items: Vec<usize>,
151
 }
208
 }
152
 
209
 
153
 impl MainState {
210
 impl MainState {
167
                 };
224
                 };
168
 
225
 
169
                 scene_items.push(SceneItem::new(
226
                 scene_items.push(SceneItem::new(
227
+                    SceneItemType::Soldier,
170
                     Point2::new((x as f32 * 24.0) + 100.0, (y as f32 * 24.0) + 100.0),
228
                     Point2::new((x as f32 * 24.0) + 100.0, (y as f32 * 24.0) + 100.0),
171
                     ItemState::new(current_behavior),
229
                     ItemState::new(current_behavior),
172
                 ));
230
                 ));
173
             }
231
             }
174
         }
232
         }
175
 
233
 
176
-        let s = MainState {
234
+        let mut main_state = MainState {
177
             frame_i: 0,
235
             frame_i: 0,
236
+            display_offset: Point2::new(0.0, 0.0),
178
             sprite_sheet_batch,
237
             sprite_sheet_batch,
179
             map_batch,
238
             map_batch,
180
             scene_items,
239
             scene_items,
240
+            scene_items_by_grid_position: HashMap::new(),
181
             physics_events: vec![],
241
             physics_events: vec![],
182
-            display_offset: Point2::new(0.0, 0.0),
242
+            left_click_down: None,
243
+            current_cursor_position: Point2::new(0.0, 0.0),
244
+            user_events: vec![],
245
+            selected_scene_items: vec![],
183
         };
246
         };
184
-        Ok(s)
247
+
248
+        for (i, scene_item) in main_state.scene_items.iter().enumerate() {
249
+            let grid_position = grid_position_from_position(&scene_item.position);
250
+            main_state
251
+                .scene_items_by_grid_position
252
+                .entry(grid_position)
253
+                .or_default()
254
+                .push(i);
255
+        }
256
+
257
+        Ok(main_state)
185
     }
258
     }
186
 
259
 
187
     fn inputs(&mut self, ctx: &Context) {
260
     fn inputs(&mut self, ctx: &Context) {
204
         if input::keyboard::is_key_pressed(ctx, KeyCode::Down) {
277
         if input::keyboard::is_key_pressed(ctx, KeyCode::Down) {
205
             self.display_offset.y -= display_offset_by;
278
             self.display_offset.y -= display_offset_by;
206
         }
279
         }
280
+
281
+        while let Some(user_event) = self.user_events.pop() {
282
+            match user_event {
283
+                UserEvent::Click(position) => {
284
+                    let scene_position = Point2::new(
285
+                        position.x - self.display_offset.x,
286
+                        position.y - self.display_offset.y,
287
+                    );
288
+                    self.selected_scene_items.drain(..);
289
+                    if let Some(scene_item_usize) =
290
+                        self.get_first_scene_item_for_position(&scene_position)
291
+                    {
292
+                        self.selected_scene_items.push(scene_item_usize);
293
+                    }
294
+                }
295
+                UserEvent::AreaSelection(from, to) => {
296
+                    let scene_from = Point2::new(
297
+                        from.x - self.display_offset.x,
298
+                        from.y - self.display_offset.y,
299
+                    );
300
+                    let scene_to =
301
+                        Point2::new(to.x - self.display_offset.x, to.y - self.display_offset.y);
302
+                    self.selected_scene_items.drain(..);
303
+                    self.selected_scene_items
304
+                        .extend(self.get_scene_items_for_area(&scene_from, &scene_to));
305
+                }
306
+            }
307
+        }
207
     }
308
     }
208
 
309
 
209
     // TODO: manage errors
310
     // TODO: manage errors
280
             position.y + self.display_offset.y,
381
             position.y + self.display_offset.y,
281
         )
382
         )
282
     }
383
     }
384
+
385
+    fn get_first_scene_item_for_position(&self, position: &Point2) -> Option<usize> {
386
+        // TODO: if found multiple: select nearest
387
+        for (i, scene_item) in self.scene_items.iter().enumerate() {
388
+            let sprite_info = scene_item.sprite_info();
389
+            if scene_item.position.x >= position.x - sprite_info.tile_width
390
+                && scene_item.position.x <= position.x + sprite_info.tile_width
391
+                && scene_item.position.y >= position.y - sprite_info.tile_height
392
+                && scene_item.position.y <= position.y + sprite_info.tile_height
393
+            {
394
+                return Some(i);
395
+            }
396
+        }
397
+
398
+        None
399
+    }
400
+
401
+    fn get_scene_items_for_area(&self, from: &Point2, to: &Point2) -> Vec<usize> {
402
+        let mut selection = vec![];
403
+
404
+        for (i, scene_item) in self.scene_items.iter().enumerate() {
405
+            if scene_item.position.x >= from.x
406
+                && scene_item.position.x <= to.x
407
+                && scene_item.position.y >= from.y
408
+                && scene_item.position.y <= to.y
409
+            {
410
+                selection.push(i);
411
+            }
412
+        }
413
+
414
+        selection
415
+    }
283
 }
416
 }
284
 
417
 
285
 impl event::EventHandler for MainState {
418
 impl event::EventHandler for MainState {
330
     fn draw(&mut self, ctx: &mut Context) -> GameResult {
463
     fn draw(&mut self, ctx: &mut Context) -> GameResult {
331
         graphics::clear(ctx, graphics::BLACK);
464
         graphics::clear(ctx, graphics::BLACK);
332
 
465
 
333
-        let mut mesh_builder = MeshBuilder::new();
466
+        let mut scene_mesh_builder = MeshBuilder::new();
334
 
467
 
335
         for scene_item in self.scene_items.iter() {
468
         for scene_item in self.scene_items.iter() {
336
             self.sprite_sheet_batch.add(
469
             self.sprite_sheet_batch.add(
338
                     .as_draw_param(scene_item.current_frame as f32)
471
                     .as_draw_param(scene_item.current_frame as f32)
339
                     .dest(scene_item.position.clone()),
472
                     .dest(scene_item.position.clone()),
340
             );
473
             );
341
-            mesh_builder.circle(
474
+            scene_mesh_builder.circle(
342
                 DrawMode::fill(),
475
                 DrawMode::fill(),
343
                 scene_item.position.clone(),
476
                 scene_item.position.clone(),
344
                 2.0,
477
                 2.0,
346
                 graphics::WHITE,
479
                 graphics::WHITE,
347
             )?;
480
             )?;
348
         }
481
         }
482
+
483
+        for i in &self.selected_scene_items {
484
+            let selected_scene_item = self
485
+                .scene_items
486
+                .get(*i)
487
+                .expect("scene_items content change !");
488
+            scene_mesh_builder.rectangle(
489
+                DrawMode::Stroke(StrokeOptions::default()),
490
+                graphics::Rect::new(
491
+                    selected_scene_item.position.x - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
492
+                    selected_scene_item.position.y - DEFAULT_SELECTED_SQUARE_SIDE_HALF,
493
+                    DEFAULT_SELECTED_SQUARE_SIDE,
494
+                    DEFAULT_SELECTED_SQUARE_SIDE,
495
+                ),
496
+                graphics::GREEN,
497
+            )?;
498
+        }
499
+
500
+        if let Some(left_click_down) = self.left_click_down {
501
+            if left_click_down != self.current_cursor_position {
502
+                scene_mesh_builder.rectangle(
503
+                    DrawMode::fill(),
504
+                    graphics::Rect::new(
505
+                        left_click_down.x - self.display_offset.x,
506
+                        left_click_down.y - self.display_offset.y,
507
+                        self.current_cursor_position.x - left_click_down.x,
508
+                        self.current_cursor_position.y - left_click_down.y,
509
+                    ),
510
+                    graphics::GREEN,
511
+                )?;
512
+            }
513
+
514
+            scene_mesh_builder.circle(
515
+                DrawMode::fill(),
516
+                left_click_down,
517
+                2.0,
518
+                2.0,
519
+                graphics::YELLOW,
520
+            )?;
521
+        }
522
+
349
         self.map_batch.add(
523
         self.map_batch.add(
350
             graphics::DrawParam::new()
524
             graphics::DrawParam::new()
351
                 .src(graphics::Rect::new(0.0, 0.0, 1.0, 1.0))
525
                 .src(graphics::Rect::new(0.0, 0.0, 1.0, 1.0))
352
                 .dest(Point2::new(0.0, 0.0)),
526
                 .dest(Point2::new(0.0, 0.0)),
353
         );
527
         );
354
 
528
 
355
-        let mesh = mesh_builder.build(ctx)?;
529
+        let scene_mesh = scene_mesh_builder.build(ctx)?;
356
         graphics::draw(
530
         graphics::draw(
357
             ctx,
531
             ctx,
358
             &self.map_batch,
532
             &self.map_batch,
367
         )?;
541
         )?;
368
         graphics::draw(
542
         graphics::draw(
369
             ctx,
543
             ctx,
370
-            &mesh,
544
+            &scene_mesh,
371
             graphics::DrawParam::new()
545
             graphics::DrawParam::new()
372
                 .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
546
                 .dest(self.position_with_display_offset(&Point2::new(0.0, 0.0))),
373
         )?;
547
         )?;
379
         println!("FPS: {}", ggez::timer::fps(ctx));
553
         println!("FPS: {}", ggez::timer::fps(ctx));
380
         Ok(())
554
         Ok(())
381
     }
555
     }
556
+
557
+    fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
558
+        match button {
559
+            MouseButton::Left => {
560
+                self.left_click_down = Some(Point2::new(x, y));
561
+            }
562
+            MouseButton::Right => {}
563
+            MouseButton::Middle => {}
564
+            MouseButton::Other(_) => {}
565
+        }
566
+    }
567
+
568
+    fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
569
+        match button {
570
+            MouseButton::Left => {
571
+                if let Some(left_click_down) = self.left_click_down {
572
+                    if left_click_down == Point2::new(x, y) {
573
+                        self.user_events.push(UserEvent::Click(left_click_down));
574
+                    } else {
575
+                        let from = Point2::new(
576
+                            cmp::min(left_click_down.x as i32, x as i32) as f32,
577
+                            cmp::min(left_click_down.y as i32, y as i32) as f32,
578
+                        );
579
+                        let to = Point2::new(
580
+                            cmp::max(left_click_down.x as i32, x as i32) as f32,
581
+                            cmp::max(left_click_down.y as i32, y as i32) as f32,
582
+                        );
583
+                        self.user_events.push(UserEvent::AreaSelection(from, to));
584
+                    }
585
+                }
586
+                self.left_click_down = None;
587
+            }
588
+            MouseButton::Right => {}
589
+            MouseButton::Middle => {}
590
+            MouseButton::Other(_) => {}
591
+        }
592
+    }
593
+
594
+    fn mouse_motion_event(&mut self, _ctx: &mut Context, x: f32, y: f32, _dx: f32, _dy: f32) {
595
+        self.current_cursor_position = Point2::new(x, y);
596
+    }
382
 }
597
 }
383
 
598
 
384
 pub fn main() -> GameResult {
599
 pub fn main() -> GameResult {