Procházet zdrojové kódy

Add interaction management on gui

Bastien Sevajol před 6 roky
rodič
revize
fe1fd110b6

+ 3 - 2
sandbox/tile/gui/actor.py Zobrazit soubor

3
 
3
 
4
 from sandbox.tile.gui.animation import ANIMATION_WALK
4
 from sandbox.tile.gui.animation import ANIMATION_WALK
5
 from sandbox.tile.gui.animation import ANIMATION_CRAWL
5
 from sandbox.tile.gui.animation import ANIMATION_CRAWL
6
+from synergine2.simulation import Subject
6
 from synergine2_cocos2d.actor import Actor
7
 from synergine2_cocos2d.actor import Actor
7
 
8
 
8
 
9
 
29
         ]
30
         ]
30
     }
31
     }
31
 
32
 
32
-    def __init__(self):
33
-        super().__init__(pyglet.resource.image('actors/man.png'))
33
+    def __init__(self, subject: Subject) -> None:
34
+        super().__init__(pyglet.resource.image('actors/man.png'), subject=subject)

+ 3 - 1
sandbox/tile/gui/base.py Zobrazit soubor

5
 from sandbox.tile.gui.animation import ANIMATION_CRAWL
5
 from sandbox.tile.gui.animation import ANIMATION_CRAWL
6
 from synergine2_cocos2d.animation import Animate
6
 from synergine2_cocos2d.animation import Animate
7
 from synergine2_cocos2d.gui import TMXGui
7
 from synergine2_cocos2d.gui import TMXGui
8
+from synergine2_cocos2d.interaction import MoveActorInteraction
8
 
9
 
9
 
10
 
10
 class Game(TMXGui):
11
 class Game(TMXGui):
11
     def before_run(self) -> None:
12
     def before_run(self) -> None:
12
-        pass
13
+        self.layer_manager.interaction_manager.register(MoveActorInteraction, self.layer_manager)
14
+
13
         # Test
15
         # Test
14
         # from sandbox.tile.gui.actor import Man
16
         # from sandbox.tile.gui.actor import Man
15
         # from cocos import euclid
17
         # from cocos import euclid

+ 3 - 0
synergine2_cocos2d/actor.py Zobrazit soubor

6
 import cocos
6
 import cocos
7
 from cocos import collision_model
7
 from cocos import collision_model
8
 from cocos import euclid
8
 from cocos import euclid
9
+from synergine2.simulation import Subject
9
 from synergine2_cocos2d.animation import AnimatedInterface
10
 from synergine2_cocos2d.animation import AnimatedInterface
10
 
11
 
11
 
12
 
16
     def __init__(
17
     def __init__(
17
         self,
18
         self,
18
         image: pyglet.image.TextureRegion,
19
         image: pyglet.image.TextureRegion,
20
+        subject: Subject,
19
         position=(0, 0),
21
         position=(0, 0),
20
         rotation=0,
22
         rotation=0,
21
         scale=1,
23
         scale=1,
34
             anchor,
36
             anchor,
35
             **kwargs
37
             **kwargs
36
         )
38
         )
39
+        self.subject = subject
37
         self.cshape = None  # type: collision_model.AARectShape
40
         self.cshape = None  # type: collision_model.AARectShape
38
         self.update_cshape()
41
         self.update_cshape()
39
         self.build_animation_images()
42
         self.build_animation_images()

+ 21 - 0
synergine2_cocos2d/event.py Zobrazit soubor

1
+# coding: utf-8
2
+
3
+"""
4
+WARNING: Do not import cocos/pyglet stuff here: cocos/pyglet modules must be loaded inside gui process.
5
+"""
6
+import typing
7
+
8
+from synergine2.simulation import Event
9
+
10
+
11
+class GuiRequestMoveEvent(Event):
12
+    def __init__(
13
+        self,
14
+        subject_id: int,
15
+        move_to_position: typing.Tuple[int, int],
16
+        *args,
17
+        **kwargs
18
+    ) -> None:
19
+        super().__init__(*args, **kwargs)
20
+        self.subject_id = subject_id
21
+        self.move_to_position = move_to_position

+ 4 - 0
synergine2_cocos2d/exception.py Zobrazit soubor

