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
  <image source="terrain.png" width="64" height="64"/>
3
  <image source="terrain.png" width="64" height="64"/>
4
  <tile id="0">
4
  <tile id="0">
5
   <properties>
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
   </properties>
9
   </properties>
10
  </tile>
10
  </tile>
11
  <tile id="1">
11
  <tile id="1">
12
   <properties>
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
   </properties>
16
   </properties>
17
  </tile>
17
  </tile>
18
 </tileset>
18
 </tileset>

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

1
 # coding: utf-8
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
 from synergine2.config import Config
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
 from synergine2_xyz.simulation import XYZSimulation
10
 from synergine2_xyz.simulation import XYZSimulation
5
 from synergine2_xyz.subjects import XYZSubjects
11
 from synergine2_xyz.subjects import XYZSubjects
6
 from synergine2_xyz.subjects import XYZSubject
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
 class TileStrategySimulation(XYZSimulation):
27
 class TileStrategySimulation(XYZSimulation):
10
     behaviours_classes = [
28
     behaviours_classes = [
11
 
29
 
20
         super().__init__(config)
38
         super().__init__(config)
21
 
39
 
22
     def create_physics(self) -> Physics:
40
     def create_physics(self) -> Physics:
23
-        return TMXPhysics(
41
+        return TilePhysics(
24
             config=self.config,
42
             config=self.config,
25
             map_file_path=self.map_file_path,
43
             map_file_path=self.map_file_path,
26
         )
44
         )

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

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

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
 
3
 
4
 import tmx
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
     pass
25
     pass
9
 
26
 
10
 
27
 
11
 class TMXMap(object):
28
 class TMXMap(object):
29
+    xyz_tile_class = XYZTile
30
+
12
     def __init__(self, map_file_path: str) -> None:
31
     def __init__(self, map_file_path: str) -> None:
13
         self.tmx_map = tmx.TileMap.load(map_file_path)
32
         self.tmx_map = tmx.TileMap.load(map_file_path)
14
         self.tmx_layers = {}  # type: typing.Dict[str, tmx.Layer]
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
         self.tmx_tilesets = {}  # type: typing.Dict[str, tmx.Tileset]
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
         self._load()
37
         self._load()
19
 
38
 
20
     @property
39
     @property
25
     def width(self) -> int:
44
     def width(self) -> int:
26
         return self.tmx_map.width
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
     def _load(self) -> None:
54
     def _load(self) -> None:
29
         self._load_layers()
55
         self._load_layers()
30
         self._load_tilesets()
56
         self._load_tilesets()
38
         for tileset in self.tmx_map.tilesets:
64
         for tileset in self.tmx_map.tilesets:
39
             self.tmx_tilesets[tileset.name] = tileset
65
             self.tmx_tilesets[tileset.name] = tileset
40
             for tile in tileset.tiles:
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
     def _load_tiles(self) -> None:
69
     def _load_tiles(self) -> None:
44
         for layer_name, layer in self.tmx_layers.items():
70
         for layer_name, layer in self.tmx_layers.items():
45
-            x, y = -1, -1
71
+            x, y = -1, 0
46
             self.tmx_layer_tiles[layer_name] = {}
72
             self.tmx_layer_tiles[layer_name] = {}
47
 
73
 
48
             # no tiles
74
             # no tiles
59
                 position_key = '{}.{}'.format(x, y)
85
                 position_key = '{}.{}'.format(x, y)
60
 
86
 
61
                 if not layer_tile.gid:
87
                 if not layer_tile.gid:
62
-                    tile = None
88
+                    tile = self.get_default_tile()
63
                 elif not layer_tile.gid in self.tmx_tiles:
89
                 elif not layer_tile.gid in self.tmx_tiles:
64
-                    tile = UnconstructedTile()
90
+                    # tile = UnconstructedTile()
91
+                    tile = self.get_default_tile()
65
                 else:
92
                 else:
66
                     tile = self.tmx_tiles[layer_tile.gid]
93
                     tile = self.tmx_tiles[layer_tile.gid]
67
 
94
 
73
     def tileset(self, name: str) -> tmx.Tileset:
100
     def tileset(self, name: str) -> tmx.Tileset:
74
         return self.tmx_tilesets[name]
101
         return self.tmx_tilesets[name]
75
 
102
 
76
-    def tile(self, gid: int) -> tmx.Tile:
103
+    def tile(self, gid: int) -> XYZTile:
77
         return self.tmx_tiles[gid]
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
                 move.path = self.simulation.physics.found_path(
67
                 move.path = self.simulation.physics.found_path(
68
                     start=self.subject.position,
68
                     start=self.subject.position,
69
                     end=move.move_to,
69
                     end=move.move_to,
70
+                    subject=self.subject,
70
                 )
71
                 )
71
 
72
 
72
                 # Note: We are in process, move change will be lost
73
                 # Note: We are in process, move change will be lost

+ 48 - 32
synergine2_xyz/physics.py View File

1
 # coding: utf-8
1
 # coding: utf-8
2
 import typing
2
 import typing
3
 
3
 
4
-import tmx
5
 from dijkstar import Graph
4
 from dijkstar import Graph
6
 from dijkstar import find_path
5
 from dijkstar import find_path
7
 
6
 
8
 from synergine2.config import Config
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
 class Physics(object):
37
 class Physics(object):
38
+    move_cost_computer_class = MoveCostComputer
39
+
14
     def __init__(
40
     def __init__(
15
         self,
41
         self,
16
         config: Config,
42
         config: Config,
17
     ) -> None:
43
     ) -> None:
18
         self.config = config
44
         self.config = config
19
         self.graph = Graph()
45
         self.graph = Graph()
46
+        self.move_cost_computer = self.move_cost_computer_class(config)
20
 
47
 
21
     def load(self) -> None:
48
     def load(self) -> None:
22
         raise NotImplementedError()
49
         raise NotImplementedError()
28
         self,
55
         self,
29
         start: typing.Tuple[int, int],
56
         start: typing.Tuple[int, int],
30
         end: typing.Tuple[int, int],
57
         end: typing.Tuple[int, int],
58
+        subject: XYZSubject,
31
     ) -> typing.List[typing.Tuple[int, int]]:
59
     ) -> typing.List[typing.Tuple[int, int]]:
