Browse Source

use map for move costs

Bastien Sevajol 6 years ago
parent
commit
40798b8755

File diff suppressed because it is too large
+ 1 - 1
sandbox/tile/maps/003/003.tmx


BIN
sandbox/tile/maps/003/terrain.png View File


+ 6 - 6
sandbox/tile/maps/003/terrain.tsx View File

@@ -3,16 +3,16 @@
3 3
  <image source="terrain.png" width="64" height="64"/>
4 4
  <tile id="0">
5 5
   <properties>
6
-   <property name="name" type="str" value="Wood wall"/>
7
-   <property name="traversable_by_man" type="bool" value="false"/>
8
-   <property name="traversable_by_vehicle" type="bool" value="false"/>
6
+   <property name="name" type="str" value="Grass"/>
7
+   <property name="traversable_by_man" type="bool" value="true"/>
8
+   <property name="traversable_by_vehicle" type="bool" value="true"/>
9 9
   </properties>
10 10
  </tile>
11 11
  <tile id="1">
12 12
   <properties>
13
-   <property name="name" type="str" value="Grass"/>
14
-   <property name="traversable_by_man" type="bool" value="true"/>
15
-   <property name="traversable_by_vehicle" type="bool" value="true"/>
13
+   <property name="name" type="str" value="Wood wall"/>
14
+   <property name="traversable_by_man" type="bool" value="false"/>
15
+   <property name="traversable_by_vehicle" type="bool" value="false"/>
16 16
   </properties>
17 17
  </tile>
18 18
 </tileset>

+ 20 - 2
sandbox/tile/simulation/base.py View File

@@ -1,11 +1,29 @@
1 1
 # coding: utf-8
2
+import tmx
3
+
4
+from sandbox.tile.simulation.physics import TerrainTile
5
+from sandbox.tile.simulation.physics import TileMoveCostComputer
2 6
 from synergine2.config import Config
3
-from synergine2_xyz.physics import Physics, TMXPhysics
7
+from synergine2_xyz.map import TMXMap
8
+from synergine2_xyz.physics import Physics
9
+from synergine2_xyz.physics import TMXPhysics
4 10
 from synergine2_xyz.simulation import XYZSimulation
5 11
 from synergine2_xyz.subjects import XYZSubjects
6 12
 from synergine2_xyz.subjects import XYZSubject
7 13
 
8 14
 
15
+class TileMap(TMXMap):
16
+    xyz_tile_class = TerrainTile
17
+
18
+    def get_default_tileset(self) -> tmx.Tileset:
19
+        return self.tmx_tilesets['terrain']
20
+
21
+
22
+class TilePhysics(TMXPhysics):
23
+    tmx_map_class = TileMap
24
+    move_cost_computer_class = TileMoveCostComputer
25
+
26
+
9 27
 class TileStrategySimulation(XYZSimulation):
