Android Code Snippet LibGdX - Box2D animated sprite with Physics body editor

I wanted to have great collision detection so I created this class.

Setup:
First make sure you have a sprite animation and seperate each frame in a seperate file.
Then load all the images into Physics body editor
Draw all required polygons
Save the project into your B4A files folder and make sure to include the images.
You can double check the JSON file to see if the image filepath is not screwed.

Then load the json file with below code.

This is a work in progress and will be made more generic in the future, but it will give you an idea how it works.

Basically it uses the lgBox2DBodyEditorLoader and based on the deltatime it draws the current frame.
It removes all the fixtures from the body and loads the fixture belonging to the current frame to the body.

Feel free to comment and/or provide optimisation tips.

Note:
Main.P2M is my pixel to meter value.

B4X:
'Class module
Sub Class_Globals

    Dim Batch As lgSpriteBatch
    Dim Bodie As lgBox2DBody
    Dim Textures(6) As lgTexture
    Dim Sprites(6) As lgSprite
    Dim scale As Float = 0.5
    Dim Conversion As lgMathUtils
    Dim Loader As lgBox2DBodyEditorLoader
  
    Dim fd As lgBox2DFixtureDef
  
    Dim StateTime As Float = 0
    Dim TimePerFrame As Float = 0.1
    Dim CurrentFrame As Float = 0
    Dim TotalFrames As Float = 6
    Dim L As List
  
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(world As lgBox2DWorld)

    'add duration of the total sprite duration or time per frame.

    Batch.Initialize

    Log ("scaling = " & scale)
  
    Loader.InitializeWithFile("actors/plane.json")
    Dim L As List = Loader.SortedList

    '1. Create a BodyDef, as usual:
    Dim bd As lgBox2DBodyDef
    bd.Position.Set(5, 5)
    bd.Type = world.BODYTYPE_Kinematic
  
    '2. Create a FixtureDef, as usual:

    fd.Density = 1
    fd.Friction = 0.5
    fd.Restitution = 0.2
  
    '3. Create a Body, as usual:
    Bodie = world.CreateBody(bd)
  
  
    '5. Load the associated image:
    '    First load the images as we need it's width to set the scaling in the bodyfixture.
  
    For i = 0 To L.Size -1
        Textures(i).Initialize("actors/" & Loader.GetImagePath(L.Get(i)))
        Sprites(i).InitializeWithTexture(Textures(i))
        Sprites(i).SetSize(Sprites(i).Width/Main.P2M, (Sprites(i).Width/Main.P2M) * (Sprites(i).Height / Sprites(i).Width) )
        Dim Origin As lgMathVector2
        Origin = Loader.GetOrigin(L.Get(i), Sprites(i).Width)
        Sprites(i).SetOrigin(Origin.X, Origin.Y)
    Next
  
    '4. Create the body fixture automatically by using the Loader, only first occurance.
    Loader.AttachFixture(Bodie, L.Get(0), fd, Sprites(0).Width)

    Log ("List: " & L)

End Sub

Sub draw(camera As lgOrthographicCamera,world As lgBox2DWorld, deltatime As Float)
  
    camera.update
  
    'deltatime is the time spent since last draw.
    'first define which frame we should show
  
    StateTime = StateTime + deltatime
  
    If StateTime > TimePerFrame Then
        If CurrentFrame < TotalFrames-1  Then
            CurrentFrame = CurrentFrame +1
            StateTime = 0
        Else
            CurrentFrame = 0
            StateTime = 0
        End If
    End If
  
    'destroy the fixtures belonging to the previous frame.
    Dim arr As lgArray
    Bodie.GetFixtureList(arr)
    Log("Fixturelist: " & arr.Size)
  
  
    For Each f As lgBox2DFixture In arr.toList
        Bodie.destroyFixture(f)
    Next
  
    'attach new fixture based on current frame.
    Loader.AttachFixture(Bodie, L.Get(CurrentFrame), fd, Sprites(CurrentFrame).Width)
  
    'update positions.
    update
  
    Batch.ProjectionMatrix = camera.Combined
    Batch.Begin
        Sprites(CurrentFrame).Draw(Batch)
    Batch.End

End Sub

Sub update()

'create logic to disable/enable bodies based on the deltatime, statetime and duration of the total sprite.

'world.Step(1/60, 8, 3)

For i = 0 To Sprites.Length - 1
    Sprites(i).X = Bodie.Position.X - Sprites(i).OriginX
    Sprites(i).Y = Bodie.Position.Y - Sprites(i).OriginY
    Sprites(i).Rotation = Bodie.Angle * Conversion.radiansToDegrees
