Android Question LibGDX - Load Tiled Object layer and render polylines

notedop

Member
Licensed User
Longtime User
Hi,

I'm trying to combine tiledmaps generated with Tiled with a BOX2D world. I'd like to generate the ground polylines within Tiled and load the polylines from the object layer by code.
The goal is to render this and together with the map but via 2 render methods. I;m not sure if the thing i'd like to achieve is even possible.

If it is possible it will become very easy to set the boundaries of the level and applying physics additional generated objects.

I have the following code to load the level:

B4X:
'Class module
Sub Class_Globals

    Private Maps As lgMapTiledMap
    Private MapRenderer As lgMapOrthogonalTiledMapRenderer
    Dim Box2Drenderer As lgBox2DDebugRenderer
    Dim CurrentLayer As lgMapTiledMapLayer
    Dim MapWidth, MapHeight As Int 'in tiles
    Dim TileSize As Float 'in pixels
    Dim World As lgBox2DWorld
    'Dim WorldGround As lgMapPolylineMapObject
    Dim MapObject As lgMapObject
    Dim bdyGround As lgBox2DBody

End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(Filename As String, Width As Int, Height As Int, GutterSize As Float)

    'Create the Box2D physics world
    Log("Begin map loading")

    Dim vGravity As lgMathVector2
    World.Initialize(vGravity.set(0, -10), True, "Box2D")
    World.SetContinuousPhysics(True)
    World.SetWarmStarting(True)

    loadmap(Filename)

    MapWidth = Maps.Properties.get("width")
    MapHeight = Maps.Properties.get("height")
    Log ("MapWidth:" & MapWidth)
    Log ("MapHeight:" & MapHeight)

    Dim TileWidth As Int = 20
    Dim TileHeight As Int = 20
    Dim RatioW As Float = Width / MapWidth

    Dim RatioH As Float = Height / MapHeight

    'Log("ratioW : "& RatioW )
    Log("ratioh : "& RatioH )
    If RatioW < RatioH Then
        MapRenderer.Initialize3(Maps, RatioH )
        TileSize = MapRenderer.UnitScale * TileWidth
    Else
        MapRenderer.Initialize3(Maps, RatioW)
        TileSize = MapRenderer.UnitScale * TileHeight
    End If
    Log("map loaded... now creating ground")

    Create_ground

End Sub

'Each layer is a different maze
Public Sub SelectLayer(Layer As Int)
    CurrentLayer = Maps.Layers.get(Layer)
End Sub

Public Sub FromTileToPixel_X(TileX As Int) As Int
    Return Round((TileX - 0.5) * TileSize)
End Sub

Public Sub FromTileToPixel_Y(TileY As Int) As Int
    Return Round((TileY - 0.5) * TileSize)
End Sub

Public Sub FromTileToPixel_InvertedY(TileY As Int) As Int
    Return Round((MapHeight - 1.5 - TileY) * TileSize)
End Sub

Public Sub FromPixelToTile_X(PixelX As Int) As Int
    Return Round((PixelX / TileSize) + 0.5)
End Sub

Public Sub FromPixelToTile_Y(PixelY As Int) As Int
    Return Round((PixelY / TileSize) + 0.5)
End Sub

'Gets the tile at the given coordinates
Public Sub GetTile(TileX As Int, TileY As Int) As lgMapStaticTiledMapTile
    Dim Cell As lgMapTiledMapLayerCell = CurrentLayer.GetCell(TileX, TileY)
    If Cell = Null Then
        Return Null
    Else
        Return Cell.Tile
    End If
End Sub

Private Sub loadmap(Filename As String)
    'Loads the mazes (TMX format)
    Dim TMXLoader As lgMapTmxMapLoader
    Maps = TMXLoader.Initialize2("maps/"& Filename )
End Sub

Public Sub Reload

    Maps.Dispose
    loadmap("naamloos.tmx")

    If MapRenderer.IsInitialized Then
        MapRenderer.Map= Maps
    Else
        MapRenderer.Initialize(Maps)
    End If

End Sub

'Draws the current layer
Public Sub Draw(Camera As lgOrthographicCamera)

    World.Step(1/60, 8, 3)

    MapRenderer.SetCameraView(Camera)
    MapRenderer.render()

    Box2Drenderer.render(World.InternalObject,Camera.Combined)

