Browse Source

Tmx map cache manager

Bastien Sevajol 6 years ago
parent
commit
f992a6693a

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


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


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

@@ -0,0 +1,18 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<tileset name="terrain" tilewidth="8" tileheight="8" spacing="1" tilecount="49" columns="7">
3
+ <image source="terrain.png" width="64" height="64"/>
4
+ <tile id="0">
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"/>
9
+  </properties>
10
+ </tile>
11
+ <tile id="1">
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"/>
16
+  </properties>
17
+ </tile>
18
+</tileset>

+ 4 - 2
sandbox/tile/run.py View File

@@ -24,10 +24,12 @@ def main(map_dir_path: str, seed_value: int=42):
24 24
     seed(seed_value)
25 25
 
26 26
     config = Config()
27
-    config.load_files(['sandbox/engulf/config.yaml'])
27
+    config.load_files(['sandbox/engulf/config.yaml'])  # TODO: heu ... engulf ??
28 28
     logger = get_default_logger(level=logging.ERROR)
29 29
 
30
-    simulation = TileStrategySimulation(config)
30
+    map_file_path = 'sandbox/tile/{}.tmx'.format(os.path.join(map_dir_path, os.path.basename(map_dir_path)))
31
+
32
+    simulation = TileStrategySimulation(config, map_file_path=map_file_path)
31 33
     subjects = TileStrategySubjects(simulation=simulation)
32 34
 
33 35
     for position in ((0, 0), (5, 3), (10, 6)):

+ 16 - 0
sandbox/tile/simulation/base.py View File

@@ -1,4 +1,6 @@
1 1
 # coding: utf-8
2
+from synergine2.config import Config
3
+from synergine2_xyz.physics import Physics, TMXPhysics
2 4
 from synergine2_xyz.simulation import XYZSimulation
3 5
 from synergine2_xyz.subjects import XYZSubjects
4 6
 from synergine2_xyz.subjects import XYZSubject
@@ -9,6 +11,20 @@ class TileStrategySimulation(XYZSimulation):
9 11
 
10 12
     ]
11 13
 
14
+    def __init__(
15
+        self,
16
+        config: Config,
17
+        map_file_path: str,
18
+    ) -> None:
19
+        self.map_file_path = map_file_path
20
+        super().__init__(config)
21
+
22
+    def create_physics(self) -> Physics:
23
+        return TMXPhysics(
24
+            config=self.config,
25
+            map_file_path=self.map_file_path,
26
+        )
27
+
12 28
 
13 29
 class TileStrategySubjects(XYZSubjects):
14 30
     pass

+ 4 - 0
synergine2/exceptions.py View File

@@ -9,3 +9,7 @@ class NotYetImplemented(SynergineException):
9 9
     """
10 10
     Like of NotImplementError. Use it to declare method to implement but only if wanted.
11 11
     """
12
+
13
+
14
+class ConfigurationError(SynergineException):
15
+    pass

+ 1 - 0
synergine2_xyz/exception.py View File

@@ -0,0 +1 @@
1
+# coding: utf-8

+ 77 - 0
synergine2_xyz/map.py View File