Next

move


End Sub

Sub move()

'create move code
Bodie.setTransform2(Main.camera.Position.x, Main.camera.Position.y, 0)


End Sub
 
Last edited:

ilan

Expert
Licensed User
Longtime User
I wanted to have great collision detection so I created this class.

Setup:
First make sure you have a sprite animation and seperate each frame in a seperate file.
Then load all the images into Physics body editor
Draw all required polygons
Save the project into your B4A files folder and make sure to include the images.
You can double check the JSON file to see if the image filepath is not screwed.

Then load the json file with below code.

This is a work in progress and will be made more generic in the future, but it will give you an idea how it works.

Basically it uses the lgBox2DBodyEditorLoader and based on the deltatime it draws the current frame.
It removes all the fixtures from the body and loads the fixture belonging to the current frame to the body.

Feel free to comment and/or provide optimisation tips.

Note:
Main.P2M is my pixel to meter value.

B4X:
'Class module
Sub Class_Globals

    Dim Batch As lgSpriteBatch
    Dim Bodie As lgBox2DBody
    Dim Textures(6) As lgTexture
    Dim Sprites(6) As lgSprite
    Dim scale As Float = 0.5
    Dim Conversion As lgMathUtils
    Dim Loader As lgBox2DBodyEditorLoader
 
    Dim fd As lgBox2DFixtureDef
 
    Dim StateTime As Float = 0
    Dim TimePerFrame As Float = 0.1
    Dim CurrentFrame As Float = 0
    Dim TotalFrames As Float = 6
    Dim L As List
 
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(world As lgBox2DWorld)

    'add duration of the total sprite duration or time per frame.

    Batch.Initialize

    Log ("scaling = " & scale)
 
    Loader.InitializeWithFile("actors/plane.json")
    Dim L As List = Loader.SortedList

    '1. Create a BodyDef, as usual:
    Dim bd As lgBox2DBodyDef
    bd.Position.Set(5, 5)
    bd.Type = world.BODYTYPE_Kinematic
 
    '2. Create a FixtureDef, as usual:

    fd.Density = 1
    fd.Friction = 0.5
    fd.Restitution = 0.2
 
    '3. Create a Body, as usual:
    Bodie = world.CreateBody(bd)
 
 
    '5. Load the associated image:
    '    First load the images as we need it's width to set the scaling in the bodyfixture.
 
    For i = 0 To L.Size -1
        Textures(i).Initialize("actors/" & Loader.GetImagePath(L.Get(i)))
        Sprites(i).InitializeWithTexture(Textures(i))
        Sprites(i).SetSize(Sprites(i).Width/Main.P2M, (Sprites(i).Width/Main.P2M) * (Sprites(i).Height / Sprites(i).Width) )
        Dim Origin As lgMathVector2
        Origin = Loader.GetOrigin(L.Get(i), Sprites(i).Width)
        Sprites(i).SetOrigin(Origin.X, Origin.Y)
    Next
 
    '4. Create the body fixture automatically by using the Loader, only first occurance.
    Loader.AttachFixture(Bodie, L.Get(0), fd, Sprites(0).Width)

    Log ("List: " & L)

End Sub

Sub draw(camera As lgOrthographicCamera,world As lgBox2DWorld, deltatime As Float)
 
    camera.update
 
    'deltatime is the time spent since last draw.
    'first define which frame we should show
 
    StateTime = StateTime + deltatime
 
    If StateTime > TimePerFrame Then
        If CurrentFrame < TotalFrames-1  Then
            CurrentFrame = CurrentFrame +1
            StateTime = 0
        Else
            CurrentFrame = 0
            StateTime = 0
        End If
    End If
 
    'destroy the fixtures belonging to the previous frame.
    Dim arr As lgArray
    Bodie.GetFixtureList(arr)
    Log("Fixturelist: " & arr.Size)
 
 
    For Each f As lgBox2DFixture In arr.toList
        Bodie.destroyFixture(f)
    Next
 
    'attach new fixture based on current frame.
    Loader.AttachFixture(Bodie, L.Get(CurrentFrame), fd, Sprites(CurrentFrame).Width)
 
    'update positions.
    update
 
    Batch.ProjectionMatrix = camera.Combined
    Batch.Begin
        Sprites(CurrentFrame).Draw(Batch)
    Batch.End

End Sub

Sub update()

'create logic to disable/enable bodies based on the deltatime, statetime and duration of the total sprite.

