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

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
     seed(seed_value)
24
     seed(seed_value)
25
 
25
 
26
     config = Config()
26
     config = Config()
27
-    config.load_files(['sandbox/engulf/config.yaml'])
27
+    config.load_files(['sandbox/engulf/config.yaml'])  # TODO: heu ... engulf ??
28
     logger = get_default_logger(level=logging.ERROR)
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
     subjects = TileStrategySubjects(simulation=simulation)
33
     subjects = TileStrategySubjects(simulation=simulation)
32
 
34
 
33
     for position in ((0, 0), (5, 3), (10, 6)):
35
     for position in ((0, 0), (5, 3), (10, 6)):

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

1
 # coding: utf-8
1
 # coding: utf-8
2
+from synergine2.config import Config
3
+from synergine2_xyz.physics import Physics, TMXPhysics
2
 from synergine2_xyz.simulation import XYZSimulation
4
 from synergine2_xyz.simulation import XYZSimulation
3
 from synergine2_xyz.subjects import XYZSubjects
5
 from synergine2_xyz.subjects import XYZSubjects
4
 from synergine2_xyz.subjects import XYZSubject
6
 from synergine2_xyz.subjects import XYZSubject
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
 class TileStrategySubjects(XYZSubjects):
29
 class TileStrategySubjects(XYZSubjects):
14
     pass
30
     pass

+ 4 - 0
synergine2/exceptions.py View File

9
     """
9
     """
10
     Like of NotImplementError. Use it to declare method to implement but only if wanted.
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

1
+# coding: utf-8

+ 77 - 0
synergine2_xyz/map.py View File

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
 # coding: utf-8
1
 # coding: utf-8
2
 import typing
2
 import typing
3
 
3
 
4
-from dijkstar import find_path
5
-
6
 from synergine2.config import Config
4
 from synergine2.config import Config
7
 from synergine2.simulation import SimulationBehaviour
5
 from synergine2.simulation import SimulationBehaviour
8
 from synergine2.simulation import SubjectBehaviour
6
 from synergine2.simulation import SubjectBehaviour
66
             new_path = None
64
             new_path = None
67
 
65
 
68
             if not move.path:
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
                 # Note: We are in process, move change will be lost
72
                 # Note: We are in process, move change will be lost
81
                 new_path = move.path
73
                 new_path = move.path

+ 90 - 0
synergine2_xyz/physics.py View File

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
 # coding: utf-8
1
 # coding: utf-8
2
-import typing
3
-
4
-from dijkstar import Graph
5
-
6
 from synergine2.config import Config
2
 from synergine2.config import Config
7
 from synergine2.simulation import Simulation as BaseSimulation
3
 from synergine2.simulation import Simulation as BaseSimulation
4
+from synergine2_xyz.physics import Physics
8
 from synergine2_xyz.subjects import XYZSubjects
5
 from synergine2_xyz.subjects import XYZSubjects
9
 from synergine2_xyz.subjects import XYZSubject
6
 from synergine2_xyz.subjects import XYZSubject
10
 
7
 
17
         config: Config,
14
         config: Config,
18
     ) -> None:
15
     ) -> None:
19
         super().__init__(config)
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
     def is_possible_subject_position(self, subject: XYZSubject, position: tuple) -> bool:
24
     def is_possible_subject_position(self, subject: XYZSubject, position: tuple) -> bool:
57
         return self.is_possible_position(position)
25
         return self.is_possible_position(position)

+ 13 - 0
synergine2_xyz/tmx_utils.py View File

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')