End Sub

Public Sub Dispose
    Maps.Dispose
    MapRenderer.Dispose
End Sub

Public Sub Create_ground

    'get the mapground polyline from the maps and transpose to a fixture definition

    Dim WorldGround As lgMapPolylineMapObject = Maps.Layers.Get2("ObjTest").Objects.Get2("MapGround")
    Dim eshape As lgBox2DPolygonShape

    eshape.Set2(WorldGround.Polyline.TransformedVertices)

    Dim fd As lgBox2DFixtureDef

    'now we have the fixture
    fd.shape = eshape

    Dim bd As lgBox2DBodyDef

    bdyGround = World.CreateBody(bd)
    bdyGround.createFixture(fd)
    eshape.dispose
    bdyGround.Position.Set(WorldGround.Polyline.OriginX,WorldGround.Polyline.OriginY)

End Sub

Sub Box2D_BeginContact(Contact As lgBox2DContact)
    'The ground is discarded
    If Contact.FixtureA.Body <> bdyGround AND Contact.FixtureB.Body <> bdyGround Then
        'The Contact instance is going to be reused, so its data have to be copied
        Log("BeginContact")
    End If
End Sub

I know the code is somewhat messed up, this is because i'm just testing and learning capabilities of libgdx. Feel free to comment on better performance or methods.

The error i'm currently gettign is:

B4X:
OGL version: OpenGL ES 2.0 2184622
OGL extensions: GL_AMD_compressed_ATC_texture GL_AMD_performance_monitor GL_AMD_program_binary_Z400 GL_EXT_texture_filter_anisotropic GL_EXT_texture_format_BGRA8888 GL_EXT_texture_type_2_10_10_10_REV GL_NV_fence GL_OES_compressed_ETC1_RGB8_texture GL_OES_depth_texture GL_OES_depth24 GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_element_index_uint GL_OES_fbo_render_mipmap GL_OES_fragment_precision_high GL_OES_get_program_binary GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_OES_standard_derivatives GL_OES_texture_3D GL_OES_texture_float GL_OES_texture_half_float GL_OES_texture_half_float_linear GL_OES_texture_npot GL_OES_vertex_half_float GL_OES_vertex_type_10_10_10_2 GL_OES_vertex_array_object GL_QCOM_alpha_test GL_QCOM_binning_control GL_QCOM_driver_control GL_QCOM_perfmon_global_mode GL_QCOM_extended_get GL_QCOM_extended_get2 GL_QCOM_tiled_rendering GL_QCOM_writeonly_rendering GL_AMD_compressed_3DC_texture
framebuffer: (5, 6, 5, 0)


depthbuffer: (16)
stencilbuffer: (0)
samples: (0)
coverage sampling: (false)
Managed meshes/app: { }


Managed textures/app: { }


Managed shaders/app: { }
Managed buffers/app: { }
Begin map loading


MapWidth:3000


MapHeight:600


ratioh : 1.3333333730697632
map loaded... now creating ground


Width in Resize= 480


Height in Resize= 800


java.lang.NullPointerException


    at com.badlogic.gdx.physics.box2d.Box2DDebugRenderer.render(SourceFile:94)
    at flm.b4a.libgdxtest.level2._draw(level2.java:119)
    at flm.b4a.libgdxtest.main._lg_render(main.java:437)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:163)
    at anywheresoftware.b4a.libgdx.LibGDX$b.render(SourceFile:118)
    at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(SourceFile:438)
    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1470)
    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1224)
java.lang.NullPointerException


java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()


    at android.os.Handler.<init>(Handler.java:121)
    at android.app.Dialog.<init>(Dialog.java:127)
    at android.app.AlertDialog.<init>(AlertDialog.java:114)
    at android.app.AlertDialog$Builder.create(AlertDialog.java:913)
    at anywheresoftware.b4a.BA.ShowErrorMsgbox(BA.java:222)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:202)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:163)
    at anywheresoftware.b4a.libgdx.LibGDX$b.render(SourceFile:118)
    at com.badlogic.gdx.backends.android.AndroidGraphics.onDrawFrame(SourceFile:438)
    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1470)
    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1224)
** Activity (main) Pause, UserClosed = true **


waiting for pause synchronization took too long; assuming deadlock and killing
 