'world.Step(1/60, 8, 3)

For i = 0 To Sprites.Length - 1
    Sprites(i).X = Bodie.Position.X - Sprites(i).OriginX
    Sprites(i).Y = Bodie.Position.Y - Sprites(i).OriginY
    Sprites(i).Rotation = Bodie.Angle * Conversion.radiansToDegrees
Next

move


End Sub

Sub move()

'create move code
Bodie.setTransform2(Main.camera.Position.x, Main.camera.Position.y, 0)


End Sub

hi,

i am trying to make a simple game where i can detect a collision between 2 bodies that are moving on the screen (not falling)
it should be a racing game, could you please the complete project so i can see how you use this class?

thank you,

regards, ilan
 

Informatix

Expert
Licensed User
Longtime User
I wanted to have great collision detection so I created this class.

Setup:
First make sure you have a sprite animation and seperate each frame in a seperate file.
Then load all the images into Physics body editor
Draw all required polygons
Save the project into your B4A files folder and make sure to include the images.
You can double check the JSON file to see if the image filepath is not screwed.

Then load the json file with below code.

This is a work in progress and will be made more generic in the future, but it will give you an idea how it works.

Basically it uses the lgBox2DBodyEditorLoader and based on the deltatime it draws the current frame.
It removes all the fixtures from the body and loads the fixture belonging to the current frame to the body.

Feel free to comment and/or provide optimisation tips.

Note:
Main.P2M is my pixel to meter value.

B4X:
'Class module
Sub Class_Globals

    Dim Batch As lgSpriteBatch
    Dim Bodie As lgBox2DBody
    Dim Textures(6) As lgTexture
    Dim Sprites(6) As lgSprite
    Dim scale As Float = 0.5
    Dim Conversion As lgMathUtils
    Dim Loader As lgBox2DBodyEditorLoader

    Dim fd As lgBox2DFixtureDef

    Dim StateTime As Float = 0
    Dim TimePerFrame As Float = 0.1
    Dim CurrentFrame As Float = 0
    Dim TotalFrames As Float = 6
    Dim L As List

End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(world As lgBox2DWorld)

    'add duration of the total sprite duration or time per frame.

    Batch.Initialize

    Log ("scaling = " & scale)

    Loader.InitializeWithFile("actors/plane.json")
    Dim L As List = Loader.SortedList

    '1. Create a BodyDef, as usual:
    Dim bd As lgBox2DBodyDef
    bd.Position.Set(5, 5)
    bd.Type = world.BODYTYPE_Kinematic

    '2. Create a FixtureDef, as usual:

    fd.Density = 1
    fd.Friction = 0.5
    fd.Restitution = 0.2

    '3. Create a Body, as usual:
    Bodie = world.CreateBody(bd)


    '5. Load the associated image:
    '    First load the images as we need it's width to set the scaling in the bodyfixture.

    For i = 0 To L.Size -1
        Textures(i).Initialize("actors/" & Loader.GetImagePath(L.Get(i)))
        Sprites(i).InitializeWithTexture(Textures(i))
        Sprites(i).SetSize(Sprites(i).Width/Main.P2M, (Sprites(i).Width/Main.P2M) * (Sprites(i).Height / Sprites(i).Width) )
        Dim Origin As lgMathVector2
        Origin = Loader.GetOrigin(L.Get(i), Sprites(i).Width)
        Sprites(i).SetOrigin(Origin.X, Origin.Y)
    Next

    '4. Create the body fixture automatically by using the Loader, only first occurance.
    Loader.AttachFixture(Bodie, L.Get(0), fd, Sprites(0).Width)

    Log ("List: " & L)

End Sub

Sub draw(camera As lgOrthographicCamera,world As lgBox2DWorld, deltatime As Float)

    camera.update

    'deltatime is the time spent since last draw.
    'first define which frame we should show

    StateTime = StateTime + deltatime

    If StateTime > TimePerFrame Then
        If CurrentFrame < TotalFrames-1  Then
            CurrentFrame = CurrentFrame +1
            StateTime = 0
        Else
            CurrentFrame = 0
            StateTime = 0
        End If
    End If

    'destroy the fixtures belonging to the previous frame.
    Dim arr As lgArray
    Bodie.GetFixtureList(arr)
    Log("Fixturelist: " & arr.Size)


    For Each f As lgBox2DFixture In arr.toList
        Bodie.destroyFixture(f)
    Next

    'attach new fixture based on current frame.
    Loader.AttachFixture(Bodie, L.Get(CurrentFrame), fd, Sprites(CurrentFrame).Width)

    'update positions.
    update

    Batch.ProjectionMatrix = camera.Combined
    Batch.Begin
        Sprites(CurrentFrame).Draw(Batch)
    Batch.End