32
         start_key = self.position_to_key(start)
60
         start_key = self.position_to_key(start)
33
         end_key = self.position_to_key(end)
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
         regular_path = []
64
         regular_path = []
37
         for position in found_path[0][1:]:
65
         for position in found_path[0][1:]:
38
             x, y = map(int, position.split('.'))
66
             x, y = map(int, position.split('.'))
42
 
70
 
43
 
71
 
44
 class TMXPhysics(Physics):
72
 class TMXPhysics(Physics):
73
+    tmx_map_class = TMXMap
74
+
45
     def __init__(
75
     def __init__(
46
         self,
76
         self,
47
         config: Config,
77
         config: Config,
49
     ) -> None:
79
     ) -> None:
50
         super().__init__(config)
80
         super().__init__(config)
51
         self.map_file_path = map_file_path
81
         self.map_file_path = map_file_path
82
+        self.tmx_map = self.tmx_map_class(map_file_path)
52
 
83
 
53
     def load(self) -> None:
84
     def load(self) -> None:
54
         self.load_graph_from_map(self.map_file_path)
85
         self.load_graph_from_map(self.map_file_path)
55
 
86
 
56
     def load_graph_from_map(self, map_file_path: str) -> None:
87
     def load_graph_from_map(self, map_file_path: str) -> None:
57
-        tmx_map = TMXMap(map_file_path)
58
-
59
         # TODO: tmx_map contient tout en cache, faire le dessous en exploitant tmx_map.
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
                 position = self.position_to_key((x, y))
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
                         continue
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
                         continue
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
 # coding: utf-8
1
 # coding: utf-8
2
+import typing
2
 from math import acos
3
 from math import acos
3
 from math import degrees
4
 from math import degrees
4
 from math import sqrt
5
 from math import sqrt
201
         degree,
202
         degree,
202
         DIRECTION_FROM_NORTH_DEGREES,
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