Attachments

  • test02.zip
    14.4 KB · Views: 166
  • test03.zip
    14.9 KB · Views: 189
Last edited:

notedop

Member
Licensed User
Longtime User
AAAARRGGGG, forgot to initialize the Box2Drenderer. I've now added this and it now works.
However; the line is showing as a polygonshape, not a line. So i've adjusted the eshape to a
lgBox2DEdgeShape

How can I convert the
lgMapPolylineMapObject
to a lgmatchvector2 object so I can load it's vertices?


B4X:
Public Sub Create_ground
   
    'get the mapground polyline from the maps and transpose to a fixture definition

    Dim WorldGround As lgMapPolylineMapObject = Maps.Layers.Get2("ObjTest").Objects.Get2("MapGround")
    Dim eshape As lgBox2DEdgeShape

    eshape.SetVertex0(WorldGround.Polyline.TransformedVertices)
    
    Dim fd As lgBox2DFixtureDef
   
    'now we have the fixture
    fd.shape = eshape
   
    Dim bd As lgBox2DBodyDef
   
    bdyGround = World.CreateBody(bd)
    bdyGround.createFixture(fd)
    eshape.dispose
    bdyGround.Position.Set(WorldGround.Polyline.OriginX,WorldGround.Polyline.OriginY)
   
End Sub
 
Upvote 0

notedop

Member
Licensed User
Longtime User
And got it again! Damn, everytime I start asking questions I somehow find out how to do it minutes after posting. Been struggling for a few hours now before I posted my Original post.
Tsjesus....


This gives the Tiled map usage sooo much more power as you do not have to rely on collision detection with the tiles, which are square. This way you can get nice angles etc to your developments and you don't have to code the polylines as you can use Tiled for it. Awesome!

My next challenge will be to load sprites with polygons from the Physics body editor. My goal is to generate a vertex for every frame for superior collision detection.
In case this has already been done, please do share :)

B4X:
Public Sub Create_ground
  
    'get the mapground polyline from the maps and transpose to a fixture definition

    Dim WorldGround As lgMapPolylineMapObject = Maps.Layers.Get2("ObjTest").Objects.Get2("MapGround")
    Dim eshape As lgBox2DChainShape

    eshape.createChain(WorldGround.Polyline.TransformedVertices)
   
    Dim fd As lgBox2DFixtureDef
  
    'now we have the fixture
    fd.shape = eshape
  
    Dim bd As lgBox2DBodyDef
  
    bdyGround = World.CreateBody(bd)
    bdyGround.createFixture(fd)
    eshape.dispose
    bdyGround.Position.Set(WorldGround.Polyline.OriginX,WorldGround.Polyline.OriginY)
  
End Sub
 
Last edited:
Upvote 0

notedop

Member
Licensed User
Longtime User
I'm having issues with the scaling of the poliline. It's not showing correctly. Due to this it does not follow the mapground as it should be (visible in Tiled when you open the map)

I've done some research and found the following javacode by Dermetfan;
https://bitbucket.org/dermetfan/som...dx/box2d/Box2DMapObjectParser.java?at=default

I've tried to implement this however the scaling is still not correct. I think it's a matter of the position and scale value, however how to determine both correctly is a challenge for me.

B4X:
Public Sub Initialize(World AslgBox2DWorld, Filename AsString, Width AsInt, Height AsInt, GutterSize AsFloat)
'Create the Box2D physics world
Log("Begin map loading")
Box2Drenderer.Initialize2(True, False, False, True, False, True)
loadmap(Filename)
MapWidth = Maps.Properties.get("width")
MapHeight = Maps.Properties.get("height")
Log ("MapWidth:" & MapWidth)
Log ("MapHeight:" & MapHeight)
Dim TileWidth AsInt = 20
Dim TileHeight AsInt = 20
RatioW = Width / MapWidth
RatioH = Height / MapHeight
ScaleW =MapWidth/Width
ScaleH = MapHeight/Height
'Log("ratioW : "& RatioW )
Log("ratioh : "& RatioH )
IfRatioW < RatioHThen
MapRenderer.Initialize3(Maps, RatioH)
scale=RatioW
TileSize = MapRenderer.UnitScale * TileWidth
Else
MapRenderer.Initialize3(Maps, RatioW)
scale=RatioH
TileSize = MapRenderer.UnitScale * TileHeight
EndIf
Log("map loaded... now creating ground. Unitscale: " & MapRenderer.UnitScale)
Create_ground(World)
End Sub


