oakes / play-clj

A Clojure game library
The Unlicense
939 stars 73 forks source link

Tiled ObjectLayers not implemented correctly #15

Closed jroblak closed 10 years ago

jroblak commented 10 years ago

I was looking into spawning enemies based on objects placed via an object layer in tiled. It appears that when the tiled map is loaded, the object layer gets created as a MapLayer, not a TiledMapTileLayer.

Because of this, it's not possible to use the object layer via any of the play-clj methods. The following works:

(tiled-map-layer (-> main-screen :screen deref) "entities")

However, it doesn't return a TiledMapTileLayer per the docs, but a MapLayer. Thus, the following errors out:

(tiled-map-layer! (tiled-map-layer (-> main-screen :screen deref) "entities") :get-properties) 

[none of the TiledMapTileLayer methods work. error: Cannot cast com.badlogic.gdx.maps.MapLayer to com.badlogic.gdx.maps.tiled.TiledMapTileLayer]

Additionally it causes even more generic methods to fail, such as:

(tiled-map-layer-names (-> main-screen :screen deref))

which crashes once it gets to the object layer.

oakes commented 10 years ago

Yep, this is definitely a limitation of these functions, because they are designed to work on tile layers. I just added basic-map-layer and basic-map-layer!, which work on MayLayer objects instead of TiledMapTileLayer objects, so they can be used for object and image layers. Thanks for pointing this out! If you notice any other problems related to this, please let me know.

oakes commented 10 years ago

I renamed the macros to map-layer and map-layer! to keep the names closer to their LibGDX equivalents.

jroblak commented 10 years ago

So, building on this, I think wrappers would now be useful for: MapObject, CircleMapObject, EllipseMapObject, PolygonMapObject, PolylineMapObject, RectangleMapObject, and TextureMapObject

Although for now I guess these can just be manipulated via

(.x (.getRectangle object)))

I can look into potentially being able to implement these as they seem simple.

However I need to understand into whats going on under the hood a little more, though, as:

(flatten (pvalues
                (apply e/create-player player-images)
                (for [object (map-layer! (map-layer screen "entities") :get-objects)]
                  (apply e/create-baddy enemy-images))))))

works, while

(flatten (pvalues
                (apply e/create-player player-images)
                (for [object (map-layer! (map-layer screen "entities") :get-objects)]
                  (apply e/create-baddy (conj enemy-images (.getRectangle object))))))))

does not (this is keeping in mind that the e/create-baddy function has to change between the two)

oakes commented 10 years ago

I tried the above and .getRectangle worked fine for me. What error did you receive? If any of the objects returned by the map-layer! call aren't RectangleMapObject instances, it will fail.

I had some free time so I just added a wrapper for MapObject to the latest SNAPSHOT if you'd like to try it. It looks like this:

(doseq [object (map-objects (map-layer screen "test"))]
  (println (map-object! object :get-rectangle)))

I also renamed tiled-map-layers to map-layers, to make it clear that it returns all layers rather than only tile layers.

oakes commented 10 years ago

I forgot to mention, I also renamed tiled-map-layer-names to map-layer-names.

jroblak commented 10 years ago

Awesome!

Not sure on the error. Still getting the hang of debugging Clojure. I'm assuming it's something related to what you said, but the object layer only has one rectangle object on it in Tiled. Will have to look into it more, but pasting your code does print the one Rectangle object. So it must be how I'm passing it to the create function or something.

jroblak commented 10 years ago

The issue was that I assumed

(conj enemy-images (map-object! object :get-rectangle))

added the map object to the end of the vector, not the beginning. I've now got it working!

oakes commented 10 years ago

Yeah that's bitten me a few times. If enemy-images is a vector, it will be added to the end, and if it's a list, it will be added to the beginning. I believe conj does whatever is most efficient for the given data structure.