11
 
11
 
12
 class OuterWorldPosition(PositionException):
12
 class OuterWorldPosition(PositionException):
13
     pass
13
     pass
14
+
15
+
16
+class InteractionNotFound(Exception):
17
+    pass

+ 15 - 0
synergine2_cocos2d/gl.py Zobrazit soubor

38
         pyglet.gl.glVertex3f(positions[2][0], positions[2][1], 0)
38
         pyglet.gl.glVertex3f(positions[2][0], positions[2][1], 0)
39
         pyglet.gl.glVertex3f(positions[3][0], positions[3][1], 0)
39
         pyglet.gl.glVertex3f(positions[3][0], positions[3][1], 0)
40
         pyglet.gl.glEnd()
40
         pyglet.gl.glEnd()
41
+
42
+
43
+def draw_line(
44
+    from_position: typing.Tuple[int, int],
45
+    to_position: typing.Tuple[int, int],
46
+    color: rgb_type,
47
+    width: typing.Optional[int]=1,
48
+):
49
+    pyglet.gl.glColor3ub(*color)
50
+    pyglet.gl.glLineWidth(width)
51
+    pyglet.graphics.draw(
52
+        4,
53
+        pyglet.gl.GL_LINES,
54
+        ("v2f", (0, 0, 0, 0, from_position[0], from_position[1], to_position[0], to_position[1]))
55
+    )

+ 72 - 17
synergine2_cocos2d/gui.py Zobrazit soubor

5
 
5
 
6
 import pyglet
6
 import pyglet
7
 from pyglet.window import mouse
7
 from pyglet.window import mouse
8
+from pyglet.window import key
8
 
9
 
9
 import cocos
10
 import cocos
10
 from cocos import collision_model
11
 from cocos import collision_model
17
 from synergine2.xyz import XYZSubjectMixin
18
 from synergine2.xyz import XYZSubjectMixin
18
 from synergine2_cocos2d.actor import Actor
19
 from synergine2_cocos2d.actor import Actor
19
 from synergine2_cocos2d.exception import OuterWorldPosition
20
 from synergine2_cocos2d.exception import OuterWorldPosition
21
+from synergine2_cocos2d.exception import InteractionNotFound
20
 from synergine2_cocos2d.gl import rectangle_positions_type
22
 from synergine2_cocos2d.gl import rectangle_positions_type
21
 from synergine2_cocos2d.gl import draw_rectangle
23
 from synergine2_cocos2d.gl import draw_rectangle
24
+from synergine2_cocos2d.interaction import InteractionManager
22
 from synergine2_cocos2d.layer import LayerManager
25
 from synergine2_cocos2d.layer import LayerManager
23
 from synergine2_cocos2d.middleware import TMXMiddleware
26
 from synergine2_cocos2d.middleware import TMXMiddleware
27
+from synergine2_cocos2d.middleware import TMXMiddlewareMapMiddleware
28
+from synergine2_cocos2d.user_action import UserAction
24
 
29
 
25
 
30
 
26
 class GridManager(object):
31
 class GridManager(object):
176
         self.sright = None
181
         self.sright = None
177
         self.sbottom = None
182
         self.sbottom = None
178
         self.s_top = None
183
         self.s_top = None
184
+        self.user_action_pending = None  # UserAction
179
 
185
 
180
         # opers that change cshape must ensure it goes to False,
186
         # opers that change cshape must ensure it goes to False,
181
         # selection opers must ensure it goes to True
187
         # selection opers must ensure it goes to True
204
         self.collision_manager.remove_tricky(actor)
210
         self.collision_manager.remove_tricky(actor)
205
 
211
 
206
     def draw(self, *args, **kwargs) -> None:
212
     def draw(self, *args, **kwargs) -> None:
213
+        self.draw_update_cshapes()
214
+        self.draw_selection()
215
+        self.draw_interactions()
216
+
217
+    def draw_update_cshapes(self) -> None:
207
         for actor in self.selectable_actors:
218
         for actor in self.selectable_actors:
208
             if actor.need_update_cshape:
219
             if actor.need_update_cshape:
209
                 if self.collision_manager.knows(actor):
220
                 if self.collision_manager.knows(actor):
211
                     actor.update_cshape()