10 28
     behaviours_classes = [
11 29
 
@@ -20,7 +38,7 @@ class TileStrategySimulation(XYZSimulation):
20 38
         super().__init__(config)
21 39
 
22 40
     def create_physics(self) -> Physics:
23
-        return TMXPhysics(
41
+        return TilePhysics(
24 42
             config=self.config,
25 43
             map_file_path=self.map_file_path,
26 44
         )

+ 28 - 0
sandbox/tile/simulation/physics.py View File

@@ -0,0 +1,28 @@
1
+# coding: utf-8
2
+from synergine2_xyz.map import XYZTile
3
+from synergine2_xyz.physics import MoveCostComputer
4
+
5
+if False:
6
+    from sandbox.tile.simulation.base import BaseSubject
7
+
8
+
9
+class TerrainTile(XYZTile):
10
+    pass
11
+
12
+
13
+class TileMoveCostComputer(MoveCostComputer):
14
+    def compute_move_cost(
15
+        self,
16
+        subject: 'BaseSubject',
17
+        tile: TerrainTile,
18
+        previous_node: str,
19
+        next_node: str,
20
+        unknown,
21
+    ) -> float:
22
+        # TODO: Objets/IT qui compute les couts de déplacement
23
+
24
+        if not tile.property('traversable_by_man'):
25
+            # TODO: revoir la lib disjkstar because les mecs traverses quand meme ...
26
+            return 1000000000000000000000000
27
+
28
+        return 1.0

+ 14 - 0
synergine2_xyz/exceptions.py View File

@@ -0,0 +1,14 @@
1
+# coding: utf-8
2
+from synergine2.exceptions import SynergineException
3
+
4
+
5
+class XYZException(SynergineException):
6
+    pass
7
+
8
+
9
+class MapException(XYZException):
10
+    pass
11
+
12
+
13
+class ImproperlyConfiguredMap(MapException):
14
+    pass

+ 38 - 8
synergine2_xyz/map.py View File

@@ -3,18 +3,37 @@ import typing
3 3
 
4 4
 import tmx
5 5
 
6
+from synergine2_xyz.exceptions import ImproperlyConfiguredMap
6 7
 
7
-class UnconstructedTile(object):
8
+
9
+class XYZTile(object):
10
+    def __init__(self, tile: tmx.Tile) -> None:
11
+        self.tile = tile
12
+
13
+    def property(self, name: str) -> typing.Union[str, int, float]:
14
+        for property in self.tile.properties:
15
+            if property.name == name:
16
+                return property.value
17
+
18
+        raise ImproperlyConfiguredMap('Tile with id "{}" don\'t contains "{}" property'.format(
19
+            self.tile.id,
20
+            name,
21
+        ))
22
+
23
+
24
+class UnconstructedTile(XYZTile):
8 25
     pass
9 26
 
10 27
 
11 28
 class TMXMap(object):
29
+    xyz_tile_class = XYZTile
30
+
12 31
     def __init__(self, map_file_path: str) -> None:
13 32
         self.tmx_map = tmx.TileMap.load(map_file_path)
14 33
         self.tmx_layers = {}  # type: typing.Dict[str, tmx.Layer]
15
-        self.tmx_layer_tiles = {}  # type: typing.Dict[str, typing.Dict[str, tmx.Tile]]
34
+        self.tmx_layer_tiles = {}  # type: typing.Dict[str, typing.Dict[str, XYZTile]]
16 35
         self.tmx_tilesets = {}  # type: typing.Dict[str, tmx.Tileset]
17
-        self.tmx_tiles = {}  # type: typing.Dict[int, tmx.Tile]
36
+        self.tmx_tiles = {}  # type: typing.Dict[int, XYZTile]
18 37
         self._load()
19 38
 
20 39
     @property
@@ -25,6 +44,13 @@ class TMXMap(object):
25 44
     def width(self) -> int:
26 45
         return self.tmx_map.width
27 46
 
47
+    def get_default_tileset(self) -> tmx.Tileset:
48
+        return list(self.tmx_tilesets.values())[0]
49
+
50
+    def get_default_tile(self) -> XYZTile:
51
+        tileset = self.get_default_tileset()
52
+        return self.xyz_tile_class(tileset.tiles[0])
53
+
28 54
     def _load(self) -> None:
29 55
         self._load_layers()
30 56
         self._load_tilesets()
@@ -38,11 +64,11 @@ class TMXMap(object):
38 64
         for tileset in self.tmx_map.tilesets:
39 65
             self.tmx_tilesets[tileset.name] = tileset
40 66
             for tile in tileset.tiles:
41
-                self.tmx_tiles[tileset.firstgid + tile.id] = tile
67
+                self.tmx_tiles[tileset.firstgid + tile.id] = self.xyz_tile_class(tile)
42 68
 
43 69
     def _load_tiles(self) -> None:
44 70
         for layer_name, layer in self.tmx_layers.items():
45
-            x, y = -1, -1
71
+            x, y = -1, 0
46 72
             self.tmx_layer_tiles[layer_name] = {}
47 73
 
48 74
             # no tiles
@@ -59,9 +85,10 @@ class TMXMap(object):
59 85
                 position_key = '{}.{}'.format(x, y)
60 86
 
61 87
                 if not layer_tile.gid:
62
-                    tile = None
88
+                    tile = self.get_default_tile()
63 89
                 elif not layer_tile.gid in self.tmx_tiles:
64
-                    tile = UnconstructedTile()
90
+                    # tile = UnconstructedTile()
91
+                    tile = self.get_default_tile()
65 92
                 else:
66 93
                     tile = self.tmx_tiles[layer_tile.gid]
67 94
 
@@ -73,5 +100,8 @@ class TMXMap(object):
73 100
     def tileset(self, name: str) -> tmx.Tileset:
74 101
         return self.tmx_tilesets[name]
75 102
 
76
-    def tile(self, gid: int) -> tmx.Tile:
103
+    def tile(self, gid: int) -> XYZTile:
77 104
         return self.tmx_tiles[gid]
105
+
106
+    def layer_tiles(self, name: str) -> typing.Dict[str, XYZTile]:
107
+        return self.tmx_layer_tiles[name]

+ 1 - 0
synergine2_xyz/move.py View File

@@ -67,6 +67,7 @@ class MoveToMechanism(SubjectMechanism):
67 67
                 move.path = self.simulation.physics.found_path(
68 68
                     start=self.subject.position,
69 69
                     end=move.move_to,
70
+                    subject=self.subject,
70 71
                 )
71 72
 
72 73
                 # Note: We are in process, move change will be lost

+ 48 - 32
synergine2_xyz/physics.py View File

@@ -1,22 +1,49 @@
1 1
 # coding: utf-8
2 2
 import typing
3 3
 
4
-import tmx
5 4
 from dijkstar import Graph
6 5
 from dijkstar import find_path
7 6
 
8 7
 from synergine2.config import Config
9
-from synergine2_xyz.map import TMXMap
10
-from synergine2_xyz.tmx_utils import get_layer_by_name
8
+from synergine2_xyz.map import TMXMap, XYZTile
9
+from synergine2_xyz.subjects import XYZSubject
10
+from synergine2_xyz.xyz import get_neighbor_positions
11
+
12
+
13
+class MoveCostComputer(object):
14
+    def __init__(
15
+        self,
16
+        config: Config,
17
+    ) -> None:
18
+        self.config = config
19
+
20
+    def for_subject(self, subject: XYZSubject):
21
+        # TODO: Verifier ce que sont les parametres pour les nommer correctement
22
+        def move_cost_func(previous_node: str, next_node: str, tile: XYZTile, unknown):
23
+            return self.compute_move_cost(subject, tile, previous_node, next_node, unknown)
24
+        return move_cost_func
25
+
26
+    def compute_move_cost(
27
+        self,
28
+        subject: XYZSubject,
29
+        tile: XYZTile,
30
+        previous_node: str,
31
+        next_node: str,
32
+        unknown,
33
+    ) -> float:
34
+        return 1.0
11 35
 
12 36
 
13 37
 class Physics(object):
38
+    move_cost_computer_class = MoveCostComputer
39
+
14 40
     def __init__(
15 41
         self,
16 42
         config: Config,
17 43
     ) -> None:
18 44
         self.config = config
19 45
         self.graph = Graph()
46
+        self.move_cost_computer = self.move_cost_computer_class(config)
20 47
 
21 48
     def load(self) -> None:
22 49
         raise NotImplementedError()
@@ -28,11 +55,12 @@ class Physics(object):
28 55
         self,
29 56
         start: typing.Tuple[int, int],
30 57
         end: typing.Tuple[int, int],
58
+        subject: XYZSubject,
31 59
     ) -> typing.List[typing.Tuple[int, int]]:
32 60
         start_key = self.position_to_key(start)
33 61
         end_key = self.position_to_key(end)
34 62
 
35
-        found_path = find_path(self.graph, start_key, end_key)
63
+        found_path = find_path(self.graph, start_key, end_key, cost_func=self.move_cost_computer.for_subject(subject))
36 64
         regular_path = []
37 65
         for position in found_path[0][1:]:
38 66
             x, y = map(int, position.split('.'))
@@ -42,6 +70,8 @@ class Physics(object):
42 70
 
43 71
 
44 72
 class TMXPhysics(Physics):
73
+    tmx_map_class = TMXMap
74
+
45 75
     def __init__(
46 76
         self,
47 77
         config: Config,
@@ -49,42 +79,28 @@ class TMXPhysics(Physics):
49 79
     ) -> None:
50 80
         super().__init__(config)
51 81
         self.map_file_path = map_file_path
82
+        self.tmx_map = self.tmx_map_class(map_file_path)
52 83
 
53 84
     def load(self) -> None:
54 85
         self.load_graph_from_map(self.map_file_path)
55 86
 
56 87
     def load_graph_from_map(self, map_file_path: str) -> None:
57
-        tmx_map = TMXMap(map_file_path)
58
-
59 88
         # TODO: tmx_map contient tout en cache, faire le dessous en exploitant tmx_map.
60
-        for y in range(tmx_map.height):
61
-            for x in range(tmx_map.width):
89
+        for y in range(self.tmx_map.height):
90
+            for x in range(self.tmx_map.width):
62 91
                 position = self.position_to_key((x, y))
63
-                neighbors = []
64
-
65
-                for modifier_x, modifier_y in (
66
-                    (+1, +1),
67
-                    (+1, +0),
68
-                    (+1, -1),
69
-                    (+0, -1),
70
-                    (-1, -1),
71
-                    (-1, +0),
72
-                    (-1, -1),
73
-                    (+0, +1),
74
-                ):
75
-                    try:
76
-                        neighbors.append('{}.{}'.format(x + modifier_x, y + modifier_y))
77
-                    except ValueError:
78
-                        pass
79
-
80
-                for neighbor in neighbors:
81
-                    neighbor_x, neighbor_y = map(int, neighbor.split('.'))
82
-
83
-                    if neighbor_x > 69 or neighbor_x < 0:
92
+
93
+                for neighbor_position in get_neighbor_positions((x, y)):
94
+                    neighbor = '{}.{}'.format(*neighbor_position)
95
+                    neighbor_x, neighbor_y = neighbor_position
96
+
97
+                    if neighbor_x > self.tmx_map.width-1 or neighbor_x < 0:
84 98
                         continue
85 99
 
86
-                    if neighbor_y > 69 or neighbor_y < 0:
100
+                    if neighbor_y > self.tmx_map.height-1 or neighbor_y < 0:
87 101
                         continue
88 102
 
89
-                    # TODO: Voir https://pypi.python.org/pypi/Dijkstar/2.2
90
-                    self.graph.add_edge(position, neighbor, 1)
103
+                    # Note: movement consider future tile properties
104
+                    to_tile = self.tmx_map.layer_tiles('terrain')[neighbor]
105
+                    # Note: Voir https://pypi.python.org/pypi/Dijkstar/2.2
106
+                    self.graph.add_edge(position, neighbor, to_tile)

+ 10 - 0
synergine2_xyz/xyz.py View File

@@ -1,4 +1,5 @@
1 1
 # coding: utf-8
2
+import typing
2 3
 from math import acos
3 4
 from math import degrees
4 5
 from math import sqrt
@@ -201,3 +202,12 @@ def get_direction_from_north_degree(degree: float):
201 202
         degree,
202 203
         DIRECTION_FROM_NORTH_DEGREES,
203 204
     ))
205
+
206
+
207
+def get_neighbor_positions(position: typing.Tuple[int, int]) -> typing.List[typing.Tuple[int, int]]:
208
+    neighbors = []
209
+
210
+    for modifier_x, modifier_y, modifier_z in DIRECTION_MODIFIERS.values():
211
+        neighbors.append((position[0] + modifier_x, position[1] + modifier_y))
212
+
213
+    return neighbors