@@ -0,0 +1,77 @@
1
+# coding: utf-8
2
+import typing
3
+
4
+import tmx
5
+
6
+
7
+class UnconstructedTile(object):
8
+    pass
9
+
10
+
11
+class TMXMap(object):
12
+    def __init__(self, map_file_path: str) -> None:
13
+        self.tmx_map = tmx.TileMap.load(map_file_path)
14
+        self.tmx_layers = {}  # type: typing.Dict[str, tmx.Layer]
15
+        self.tmx_layer_tiles = {}  # type: typing.Dict[str, typing.Dict[str, tmx.Tile]]
16
+        self.tmx_tilesets = {}  # type: typing.Dict[str, tmx.Tileset]
17
+        self.tmx_tiles = {}  # type: typing.Dict[int, tmx.Tile]
18
+        self._load()
19
+
20
+    @property
21
+    def height(self) -> int:
22
+        return self.tmx_map.height
23
+
24
+    @property
25
+    def width(self) -> int:
26
+        return self.tmx_map.width
27
+
28
+    def _load(self) -> None:
29
+        self._load_layers()
30
+        self._load_tilesets()
31
+        self._load_tiles()
32
+
33
+    def _load_layers(self) -> None:
34
+        for layer in self.tmx_map.layers:
35
+            self.tmx_layers[layer.name] = layer
36
+
37
+    def _load_tilesets(self) -> None:
38
+        for tileset in self.tmx_map.tilesets:
39
+            self.tmx_tilesets[tileset.name] = tileset
40
+            for tile in tileset.tiles:
41
+                self.tmx_tiles[tileset.firstgid + tile.id] = tile
42
+
43
+    def _load_tiles(self) -> None:
44
+        for layer_name, layer in self.tmx_layers.items():
45
+            x, y = -1, -1
46
+            self.tmx_layer_tiles[layer_name] = {}
47
+
48
+            # no tiles
49
+            if not isinstance(layer, tmx.Layer):
50
+                continue
51
+
52
+            for layer_tile in layer.tiles:
53
+                x += 1
54
+
55
+                if x == self.width:
56
+                    x = 0
57
+                    y += 1
58
+
59
+                position_key = '{}.{}'.format(x, y)
60
+
61
+                if not layer_tile.gid:
62
+                    tile = None
63
+                elif not layer_tile.gid in self.tmx_tiles:
64
+                    tile = UnconstructedTile()
65
+                else:
66
+                    tile = self.tmx_tiles[layer_tile.gid]
67
+
68
+                self.tmx_layer_tiles[layer_name][position_key] = tile
69
+
70
+    def layer(self, name: str) -> tmx.Layer:
71
+        return self.tmx_layers[name]
72
+
73
+    def tileset(self, name: str) -> tmx.Tileset:
74
+        return self.tmx_tilesets[name]
75
+
76
+    def tile(self, gid: int) -> tmx.Tile:
77
+        return self.tmx_tiles[gid]

+ 4 - 12
synergine2_xyz/move.py View File

@@ -1,8 +1,6 @@
1 1
 # coding: utf-8
2 2
 import typing
3 3
 
4
-from dijkstar import find_path
5
-
6 4
 from synergine2.config import Config
7 5
 from synergine2.simulation import SimulationBehaviour
8 6
 from synergine2.simulation import SubjectBehaviour
@@ -66,16 +64,10 @@ class MoveToMechanism(SubjectMechanism):
66 64
             new_path = None
67 65
 
68 66
             if not move.path:
69
-                # TODO: Must be XYZSimulation !
70
-                start = '{}.{}'.format(*self.subject.position)
71
-                end = '{}.{}'.format(*move.move_to)
72
-
73
-                found_path = find_path(self.simulation.graph, start, end)
74
-                move.path = []
75
-
76
-                for position in found_path[0][1:]:
77
-                    x, y = map(int, position.split('.'))
78
-                    move.path.append((x, y))
67
+                move.path = self.simulation.physics.found_path(
68
+                    start=self.subject.position,
69
+                    end=move.move_to,
70
+                )
79 71
 
80 72
                 # Note: We are in process, move change will be lost
81 73
                 new_path = move.path

+ 90 - 0
synergine2_xyz/physics.py View File