222
                     actor.update_cshape()
212
                     self.collision_manager.add(actor)
223
                     self.collision_manager.add(actor)
213
 
224
 
225
+    def draw_selection(self) -> None:
214
         for actor, cshape in self.selection.items():
226
         for actor, cshape in self.selection.items():
215
             grid_position = self.grid_manager.get_grid_position(actor.position)
227
             grid_position = self.grid_manager.get_grid_position(actor.position)
216
             rect_positions = self.grid_manager.get_rectangle_positions(grid_position)
228
             rect_positions = self.grid_manager.get_rectangle_positions(grid_position)
220
                 (0, 81, 211),
232
                 (0, 81, 211),
221
             )
233
             )
222
 
234
 
235
+    def draw_interactions(self) -> None:
236
+        if self.user_action_pending:
237
+            try:
238
+                interaction = self.layer_manager.interaction_manager.get_for_user_action(self.user_action_pending)
239
+                interaction.draw_pending()
240
+            except InteractionNotFound:
241
+                pass
242
+
223
     def on_enter(self):
243
     def on_enter(self):
224
         super(EditLayer, self).on_enter()
244
         super(EditLayer, self).on_enter()
225
         scene = self.get_ancestor(cocos.scene.Scene)
245
         scene = self.get_ancestor(cocos.scene.Scene)
365
 
385
 
366
     def on_key_press(self, k, m):
386
     def on_key_press(self, k, m):
367
         binds = self.bindings
387
         binds = self.bindings
388
+
389
+        # TODO: Clarify code
390
+        # Actions available if actor selected
391
+        if self.selection:
392
+            if k == key.M:
393
+                self.user_action_pending = UserAction.ORDER_MOVE
394
+
368
         if k in binds:
395
         if k in binds:
369
             self.buttons[binds[k]] = 1
396
             self.buttons[binds[k]] = 1
370
             self.modifiers[binds[k]] = 1
397
             self.modifiers[binds[k]] = 1
391
             'GUI click: x: {}, y: {}, rx: {}, ry: {} ({}|{})'.format(x, y, rx, ry, buttons, modifiers)
418
             'GUI click: x: {}, y: {}, rx: {}, ry: {} ({}|{})'.format(x, y, rx, ry, buttons, modifiers)
392
         )
419
         )
393
 
420
 
394
-        actor = self.single_actor_from_mouse()
395
-        if actor:
396
-            self.selection_add(actor)
421
+        if mouse.LEFT:
422
+            # Non action pending case
423
+            if not self.user_action_pending:
424
+                actor = self.single_actor_from_mouse()
425
+                if actor:
426
+                    self.selection.clear()
427
+                    self.selection_add(actor)
428
+            # Action pending case
429
+            else:
430
+                try:
431
+                    interaction = self.layer_manager.interaction_manager.get_for_user_action(self.user_action_pending)
432
+                    interaction.execute()
433
+                except InteractionNotFound:
434
+                    pass
435
+
436
+        if mouse.RIGHT:
437
+            if self.user_action_pending:
438
+                self.user_action_pending = None
397
 
439
 
398
     def on_mouse_release(self, sx, sy, button, modifiers):
440
     def on_mouse_release(self, sx, sy, button, modifiers):
399
         # should we handle here mod_restricted_mov ?
441
         # should we handle here mod_restricted_mov ?
423
         else:
465
         else:
424
             # new_selected becomes the current selected
466
             # new_selected becomes the current selected
425
             self.selection.clear()
467
             self.selection.clear()
468
+            self.user_action_pending = None
426
             if under_mouse_unique is not None:
469
             if under_mouse_unique is not None:
427
                 self.selection_add(under_mouse_unique)
470
                 self.selection_add(under_mouse_unique)
428
 
471
 
581
         subject: XYZSubjectMixin,
624
         subject: XYZSubjectMixin,
582
         layer_manager: LayerManager,
625
         layer_manager: LayerManager,
583
     ) -> None:
626
     ) -> None:
584
-        actor = self.actor_class()
585
-        pixel_position = layer_manager.grid_manager.get_pixel_position_of_grid_position((
586
-            subject.position[0],
587
-            subject.position[1],
588
-        ))
627
+        actor = self.actor_class(subject)
628
+        pixel_position = layer_manager.grid_manager.get_pixel_position_of_grid_position(
629
+            (subject.position[0], subject.position[1]),
630
+        )
589
         actor.update_position(euclid.Vector2(*pixel_position))
631
         actor.update_position(euclid.Vector2(*pixel_position))
590
 
632
 
591
         # TODO: Selectable nature must be configurable
633
         # TODO: Selectable nature must be configurable
631
             resizable=True,
673
             resizable=True,
632
         )
674
         )
633
 
675
 
676
+        self.interaction_manager = InteractionManager(
677
+            config=self.config,
678
+            logger=self.logger,
679
+            terminal=self.terminal,
680
+        )
681
+        self.layer_manager = LayerManager(
682
+            self.config,
683
+            self.logger,
684
+            middleware=self.get_layer_middleware(),
685
+            interaction_manager=self.interaction_manager,
686
+        )
687
+        self.layer_manager.init()
688
+        self.layer_manager.center()
689
+
634
         # Enable blending
690
         # Enable blending
635
         pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
691
         pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
636
         pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
692
         pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
641
 
697
 
642
         self.subject_mapper_factory = SubjectMapperFactory()
698
         self.subject_mapper_factory = SubjectMapperFactory()
643
 
699
 
700
+    def get_layer_middleware(self) -> MapMiddleware:
701
+        raise NotImplementedError()
702
+
644
     def run(self):
703
     def run(self):
645
         self.before_run()
704
         self.before_run()
646
         pyglet.clock.schedule_interval(
705
         pyglet.clock.schedule_interval(
672
         map_dir_path: str=None,
731
         map_dir_path: str=None,
673
     ):
732
     ):
674
         assert map_dir_path
733
         assert map_dir_path
734
+        self.map_dir_path = map_dir_path
675
         super(TMXGui, self).__init__(
735
         super(TMXGui, self).__init__(
676
             config,
736
             config,
677
             logger,
737
             logger,
678
             terminal,
738
             terminal,
679
             read_queue_interval,
739
             read_queue_interval,
680
         )
740
         )
681
-        self.map_dir_path = map_dir_path
682
-        self.layer_manager = LayerManager(
741
+
742
+    def get_layer_middleware(self) -> MapMiddleware:
743
+        return TMXMiddleware(
683
             self.config,
744
             self.config,
684
             self.logger,
745
             self.logger,
685
-            middleware=TMXMiddleware(
686
-                self.config,
687
-                self.logger,
688
-                self.map_dir_path,
689
-            ),
746
+            self.map_dir_path,
690
         )
747
         )
691
-        self.layer_manager.init()
692
-        self.layer_manager.center()
693
 
748
 
694
     def get_main_scene(self) -> cocos.cocosnode.CocosNode:
749
     def get_main_scene(self) -> cocos.cocosnode.CocosNode:
695
         return self.layer_manager.main_scene
750
         return self.layer_manager.main_scene

+ 102 - 0
synergine2_cocos2d/interaction.py Zobrazit soubor