End Sub

Sub update()

'create logic to disable/enable bodies based on the deltatime, statetime and duration of the total sprite.

'world.Step(1/60, 8, 3)

For i = 0 To Sprites.Length - 1
    Sprites(i).X = Bodie.Position.X - Sprites(i).OriginX
    Sprites(i).Y = Bodie.Position.Y - Sprites(i).OriginY
    Sprites(i).Rotation = Bodie.Angle * Conversion.radiansToDegrees
Next

move


End Sub

Sub move()

'create move code
Bodie.setTransform2(Main.camera.Position.x, Main.camera.Position.y, 0)


End Sub
The first advice ("seperate each frame in a seperate file") is something to avoid with libGDX and OpenGL in general. The less you have textures the greater is the performance because you minimize of the amount of texture switching in the GPU. It is better to use big textures that you split into regions. The libGDX guide in this forum explains how to create a texture atlas.

The code of the Draw function is to rewrite entirely because it is a perfect way to lose a lot of FPS. Why ?
1) You're allocating/disposing memory every frame with your DestroyFixture and AttachFixture calls. So the garbage collector has a lot of work every frame. Moreover AttachFixture is not a simple function. It creates the fixtures with all the vertices generated with Physics Body Editor, then free the vertices (memory deallocation once more).
My advice: All the bodies have to be created for all frames before rendering and stored in an array. Set their Active property to false and turn it on only for the body to use in the simulation. That costs a bit of memory of course but it's a neglectable amount in a real game and that avoids creating/disposing anything in the Render event. If you want to establish a relationship between bodies and sprites, use the Body.UserData and Sprite.Tag properties.
2) You're drawing a single object in a batch ! It's the worst thing to do. A Draw function should never call Batch.Begin and Batch.End unless it has a specific reason to isolate the drawing (e.g. a specific shader is used). The purpose of the "batchers" of libGDX is to optimize the rendering by combining the meshes of various objects to draw. So you should call only once Batch.Begin and Batch.End in your Render event and place in between all drawing commands.

At the beginning of your post, you claim: "I wanted to have great collision detection", but your code is unusable for that. How can you detect a collision with Box2D if you don't call World.Step ???
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
i am trying to make a simple game where i can detect a collision between 2 bodies
Why don't you look at my examples ? One of them is explicitely named Box2D_CollisionProcessing. In this example, objects fall because of the gravity setting but set this gravity to 0 and apply your own forces to move the bodies.
 
Last edited:

ilan

Expert
Licensed User
Longtime User
Why don't you look at my examples ? One of them is explicitely named Box2D_CollisionProcessing. In this example, objects fall because of the gravity setting but set this gravity to 0 and apply your own forces to move the bodies and the code will work exactly the same.

yes i saw it, you are right i will do it. the problem is this is my first time i use Box2D and dont understand everything in your code but slowly i do.
i understand now how i can detect the collision.

i will move my vehicle with SB and check collision with Box2d, hopefully i will get what i need.
my first game will be something very simple after that i will do a much more complex racing game.

There is something that i still have not understood correctly. if my vehicle that has the mass of about 32 hits the ground that has the mass of 0 and ground hold the vehicle, both are box2d dynamic bodies, my logic say that the object with more mass should move the object with less mass?! is it correct?

the reason i ask i would like to add walls to my game, and if my car will touch the walls it will stay on the road and move along the curved walls.
but if i move my vehicle with SB how can i mix that?? the collision is done with box2d so do i also need to move it with box2d? to get that result?

now i just destroy the car (and draw an explosion) when my car hit the walls, i would like to destroy it only if the car hits the walls more the 2 sec then explode...
give it like a hit value and if its on 0 destory, and every touch this value go down (let say it starts with 10 and go down by every touch with wall until 0 explode)

how could i do something like this?

thank you for your help:)
 

Informatix

Expert
Licensed User
Longtime User
yes i saw it, you are right i will do it. the problem is this is my first time i use Box2D and dont understand everything in your code but slowly i do.
i understand now how i can detect the collision.

i will move my vehicle with SB and check collision with Box2d, hopefully i will get what i need.
my first game will be something very simple after that i will do a much more complex racing game.

There is something that i still have not understood correctly. if my vehicle that has the mass of about 32 hits the ground that has the mass of 0 and ground hold the vehicle, both are box2d dynamic bodies, my logic say that the object with more mass should move the object with less mass?! is it correct?