@@ -0,0 +1,90 @@
1
+# coding: utf-8
2
+import typing
3
+
4
+import tmx
5
+from dijkstar import Graph
6
+from dijkstar import find_path
7
+
8
+from synergine2.config import Config
9
+from synergine2_xyz.map import TMXMap
10
+from synergine2_xyz.tmx_utils import get_layer_by_name
11
+
12
+
13
+class Physics(object):
14
+    def __init__(
15
+        self,
16
+        config: Config,
17
+    ) -> None:
18
+        self.config = config
19
+        self.graph = Graph()
20
+
21
+    def load(self) -> None:
22
+        raise NotImplementedError()
23
+
24
+    def position_to_key(self, position: typing.Tuple[int, int]) -> str:
25
+        return '{}.{}'.format(*position)
26
+
27
+    def found_path(
28
+        self,
29
+        start: typing.Tuple[int, int],
30
+        end: typing.Tuple[int, int],
31
+    ) -> typing.List[typing.Tuple[int, int]]:
32
+        start_key = self.position_to_key(start)
33
+        end_key = self.position_to_key(end)
34
+
35
+        found_path = find_path(self.graph, start_key, end_key)
36
+        regular_path = []
37
+        for position in found_path[0][1:]:
38
+            x, y = map(int, position.split('.'))
39
+            regular_path.append((x, y))
40
+
41
+        return regular_path
42
+
43
+
44
+class TMXPhysics(Physics):
45
+    def __init__(
46
+        self,
47
+        config: Config,
48
+        map_file_path: str,
49
+    ) -> None:
50
+        super().__init__(config)
51
+        self.map_file_path = map_file_path
52
+
53
+    def load(self) -> None:
54
+        self.load_graph_from_map(self.map_file_path)
55
+
56
+    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.
60
+        for y in range(tmx_map.height):
61
+            for x in range(tmx_map.width):
62
+                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:
84
+                        continue
85
+
86
+                    if neighbor_y > 69 or neighbor_y < 0:
87
+                        continue
88
+
89
+                    # TODO: Voir https://pypi.python.org/pypi/Dijkstar/2.2
90
+                    self.graph.add_edge(position, neighbor, 1)

+ 5 - 37
synergine2_xyz/simulation.py View File

@@ -1,10 +1,7 @@
1 1
 # coding: utf-8
2
-import typing
3
-
4
-from dijkstar import Graph
5
-
6 2
 from synergine2.config import Config
7 3
 from synergine2.simulation import Simulation as BaseSimulation
4
+from synergine2_xyz.physics import Physics
8 5
 from synergine2_xyz.subjects import XYZSubjects
9 6
 from synergine2_xyz.subjects import XYZSubject
10 7
 
@@ -17,41 +14,12 @@ class XYZSimulation(BaseSimulation):
17 14
         config: Config,
18 15
     ) -> None:
19 16
         super().__init__(config)
20
-        self.graph = Graph()
21
-
22
-        # TODO: Le graph devra être calculé à partir de données comme tmx
23
-        for y in range(40):
24
-            for x in range(70):
25
-                position = '{}.{}'.format(x, y)
26
-                neighbors = []
27
-
28
-                for modifier_x, modifier_y in (
29
-                    (+1, +1),
30
-                    (+1, +0),
31
-                    (+1, -1),
32
-                    (+0, -1),
33
-                    (-1, -1),
34
-                    (-1, +0),
35
-                    (-1, -1),
36
-                    (+0, +1),
37
-                ):
38
-                    try:
39
-                        neighbors.append('{}.{}'.format(x+modifier_x, y+modifier_y))
40
-                    except ValueError:
41
-                        pass
42
-
43
-                for neighbor in neighbors:
44
-                    neighbor_x, neighbor_y = map(int, neighbor.split('.'))
45
-
46
-                    if neighbor_x > 69 or neighbor_x < 0:
47
-                        continue
48 17
 
49
-                    if neighbor_y > 69 or neighbor_y < 0:
50
-                        continue
18
+        self.physics = self.create_physics()
19
+        self.physics.load()
51 20
 
52
-                    # TODO: Voir https://pypi.python.org/pypi/Dijkstar/2.2
53
-                    self.graph.add_edge(position, neighbor, 1)
54
-        pass
21
+    def create_physics(self) -> Physics:
22
+        raise NotImplementedError()
55 23
 
56 24
     def is_possible_subject_position(self, subject: XYZSubject, position: tuple) -> bool:
57 25
         return self.is_possible_position(position)

+ 13 - 0
synergine2_xyz/tmx_utils.py View File

@@ -0,0 +1,13 @@
1
+# coding: utf-8
2
+from tmx import TileMap
3
+from tmx import Layer
4
+
5
+from synergine2.exceptions import ConfigurationError
6
+
7
+
8
+def get_layer_by_name(map_: TileMap, layer_name: str) -> Layer:
9
+    for layer in map_.layers:
10
+        if layer.name == layer_name:
11
+            return layer
12
+
13
+    raise ConfigurationError('No layer named "{}" in map')