Public Sub Create_ground(World As lgBox2DWorld)
   
    'create body definition
    Dim bd As lgBox2DBodyDef
    bd.Type = World.BODYTYPE_Static
    bd.position.Set(0,0)
   
    'pull poliline from map
   
    Dim WorldGround As lgMapPolylineMapObject = Maps.Layers.Get2("ObjTest").Objects.Get2("MapGround")
    WorldGround.Polyline.setPosition(WorldGround.Polyline.x * ScaleW - bd.position.x, WorldGround.Polyline.y * ScaleH - bd.position.y)
    WorldGround.Polyline.setScale(ScaleW, ScaleH)
    Log ("Scale: " & ScaleW & ":" & ScaleH)
   
    'create shape
    Dim eshape As lgBox2DChainShape
    eshape.createChain(WorldGround.Polyline.TransformedVertices)

    'now we have the fixture
    Dim fd As lgBox2DFixtureDef
    fd.shape = eshape
    fd.restitution = 0
    fd.friction = 0.25
   
    World.CreateBody(bd).createFixture(fd)
   
    eshape.Dispose   

End Sub
 

Attachments

  • test03.zip
    14.9 KB · Views: 182
Upvote 0

notedop

Member
Licensed User
Longtime User
Yep, I believe i have found the issue. It's how Tiled processes changes made to a polyline.

I'll first need to run some test, but it seems that Tiled doesn't update the X,Y co-ordinate of the origin vertex correctly when changing an existing polyline. This caused the polyline to have a different origin point, which would cause positioning issues.

In the Test03.zip example you can see the X,Y of the origin vertex is X = 1014,67 and Y = 541,33.
But looking at what has been drawn to the Tiled screen, it clearly shows that the origin vertex starts at X=0 Y=541,33.

I've replicated this issue by drawing a polyline in Tiled, hitting enter to complete it and re-positioning ONLY the origin vertex. This would mean the polyline gets longer or smaller.
The X/Y does not get updated.
If I would move the FULL polyline, then the X/Y does get updated.

I will redraw the line today and see if this also solves my scaling issues, which now most likely seems to be rather a positioning issue.
 
Upvote 0

notedop

Member
Licensed User
Longtime User
yep, I finally got it. Scaling and positioning is now correct.
B4X:
    Dim bd As lgBox2DBodyDef
    bd.Type = World.BODYTYPE_Static
 
 
    'pull poliline from map
 
    Dim WorldGround As lgMapPolylineMapObject = Maps.Layers.Get2("ObjTest").Objects.Get2("MapGround")
    WorldGround.Polyline.setPosition(WorldGround.Polyline.X*scale, WorldGround.Polyline.y*scale)
    WorldGround.Polyline.setScale(scale, scale)

    'create shape
    Dim eshape As lgBox2DChainShape
    eshape.createChain(WorldGround.Polyline.TransformedVertices)

    'now we have the fixture
    Dim fd As lgBox2DFixtureDef
    fd.shape = eshape
    fd.restitution = 0
    fd.friction = 0.25
 
    World.CreateBody(bd).createFixture(fd)
 
    eshape.Dispose

So the issue was with TILED as described above. Due to this I was under the impression that I was not scaling correct but it was not the scaling but positioning. Then I started to look everywhere to correct the issue and messing up my code very badly...

Attached is an example of replicated issue.
1st line I draw from X=0 to the right.
2nd line I draw from X= 158 to the right, approx half the size compared to the 1st line.

Then i've edited the 2nd line and re-positioned the vertex from X= 158 to X=0. Not by editing the properties but by expanding it with the mouse.
The 2nd line is now the same size as the 1st Line but the X position in the Properties is not updated with the new X value
 

Attachments

  • Tiled issue.zip
    3.5 KB · Views: 198
Upvote 0

notedop

Member
Licensed User
Longtime User
I'm using Tiled 0.10.1 which seems to be the latest version available. I briefly checked the buglist and it its on there, however it was resolved as expected behaviour. Rather strange.... Anyhow, i'll log a bugfix request when i've got some additional time on hand, just to make it clear it's anoying behaviour of the program :eek:
 
Upvote 0
Top