the reason i ask i would like to add walls to my game, and if my car will touch the walls it will stay on the road and move along the curved walls.
but if i move my vehicle with SB how can i mix that?? the collision is done with box2d so do i also need to move it with box2d? to get that result?

now i just destroy the car (and draw an explosion) when my car hit the walls, i would like to destroy it only if the car hits the walls more the 2 sec then explode...
give it like a hit value and if its on 0 destory, and every touch this value go down (let say it starts with 10 and go down by every touch with wall until 0 explode)

how could i do something like this?

thank you for your help:)
Use Box2D to move your car. You do not need Steering Behaviors. When you'll be familiar with Box2D, I will explain how to combine it with SB.
As I said, you should read the excellent tutorials of http://www.iforce2d.net
 

ilan

Expert
Licensed User
Longtime User

wonder

Expert
Licensed User
Longtime User
@ilan, I'm starting to be really curious about your racing game! I wonder what are you cooking in there.... ;) :D
 

ilan

Expert
Licensed User
Longtime User
@ilan, I'm starting to be really curious about your racing game! I wonder what are you cooking in there.... ;) :D

A simple racing game is finished and allready in share your creation section. (Need to make some modifications)

The next racing game will be much better and fun (i hope) should be soon available.

Working with box2d and libgdx makes game making much simpler.
the collisions detection is much better and no need to break your head to calculate every possible collisions point, box2d does it for you ;)
 

LucaMs

Expert
Licensed User
Longtime User
The first advice ("seperate each frame in a seperate file") is something to avoid with libGDX and OpenGL in general. The less you have textures the greater is the performance because you minimize of the amount of texture switching in the GPU. It is better to use big textures that you split into regions. The libGDX guide in this forum explains how to create a texture atlas.

The code of the Draw function is to rewrite entirely because it is a perfect way to lose a lot of FPS. Why ?
1) You're allocating/disposing memory every frame with your DestroyFixture and AttachFixture calls. So the garbage collector has a lot of work every frame. Moreover AttachFixture is not a simple function. It creates the fixtures with all the vertices generated with Physics Body Editor, then free the vertices (memory deallocation once more).
My advice: All the bodies have to be created for all frames before rendering and stored in an array. Set their Active property to false and turn it on only for the body to use in the simulation. That costs a bit of memory of course but it's a neglectable amount in a real game and that avoids creating/disposing anything in the Render event. If you want to establish a relationship between bodies and sprites, use the Body.UserData and Sprite.Tag properties.
2) You're drawing a single object in a batch ! It's the worst thing to do. A Draw function should never call Batch.Begin and Batch.End unless it has a specific reason to isolate the drawing (e.g. a specific shader is used). The purpose of the "batchers" of libGDX is to optimize the rendering by combining the meshes of various objects to draw. So you should call only once Batch.Begin and Batch.End in your Render event and place in between all drawing commands.

At the beginning of your post, you claim: "I wanted to have great collision detection", but your code is unusable for that. How can you detect a collision with Box2D if you don't call World.Step ???


This is a great answer, from which "you" can understand Informatix is a real expert.

I have a question (wrong thread, as usual :D):

have you ever published games (apps), Informatix?
I would like to see them because I am sure that they would be of excellent quality and to see how much they will allow you to earn (what strategy you would use to earn "enough").
 

Informatix

Expert
Licensed User
Longtime User
have you ever published games (apps), Informatix?
For Android, I released two games until now and I have two in progress. The first one, My Playground, was created 2 years ago with Accelerated Surface and it suffers from the limitations of this library and of my knowledge of Android at this period. So it's not a code of great interest. The second game is unpublished on the Play Store because some graphics are still temporary but it can be downloaded and played (link in my signature). When all the graphics will be replaced by their final version, I will translate it to english. For now it is only in french. The most interesting part of this game is the IA, which I am really proud of, but the code is not publicly available.
Creating a game is something that takes month so I have not a lot of things to show. I started coding with Android only in 2012.
Currently, I'm making two new games with two professional artists. These projects are of a high technical level and I promised that I will explain how I made one of them when it is released (probably near the end of this year).

how much they will allow you to earn (what strategy you would use to earn "enough").
Earning something is not what I'm looking for. I earn enough with my job so I feel free to make games without ads, without any business strategy, with no popular style or theme... Things are a bit different with the 2 games to come because I'm not alone onboard. But it is too soon to speak about this subject.
 
Top