Skip to content

Commit 2d11951

Browse files
authored
Merge pull request #24 from thetrav/master
Flesh out support for objectgroups (object layers) and add support for image layers
2 parents 079acd8 + b3c2c00 commit 2d11951

File tree

2 files changed

+136
-101
lines changed

2 files changed

+136
-101
lines changed

pytiled_parser/objects.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,21 @@ class LayerGroup(Layer):
420420
layers: Optional[List[Union["LayerGroup", Layer, ObjectLayer]]]
421421

422422

423+
@attr.s(auto_attribs=True, kw_only=True)
424+
class ImageLayer(Layer):
425+
"""Image Layer.
426+
427+
An image layer displays a single image.
428+
429+
See: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#imagelayer
430+
431+
Attributes:
432+
image: the image to display for this layer.
433+
"""
434+
435+
image: Image
436+
437+
423438
@attr.s(auto_attribs=True)
424439
class TileSet:
425440
"""Object for storing a TSX with all associated collision data.

pytiled_parser/xml_parser.py

Lines changed: 121 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -261,53 +261,107 @@ def _parse_tiled_objects(
261261
tiled_objects: List[objects.TiledObject] = []
262262

263263
for object_element in object_elements:
264-
id_ = int(object_element.attrib["id"])
265-
location_x = float(object_element.attrib["x"])
266-
location_y = float(object_element.attrib["y"])
267-
location = objects.OrderedPair(location_x, location_y)
264+
my_object = _parse_object(object_element)
265+
if my_object is not None:
266+
tiled_objects.append(my_object)
268267

269-
tiled_object = objects.TiledObject(id_=id_, location=location)
268+
return tiled_objects
270269

271-
try:
272-
tiled_object.gid = int(object_element.attrib["gid"])
273-
except KeyError:
274-
tiled_object.gid = None
275270

276-
try:
277-
# If any dimension is provided, they both will be
278-
width = float(object_element.attrib["width"])
279-
height = float(object_element.attrib["height"])
280-
tiled_object.size = objects.Size(width, height)
281-
except KeyError:
282-
pass
271+
def _parse_object(obj):
272+
my_id = obj.attrib["id"]
273+
my_x = float(obj.attrib["x"])
274+
my_y = float(obj.attrib["y"])
275+
my_location = objects.OrderedPair(x=my_x, y=my_y)
276+
if "width" in obj.attrib:
277+
my_width = float(obj.attrib["width"])
278+
else:
279+
my_width = None
280+
if "height" in obj.attrib:
281+
my_height = float(obj.attrib["height"])
282+
else:
283+
my_height = None
284+
my_name = obj.attrib["name"]
283285

284-
try:
285-
tiled_object.opacity = float(object_element.attrib["opacity"])
286-
except KeyError:
287-
pass
286+
properties: Optional[objects.Properties]
287+
properties_element = obj.find("./properties")
288+
if properties_element is not None:
289+
properties = _parse_properties_element(properties_element)
290+
else:
291+
properties = None
288292

289-
try:
290-
tiled_object.rotation = float(object_element.attrib["rotation"])
291-
except KeyError:
292-
pass
293+
# This is where it would be nice if we could assume a walrus
294+
# operator was part of our Python distribution.
293295

294-
try:
295-
tiled_object.name = object_element.attrib["name"]
296-
except KeyError:
297-
pass
296+
my_object = None
298297

299-
try:
300-
tiled_object.type = object_element.attrib["type"]
301-
except KeyError:
302-
pass
298+
polygon = obj.findall("./polygon")
303299

304-
properties_element = object_element.find("./properties")
305-
if properties_element is not None:
306-
tiled_object.properties = _parse_properties_element(properties_element)
300+
if polygon and len(polygon) > 0:
301+
points = _parse_points(polygon[0].attrib["points"])
302+
my_object = objects.PolygonObject(
303+
id_=my_id,
304+
name=my_name,
305+
location=my_location,
306+
size=(my_width, my_height),
307+
points=points,
308+
properties=properties,
309+
)
307310

308-
tiled_objects.append(tiled_object)
311+
if my_object is None:
312+
polyline = obj.findall("./polyline")
313+
314+
if polyline and len(polyline) > 0:
315+
points = _parse_points(polyline[0].attrib["points"])
316+
my_object = objects.PolylineObject(
317+
id_=my_id,
318+
name=my_name,
319+
location=my_location,
320+
size=(my_width, my_height),
321+
points=points,
322+
properties=properties,
323+
)
324+
325+
if my_object is None:
326+
ellipse = obj.findall("./ellipse")
327+
328+
if ellipse and len(ellipse):
329+
my_object = objects.ElipseObject(
330+
id_=my_id,
331+
name=my_name,
332+
location=my_location,
333+
size=(my_width, my_height),
334+
properties=properties,
335+
)
336+
337+
if my_object is None:
338+
if "template" in obj.attrib:
339+
print(
340+
"Warning, this .tmx file is using an unsupported"
341+
"'template' attribute. Ignoring."
342+
)
343+
344+
if my_object is None:
345+
point = obj.findall("./point")
346+
if point:
347+
my_object = objects.PointObject(
348+
id_=my_id,
349+
name=my_name,
350+
location=my_location,
351+
properties=properties,
352+
)
353+
354+
if my_object is None:
355+
my_object = objects.RectangleObject(
356+
id_=my_id,
357+
name=my_name,
358+
location=my_location,
359+
size=(my_width, my_height),
360+
properties=properties,
361+
)
362+
363+
return my_object
309364

310-
return tiled_objects
311365

312366

313367
def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer:
@@ -349,6 +403,29 @@ def _parse_object_layer(element: etree.Element,) -> objects.ObjectLayer:
349403
)
350404

351405

406+
def _parse_image_layer(element: etree.Element,) -> objects.ImageLayer:
407+
"""Parse the imagelayer element given.
408+
409+
Args:
410+
element: Element to be parsed.
411+
412+
Returns:
413+
ImageLayer: The image layer object.
414+
"""
415+
id_, name, offset, opacity, properties = _parse_layer(element)
416+
417+
image = _parse_image_element(element.find("./image"))
418+
419+
return objects.ImageLayer(
420+
id_=id_,
421+
name=name,
422+
offset=offset,
423+
opacity=opacity,
424+
properties=properties,
425+
image=image,
426+
)
427+
428+
352429
def _parse_layer_group(element: etree.Element,) -> objects.LayerGroup:
353430
"""Parse the objectgroup element given.
354431
@@ -393,6 +470,8 @@ def _get_layer_parser(
393470
return _parse_object_layer
394471
if layer_tag == "group":
395472
return _parse_layer_group
473+
if layer_tag == "imagelayer":
474+
return _parse_image_layer
396475
return None
397476

398477

@@ -451,7 +530,7 @@ def _parse_points(point_string: str) -> List[objects.OrderedPair]:
451530
xys = str_pair.split(",")
452531
x = float(xys[0])
453532
y = float(xys[1])
454-
points.append((x, y))
533+
points.append(objects.OrderedPair(x, y))
455534

456535
return points
457536

@@ -531,69 +610,10 @@ def _parse_tiles(tile_element_list: List[etree.Element]) -> Dict[int, objects.Ti
531610
if objectgroup_element:
532611
objectgroup = []
533612
object_list = objectgroup_element.findall("./object")
534-
for object in object_list:
535-
my_id = object.attrib["id"]
536-
my_x = float(object.attrib["x"])
537-
my_y = float(object.attrib["y"])
538-
if "width" in object.attrib:
539-
my_width = float(object.attrib["width"])
540-
else:
541-
my_width = None
542-
if "height" in object.attrib:
543-
my_height = float(object.attrib["height"])
544-
else:
545-
my_height = None
546-
547-
# This is where it would be nice if we could assume a walrus
548-
# operator was part of our Python distribution.
549-
550-
my_object = None
551-
552-
polygon = object.findall("./polygon")
553-
554-
if polygon and len(polygon) > 0:
555-
points = _parse_points(polygon[0].attrib["points"])
556-
my_object = objects.PolygonObject(
557-
id_=my_id,
558-
location=(my_x, my_y),
559-
size=(my_width, my_height),
560-
points=points,
561-
)
562-
563-
if my_object is None:
564-
polyline = object.findall("./polyline")
565-
566-
if polyline and len(polyline) > 0:
567-
points = _parse_points(polyline[0].attrib["points"])
568-
my_object = objects.PolylineObject(
569-
id_=my_id,
570-
location=(my_x, my_y),
571-
size=(my_width, my_height),
572-
points=points,
573-
)
574-
575-
if my_object is None:
576-
ellipse = object.findall("./ellipse")
577-
578-
if ellipse and len(ellipse):
579-
my_object = objects.ElipseObject(
580-
id_=my_id, location=(my_x, my_y), size=(my_width, my_height)
581-
)
582-
583-
if my_object is None:
584-
if "template" in object.attrib:
585-
print(
586-
"Warning, this .tmx file is using an unsupported"
587-
"'template' attribute. Ignoring."
588-
)
589-
continue
590-
591-
if my_object is None:
592-
my_object = objects.RectangleObject(
593-
id_=my_id, location=(my_x, my_y), size=(my_width, my_height)
594-
)
595-
596-
objectgroup.append(my_object)
613+
for obj in object_list:
614+
my_object = _parse_object(obj)
615+
if my_object is not None:
616+
objectgroup.append(my_object)
597617

598618
# if this is None, then the Tile is part of a spritesheet
599619
image = None

0 commit comments

Comments
 (0)