1
+# coding: utf-8
2
+import typing
3
+
4
+from synergine2.config import Config
5
+from synergine2.log import SynergineLogger
6
+from synergine2.terminals import Terminal, TerminalPackage
7
+from synergine2_cocos2d.event import GuiRequestMoveEvent
8
+from synergine2_cocos2d.exception import InteractionNotFound
9
+from synergine2_cocos2d.gl import draw_line
10
+from synergine2_cocos2d.layer import LayerManager
11
+from synergine2_cocos2d.user_action import UserAction
12
+
13
+
14
+class InteractionManager(object):
15
+    def __init__(
16
+        self,
17
+        config: Config,
18
+        logger: SynergineLogger,
19
+        terminal: Terminal,
20
+    ) -> None:
21
+        self.config = config
22
+        self.logger = logger
23
+        self.terminal = terminal
24
+        self.interactions = []
25
+
26
+    def register(
27
+        self,
28
+        interaction_class: typing.Type['Interaction'],
29
+        layer_manager: LayerManager,
30
+    ) -> None:
31
+        self.interactions.append(interaction_class(
32
+            self.config,
33
+            self.logger,
34
+            terminal=self.terminal,
35
+            layer_manager=layer_manager,
36
+        ))
37
+
38
+    def get_for_user_action(self, action: UserAction) -> 'Interaction':
39
+        for interaction in self.interactions:
40
+            if interaction.gui_action == action:
41
+                return interaction
42
+        raise InteractionNotFound('For action"{}"'.format(action))
43
+
44
+
45
+class Interaction(object):
46
+    gui_action = None  # type: UserAction
47
+
48
+    def __init__(
49
+        self,
50
+        config: Config,
51
+        logger: SynergineLogger,
52
+        terminal: Terminal,
53
+        layer_manager: LayerManager,
54
+    ) -> None:
55
+        self.config = config
56
+        self.logger = logger
57
+        self.terminal = terminal
58
+        self.layer_manager = layer_manager
59
+
60
+    def draw_pending(self) -> None:
61
+        pass
62
+
63
+    def execute(self) -> None:
64
+        package = self.get_package_for_terminal()
65
+        self.terminal.send(package)
66
+
67
+    def get_package_for_terminal(self) -> TerminalPackage:
68
+        raise NotImplementedError()
69
+
70
+
71
+class MoveActorInteraction(Interaction):
72
+    gui_action = UserAction.ORDER_MOVE
73
+
74
+    def draw_pending(self) -> None:
75
+            for actor in self.layer_manager.edit_layer.selection:
76
+                grid_position = self.layer_manager.grid_manager.get_grid_position(actor.position)
77
+                pixel_position = self.layer_manager.grid_manager.get_pixel_position_of_grid_position(grid_position)
78
+
79
+                draw_line(
80
+                    self.layer_manager.scrolling_manager.world_to_screen(*pixel_position),
81
+                    self.layer_manager.edit_layer.screen_mouse,
82
+                    (0, 0, 255),
83
+                )
84
+
85
+    def get_package_for_terminal(self) -> TerminalPackage:
86
+        # TODO: MoveEvent ?
87
+        events = []
88
+        mouse_grid_position = self.layer_manager.grid_manager.get_grid_position(
89
+            self.layer_manager.scrolling_manager.screen_to_world(
90
+                *self.layer_manager.edit_layer.screen_mouse,
91
+            )
92
+        )
93
+
94
+        for actor in self.layer_manager.edit_layer.selection:
95
+            events.append(GuiRequestMoveEvent(
96
+                subject_id=actor.subject.id,
97
+                move_to_position=mouse_grid_position,
98
+            ))
99
+
100
+        return TerminalPackage(
101
+            events=events
102
+        )

+ 3 - 0
synergine2_cocos2d/layer.py Zobrazit soubor

12
 if False:
12
 if False:
13
     from synergine2_cocos2d.actor import Actor
13
     from synergine2_cocos2d.actor import Actor
14
     from synergine2_cocos2d.gui import GridManager
14
     from synergine2_cocos2d.gui import GridManager
15
+    from synergine2_cocos2d.interaction import InteractionManager
15
 
16
 
16
 
17
 
17
 class ScrollingManager(cocos.layer.ScrollingManager):
18
 class ScrollingManager(cocos.layer.ScrollingManager):
52
         config: Config,
53
         config: Config,
53
         logger: SynergineLogger,
54
         logger: SynergineLogger,
54
         middleware: MapMiddleware,
55
         middleware: MapMiddleware,
56
+        interaction_manager: 'InteractionManager',
55
     ) -> None:
57
     ) -> None:
56
         self.config = config
58
         self.config = config
57
         self.logger = logger
59
         self.logger = logger
58
         self.middleware = middleware
60
         self.middleware = middleware
61
+        self.interaction_manager = interaction_manager
59
 
62
 
60
         self.grid_manager = None  # type: GridManager
63
         self.grid_manager = None  # type: GridManager
61
         self.scrolling_manager = None  # type: ScrollingManager
64
         self.scrolling_manager = None  # type: ScrollingManager

+ 6 - 0
synergine2_cocos2d/user_action.py Zobrazit soubor

1
+# coding: utf-8
2
+from enum import Enum
3
+
4
+
5
+class UserAction(Enum):
6
+    ORDER_MOVE = 'ORDER_MOVE'