Android Tutorial Introduction to the libGDX library

Introduction to the libGDX library

What is libGDX ?

libGDX is a game engine. As we saw in the first tutorial, a game engine provides a framework to create games and covers all aspects (rendering, animation, input, music, networking, physics, ...) of various kinds of games.

libGDX is considered as one of the best and fastest engine for the Android world. It is free, rich-featured, reliable, and proved its efficiency in a lot of well-known games (Ingress, Zombie Smasher, Apparatus, Monsterama Park, Clash of the Olympians, Bumbledore, etc.)

It’s a layered framework: it goes from low-level classes for OpenGL experts to high-level classes, easy to use by beginners. It includes a scene graph (Scene2D classes), a physics engine (Box2D classes), a particle system, a map renderer, a sprite batcher, an extensive set of mathematics classes… more than 200 classes in total.

For technical reasons, the version for Basic4Android cannot be multi-platform, and by choice, the current release doesn't include the 3D classes (except the ones for the perspective camera and for the decals) and the Daydream class.

libGDX was created in 2010 by Mario Zechner (badlogicgames.com) and is maintained by M. Zechner, Nathan Sweet (esotericsoftware.com) and a community of developers.

Minimum requirements

OpenGL ES 2.0
Android Froyo (API 8)

Hardware acceleration

libGDX does not require that you enable hardware acceleration on your device because it is based on OpenGL, which interacts directly with the GPU.

Debugging

You cannot use the debugger of B4A with libGDX because most of the code of the library runs in a different thread. You have to use the Log() function to debug your game.

The library provides a debug renderer for Box2D (lgBox2DDebugRenderer), a debug renderer for Scene2D (lgScn2DDebugRenderer) and a profiler for OpenGL (lgGLProfiler).

A word about the classes

The main class is LibGDX. All other classes are prefixed by lg.
All Box2D classes are prefixed by lgBox2D. All Scene2D classes are prefixed by lgScn2D. All Map classes are prefixed by lgMap. All Math classes are prefixed by lgMath.

The LibGDX class gives access to five interfaces: Audio (lgAudio), Files (lgFiles), Graphics (lgGraphics), Input (lgInput), and Net (lgNet). You will use the Input interface, for example, to get the input from the accelerometer.

With some classes (e.g. the five interfaces), you cannot create a new instance with Dim. You have to use an instance returned by the library. For example, you cannot write:
B4X:
Dim Graphics As lgGraphics
Graphics = lGdx.Graphics
but you can write:
B4X:
Dim Graphics As lgGraphics = lGdx.Graphics

Some classes cannot be instantiated at all because they are generic classes (e.g. com.badlogic.gdx.scenes.scene2d.utils.Drawable or com.badlogic.gdx.maps.tiled.TiledMapTile). In this case, either you store their instance as an Object or you use a subclass, e.g.:
B4X:
Dim objTile As Object = CurrentLayer.GetCell(X, Y).Tile
Dim staticTile As lgMapStaticTiledMapTile = CurrentLayer.GetCell(X, Y).Tile

OpenGL ES

I explained what OpenGL is in the previous tutorial and I won't discuss it further here because the main advantage to use a game engine like libGDX is to benefit from the abstraction layer above OpenGL. However, if you need (or want) to call directly OpenGL, here's how to get access to the classes and functions:
B4X:
Dim lGdx_GL20 As lgGL20 = lGdx.Graphics.GL20
or better (includes also the constants):
B4X:
Dim GL As lgGL

Note: libGDX uses the 2D coordinate system and the color encoding of OpenGL ES, so the Y-axis is pointing upwards and each color value ranges from 0 to 1.

The libGDX life-cycle

An important thing to keep in mind about libGDX is that it runs in its own thread. Your Basic4Android application runs most of the time in a different thread called the UI thread, or main thread. That means you cannot access the other views of your activity and change their properties from the libGDX thread. Fortunately, there's a function in the LibGDX class that passes the runnable (the piece of code to execute) from a thread to the other: CallSubUI. So if you want to change the activity title, set a label text or show a MsgBox from the libGDX thread, don't forget to use this function to avoid a crash!

Since libGDX runs in a different thread, you have to inform its library of the events of your activity : Create, Pause and Resume. First, create an instance of libGDX in Globals :
B4X:
Dim lGdx As libGDX
In Activity_Create (and nowhere else), add the Initialize function :
B4X:
lGdx.Initialize(False, "LG") 'fills the activity with the libGDX surface, uses OpenGL 1 for compatibility and prefixes the events with LG
In Activity_Resume, add the following line :
B4X:
If lGdx.IsInitialized Then lGdx.Resume
In Activity_Pause, add the following line :
B4X:
If lGdx.IsInitialized Then lGdx.Pause

You could initialize libGDX differently. For example, it could be limited to a view with InitializeView. You could also define a configuration (lgConfiguration class) and pass it to libGDX. Example:
B4X:
Dim Config As lgConfiguration

'Disables the accelerometer and the compass
Config.useAccelerometer = False
Config.useCompass = False

'Uses a WakeLock (the device will stay on)
Config.useWakelock = True

'Creates the libGDX surface
lGdx.Initialize2(Config, "LG")

Once done, your library is ready to raise the events of its life-cycle : Create, Resize, Render, Pause, Resume, Dispose. These events are the place to put all the code of your game. Don't put anything in the usual activity events. They are reserved for your other views and are raised by the UI thread.

Create :

Create is the first raised event. It is raised soon after the initialization of the library and the creation of the OpenGL surface.
In this event, initialize your renderer and your input processors, load your resources (we'll see that in detail later) and initialize your game data.

Resize :

Resize is raised when the size of the libGDX surface changes. Under Android, that should only happen when you start the application and when it is restarted after a rotation or resumed.
It is raised at least once, after Create, and, when the application is resumed, just before Resume.
In this event, initialize the camera viewport. It's probably the only use you will find for it.
This event returns the new width and height in pixels.

Render :

Render is raised as soon as possible after Create and Resize.
It's where things are drawn. It's also where you have to put the logic of your game, but I would not recommend putting hundreds of lines of code here. Instead, create new subs and new modules and call them from this event.
The first lines in Render should be to clear the screen. Example:
B4X:
lGdx_GL.glClearColor(0, 0, 1, 1) 'Blue background
lGdx_GL.glClear(lGdx_GL.GL10_COLOR_BUFFER_BIT)

Pause :

Pause is raised when the activity is sent in the background, rotated or exited.
It's the right place to save your game data.
Note that the OpenGL context is destroyed when the app goes in the background, so all your unmanaged textures and pixmaps have to be reloaded or recreated in the Resume event when the app returns to the foreground.

Resume :

Contrary to the Resume event of your activity, this one is not raised after Create, only when the application returns from a pause.
As the OpenGL context is destroyed when the app goes in the background, all your unmanaged* textures and pixmaps have to be reloaded or recreated when this event is raised. More info here. *Not loaded by an asset manager.

Dispose :

Dispose is called when the activity is exited, after Pause, or when the device is rotated.
In this event, release all the used resources by calling the Dispose function of objects (if they have one).

The life-cycle :
application_lifecycle_diagram.png


Multiple screens

A game is made of many screens. You could create an activity for each one, but that would not be very convenient because you'd have to reinitialize the library in each activity and reload some resources. Moreover, that would not ease any graphical transition between screens. In fact, most games are made with a very small number of activities and make use of a screen manager instead. The screen manager stores the reference of the different screens and allows switching between them. Each screen has its own life-cycle.
To create a screen manager with two screens, for example, declare them in Globals:
B4X:
Dim lGdx_ScrMgr As lgScreenManager
Dim lGdx_Screen(2) As lgScreen
Then add these lines in the Create event handler:
B4X:
'Creates two screens
lGdx_ScrMgr.Initialize(lGdx)
lGdx_Screen(0) = lGdx_ScrMgr.AddScreen("LGS1")
lGdx_Screen(1) = lGdx_ScrMgr.AddScreen("LGS2")
Show the first screen:
B4X:
lGdx_ScrMgr.CurrentScreen = lGdx_Screen(0)

When you want to change the current screen, just change the value of the CurrentScreen property. That will raise the Hide event of the previous screen and the Show event of the new one.

The screens have the same life-cycle as the library, and thus the same events except that Create is named Show and Dispose is named Hide.

Input processor and gesture detector

To get the input events raised by your players, you have to declare input processors. libGDX has an input processor for keyboard and touch events (lgInputProcessor) and a specialized input processor for gestures (lgGestureDetector).
Start by declaring them in Globals:
B4X:
Dim lGdx_IP As lgInputProcessor
Dim lGdx_GD As lgGestureDetector
Initialize them in the Create event (or the Show event of a screen if you want different processors for different screens):
B4X:
lGdx_IP.Initialize("IP")
lGdx_GD.Initialize("GD")
And add the event handlers that you need:
B4X:
Sub IP_KeyDown(KeyCode As Int) As Boolean
   Return False
End Sub

Sub IP_KeyUp(KeyCode As Int) As Boolean
   Return False
End Sub

Sub IP_KeyTyped(Character As Char) As Boolean
   Return False
End Sub

Sub IP_TouchDown(ScreenX As Int, ScreenY As Int, Pointer As Int) As Boolean
   Return False
End Sub

Sub IP_TouchDragged(ScreenX As Int, ScreenY As Int, Pointer As Int) As Boolean
   Return False
End Sub

Sub IP_TouchUp(ScreenX As Int, ScreenY As Int, Pointer As Int) As Boolean
   Return False
End Sub

Sub GD_TouchDown(X As Float, Y As Float, Pointer As Int) As Boolean
   Return False
End Sub

Sub GD_Fling(VelocityX As Float, VelocityY As Float) As Boolean
   Return False
End Sub

Sub GD_LongPress(X As Float, Y As Float) As Boolean
   Return False
End Sub

Sub GD_Pan(X As Float, Y As Float, DeltaX As Float, DeltaY As Float) As Boolean
   Return False
End Sub

Sub GD_Pinch(InitialPointer1 As lgMathVector2, InitialPointer2 As lgMathVector2, Pointer1 As lgMathVector2, Pointer2 As lgMathVector2) As Boolean
   Return False
End Sub

Sub GD_Tap(X As Float, Y As Float, Count As Int) As Boolean
   Return False
End Sub

Sub GD_Zoom(InitialDistance As Float, Distance As Float) As Boolean
    Return False
End Sub

Description of events :

Fling: The user quickly dragged a finger across the screen, then lifted it. Useful to implement swipe gestures.
Pan: The user is dragging a finger across the screen. The detector reports the current touch coordinates as well as the delta between the current and previous touch positions. Useful to implement camera panning in 2D.
Pinch: Similar to zoom. The detector reports the initial and current finger positions instead of the distance. Useful to implement camera zooming and more sophisticated gestures such as rotation.
Tap: The user touched the screen and lifted the finger. The finger must not move outside a specified square area around the initial touch position for a tap to be registered. Multiple consecutive taps will be detected if the user performs taps within a specified time interval.
Zoom: The user has placed two fingers on the screen and is moving them together/apart. The detector reports both the initial and current distance between fingers in pixels. Useful to implement camera zooming.

The other events are self-explanatory.

.....
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
1. can i group lgscnactors together? and move them around or do i have to do it for each actor?
From the first post:
"The lgScn2DGroup class is used to create groups of actors. It allows to do group actions (group movement for example) or group transformations. The stage contains already a group named Root which is the main group."

2. hiding the actor will prevent calling the listener touch event when clicking on that area (like a normal android button)?
Yes.
 

ilan

Expert
Licensed User
Longtime User
hi, i would like to improve the performance of my box2d+libgdx game.
the frame rate i get is not very stable. i would like to ask what improvements i could do to make the rendering process faster?
 

wonder

Expert
Licensed User
Longtime User
@ilan, you need to identify the bottleneck(s) first.

As you already know, the generic formula is:
B4X:
Dim nt As NanoTime
Dim ts As Long
...
...
ts = nt.NanoTime
    'Code to be benchmarked
    ...
    ...
ts = nt.NanoTime - ts
'Display / Log (ts)

Divide you main cycle into sections use multiple timestamp variables:
B4X:
ts1= nt.NanoTime
    'Collision detection code
ts1 = nt.NanoTime - ts1

ts2= nt.NanoTime
    'Player movement code
ts2 = nt.NanoTime - ts2

ts3= nt.NanoTime
    'Physics code
ts3 = nt.NanoTime - ts3

...

Subdivide each section into smaller ones until you find the problem(s).

Have a look at you nested cycles, they're usually the devil...
B4X:
For x = xMin to xMax
    For y = yMin to yMax
        '...
    Next
Next
 

ilan

Expert
Licensed User
Longtime User
ok thanx wonder i will do it.

i have just changed all for i...next to for each loops and i am getting about 30% better rendering time
i noticed that when the player is dead the rendering time goes to half so the animation of the player take for some reason much time

and i cannot explain why

this is the code:

B4X:
Sub drawPlayer
    If medead Then
        player.setLinearVelocity2(0,0)
        player.GravityScale = 0
        Return
    End If
    If Not(gamepause = True And medead = False) Then
        If frames Mod 5 = 0 Then playerindex = playerindex + 1
    End If
    Dim objcordinate As objcor
    objcordinate = player.UserData
   
    'check if exit gate now
    Dim gateobj As objcor = gate.UserData
    If gateobj.exitnow Then
        If player.getLinearVelocityFromWorldPoint(player.WorldCenter).x = 0 And player.getLinearVelocityFromWorldPoint(player.WorldCenter).y = 0 Then
            If exitCD < 0 Then Return
            Batch.SetColorRGBA(1,1,1,mapping(exitCD,60,0,1,0))
            Batch.DrawTex2(playervictory,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)
            Batch.SetColorRGBA(1,1,1,1)
            Return           
        End If   
    End If

    If onlift Then
        If objcordinate.horizontal = 0 Then
            If Abs(player.getLinearVelocityFromWorldPoint(player.WorldCenter).x) <> Abs(objcordinate.speed) Then
                If drawleft Then
                    Batch.DrawRegion2(player_framesFlip(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                Else
                    Batch.DrawRegion2(player_frames(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                End If   
            Else
                If drawleft Then
                    Batch.DrawRegion2(playeridle_framesFlip(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                Else
                    Batch.DrawRegion2(playeridle_frames(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)
                End If   
            End If
        Else
            If Abs(player.getLinearVelocityFromWorldPoint(player.WorldCenter).x) > 0.2 Then
                If drawleft Then
                    Batch.DrawRegion2(player_framesFlip(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                Else
                    Batch.DrawRegion2(player_frames(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                End If   
            Else
                If drawleft Then
                    Batch.DrawRegion2(playeridle_framesFlip(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
                Else
                    Batch.DrawRegion2(playeridle_frames(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)
                End If   
            End If               
        End If       
        Return
    End If
       
    If Abs(player.getLinearVelocityFromWorldPoint(player.WorldCenter).y) < 0.2 And Abs(player.getLinearVelocityFromWorldPoint(player.WorldCenter).x) < 0.2 Then
        'IDLE
        If drawleft Then
            Batch.DrawRegion2(playeridle_framesFlip(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
        Else
            Batch.DrawRegion2(playeridle_frames(0,playerindex Mod 5),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)
        End If       
    Else
        If player.getLinearVelocityFromWorldPoint(player.WorldCenter).y > 1 Then 'JUMP
            If drawleft Then
                Batch.DrawTex2(playerjumpFlip,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)               
            Else
                Batch.DrawTex2(playerjump,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
            End If   
        else if player.getLinearVelocityFromWorldPoint(player.WorldCenter).y < -0.2 Then 'FALL
            If drawleft Then
                Batch.DrawTex2(playerfallFlip,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)       
            Else
                Batch.DrawTex2(playerfall,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)       
            End If   
        Else 'RUN
            If drawleft Then
                Batch.DrawRegion2(player_framesFlip(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
            Else
                Batch.DrawRegion2(player_frames(0,playerindex Mod 8),PosCon.box2d2world(player.Position.x,player.Position.y,0,0).x-objcordinate.width*1.1,PosCon.box2d2world(player.Position.x,player.Position.y,0,0).y-objcordinate.height/1.85,objcordinate.width*2.1,objcordinate.height*1.8)   
            End If       
        End If       
    End If
End Sub

there are no loops at all.
 

wonder

Expert
Licensed User
Longtime User
should i divide the nanotime by 1.000.000 to get 1 ms?
Yes, and then you can use NumberFormat2() or Round2() on the result... :)

Regarding your previous post, your code is quite complex (at least to my eyes) to give you a straight answer at first sight.

You could cache some of the calculations and function calls. You could also use a FastAbs function.
B4X:
'For faster results, change the type to what suits your needs (int, short, float or long)
Sub FastAbs(x As Double) As Double
    If x < 0 Then Return -x
    Return x
End Sub
Nonetheless such improvements wouldn't help you much... You'd gain no more than a few hundreds of nanoseconds.

Have you already pinpointed the exact set of instructions which are causing the problem?
 

ilan

Expert
Licensed User
Longtime User
Have you already pinpointed the exact set of instructions which are causing the problem?

ok the player drawing was not the problem.

it seems like the drawing of the stage (lgScn2DStage) takes to much and when the player dies the actors (buttons) of the stage disappear and thats why the drop of drawing time i get earlier.

but why does it takes so long i just draw for each actor an image
 

ilan

Expert
Licensed User
Longtime User
should i maybe use simple buttons on a panel instead of drawing them each frame on a lgScn2DStage?
 

Informatix

Expert
Licensed User
Longtime User
should i maybe use simple buttons on a panel instead of drawing them each frame on a lgScn2DStage?
Oh, please no. Don't mix standard views with libGDX if you want performance (and I can imagine all the threading issues...). In my DarkContinent game, I have a lot of lgScn2DButton and in my Diktatour game I display hundreds of actors at the same time at 60fps, so rendering actors in the stage is not the problem here.
 

wonder

Expert
Licensed User
Longtime User
I have no issues using a transparent panel for input (tested in several devices and emulators), but I do agree it required some carefully handcrafted code to make it work.

My implementation is as follows:
Untitled Diagram (1).png
 
Last edited:

ilan

Expert
Licensed User
Longtime User
Oh, please no. Don't mix standard views with libGDX if you want performance (and I can imagine all the threading issues...). In my DarkContinent game, I have a lot of lgScn2DButton and in my Diktatour game I display hundreds of actors at the same time at 60fps, so rendering actors in the stage is not the problem here.

maybe i am doing something wrong

i have few actors on my stage on game play. and the

B4X:
        gamestage1.Act
        gamestage1.Draw

takes about 7ms and thats to much.

i copied the whole code where i draw the buttons to a different sub and i commented the gamestage1.draw and .act and now i get much less time.

i cannot explain why the same code but in a different sub takes much less time then using gamestage1.Draw.

this is the code where i draw the actors.

B4X:
Sub Stage_Draw    
    If ingame Then
        If gamepause = False Then
            If medead = True Then
                gamebtnGrooup.Visible = False
                restartCD = restartCD - 1
                If restartCD < 0 Then
                    pausebtnGroup.Visible = True
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(deadpausetexture,deadtitle.X,deadtitle.Y,deadtitle.Width,deadtitle.Height)
                    GameBatch.DrawTex2(backtomaintexture,deadbacktomenu.X,deadbacktomenu.Y,deadbacktomenu.Width,deadbacktomenu.Height)
                    GameBatch.DrawTex2(playagaintexture,deadplayagain.X,deadplayagain.Y,deadplayagain.Width,deadplayagain.Height)                   
                End If
            Else
                pausebtnGroup.Visible = False
                gamebtnGrooup.Visible = True
                If firedown Then
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(firebtnimg,playbtn(2).x+(vpW*0.015),playbtn(2).y+(vpW*0.015),playbtn(2).Width-(vpW*0.03),playbtn(2).Height-(vpW*0.03))
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(firebtnimg,playbtn(2).x,playbtn(2).y,playbtn(2).Width,playbtn(2).Height)   
                End If

                If goleft Then
                    drawleft = True
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(leftbtnimg,playbtn(0).X,playbtn(0).Y,playbtn(0).Width,playbtn(0).Height)
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(leftbtnimg,playbtn(0).X,playbtn(0).Y,playbtn(0).Width,playbtn(0).Height)   
                End If

                If goright Then
                    drawleft = False
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(rightbtnimg,playbtn(1).X,playbtn(1).Y,playbtn(1).Width,playbtn(1).Height)
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(rightbtnimg,playbtn(1).X,playbtn(1).Y,playbtn(1).Width,playbtn(1).Height)   
                End If
               
                GameBatch.SetColorRGBA(1,1,1,0.9) 'reset color
                If drawshield Then
                    bitmapfont.Scale(0.8)
                    Dim text As String = labeltxt.Get(infoint)
                    Dim width As Float    
                    Dim linespace As Float = vpH*0.1   
                    If text.Contains(CRLF) Then
                        Dim str() As String = Regex.Split(CRLF,labeltxt.Get(infoint))
                        width = 0
                        For h = 0 To str.Length-1
                            If bitmapfont.getBounds(str(0)).width > width Then
                                width = bitmapfont.getBounds(str(0)).width
                            End If
                        Next
                        width = width + (vpW*0.15)
                        GameBatch.DrawTex2(shildtexture,vpW*0.5-(width/2),vpH-(str.Length*linespace)-(linespace+(linespace/2)),width,linespace*(str.Length+1))
                        For i = 0 To str.Length-1
                            shadowtext(GameBatch,str(i),vpW*0.5,vpH-(i*linespace)-linespace,mycolor.WHITE,True,True)
                        Next       
                    Else
                        width = bitmapfont.getBounds(labeltxt.Get(infoint)).width + (vpW*0.15)
                        GameBatch.DrawTex2(shildtexture,vpW*0.5-(width/2),vpH-((linespace*2)+(linespace/2)),width,linespace*2)
                        shadowtext(GameBatch,labeltxt.Get(infoint),vpW*0.5,vpH-linespace,mycolor.WHITE,True,True)   
                    End If   
                End If   
               
                'draw pause button
                GameBatch.DrawTex2(pausetxt,playbtn(4).X,playbtn(4).Y,playbtn(4).Width,playbtn(4).Height)
                   
                'draw coin btn
                GameBatch.DrawTex2(coincount,playbtn(3).X,playbtn(3).Y,playbtn(3).Width,playbtn(3).Height)
                GameBatch.DrawTex2(levelcount,playbtn(5).X,playbtn(5).Y,playbtn(5).Width,playbtn(5).Height)
               
                bitmapfont.Scale(0.45)
                shadowtext(GameBatch,    cointint, playbtn(3).X+(playbtn(3).Width/1.8),playbtn(3).Y+(playbtn(3).Height/1.9),mycolor.WHITE,True,True)
                shadowtext(GameBatch, level, playbtn(5).X+(playbtn(5).Width/1.8),playbtn(5).Y+(playbtn(5).Height/1.9),mycolor.WHITE,True,True)
            End If       
        Else
            pausebtnGroup.Visible = True
            gamebtnGrooup.Visible = False
            GameBatch.SetColorRGBA(0.85,0.85,0.85,0.9)
            GameBatch.DrawTex2(pausetexture,deadtitle.X,deadtitle.Y,deadtitle.Width,deadtitle.Height)
            GameBatch.DrawTex2(backtomaintexture,deadbacktomenu.X,deadbacktomenu.Y,deadbacktomenu.Width,deadbacktomenu.Height)
            GameBatch.DrawTex2(playagaintexture,deadplayagain.X,deadplayagain.Y,deadplayagain.Width,deadplayagain.Height)           
        End If
    End If
End Sub

using the SAME code in this sub:

B4X:
Sub gamebtn_Draw(SpriteBatch As lgSpriteBatch, ParentAlpha As Float)

will take maybe x2-3 more time. i am a little bit confused. maybe i need to dispose the stage before drawing it?
 

Informatix

Expert
Licensed User
Longtime User
I have no issues using a transparent panel for input (tested in several devices and emulators), but I do agree it required some carefully handcrafted code to make it work.

My implementation is as follows:
View attachment 49976
That remains a bad idea and I strongly advise against the mix for many reasons: views are run in a different thread, use a different coordinate system, have an event system that is separate from the libGDX one, are not customizable as actors can be, cannot be grouped and transformed (rotated, scaled, etc.) as actors can be, cannot be affected by shaders...
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
btw i cannot remember you posting a game with this name in the share your creation forum. is it a secret game from you? :)
It's not secret. Last year, I posted a request to find an artist to finish the game because my wife cannot. But it's difficult to find someone that accept to draw with the same style and who will earn probably nothing in the end for this work.
 

Informatix

Expert
Licensed User
Longtime User
maybe i am doing something wrong

i have few actors on my stage on game play. and the

B4X:
        gamestage1.Act
        gamestage1.Draw

takes about 7ms and thats to much.

i copied the whole code where i draw the buttons to a different sub and i commented the gamestage1.draw and .act and now i get much less time.

i cannot explain why the same code but in a different sub takes much less time then using gamestage1.Draw.

this is the code where i draw the actors.

B4X:
Sub Stage_Draw  
    If ingame Then
        If gamepause = False Then
            If medead = True Then
                gamebtnGrooup.Visible = False
                restartCD = restartCD - 1
                If restartCD < 0 Then
                    pausebtnGroup.Visible = True
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(deadpausetexture,deadtitle.X,deadtitle.Y,deadtitle.Width,deadtitle.Height)
                    GameBatch.DrawTex2(backtomaintexture,deadbacktomenu.X,deadbacktomenu.Y,deadbacktomenu.Width,deadbacktomenu.Height)
                    GameBatch.DrawTex2(playagaintexture,deadplayagain.X,deadplayagain.Y,deadplayagain.Width,deadplayagain.Height)                 
                End If
            Else
                pausebtnGroup.Visible = False
                gamebtnGrooup.Visible = True
                If firedown Then
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(firebtnimg,playbtn(2).x+(vpW*0.015),playbtn(2).y+(vpW*0.015),playbtn(2).Width-(vpW*0.03),playbtn(2).Height-(vpW*0.03))
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(firebtnimg,playbtn(2).x,playbtn(2).y,playbtn(2).Width,playbtn(2).Height) 
                End If

                If goleft Then
                    drawleft = True
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(leftbtnimg,playbtn(0).X,playbtn(0).Y,playbtn(0).Width,playbtn(0).Height)
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(leftbtnimg,playbtn(0).X,playbtn(0).Y,playbtn(0).Width,playbtn(0).Height) 
                End If

                If goright Then
                    drawleft = False
                    GameBatch.SetColorRGBA(0.85,0.85,0.85,0.95)
                    GameBatch.DrawTex2(rightbtnimg,playbtn(1).X,playbtn(1).Y,playbtn(1).Width,playbtn(1).Height)
                Else
                    GameBatch.SetColorRGBA(1,1,1,0.8)
                    GameBatch.DrawTex2(rightbtnimg,playbtn(1).X,playbtn(1).Y,playbtn(1).Width,playbtn(1).Height) 
                End If
             
                GameBatch.SetColorRGBA(1,1,1,0.9) 'reset color
                If drawshield Then
                    bitmapfont.Scale(0.8)
                    Dim text As String = labeltxt.Get(infoint)
                    Dim width As Float  
                    Dim linespace As Float = vpH*0.1 
                    If text.Contains(CRLF) Then
                        Dim str() As String = Regex.Split(CRLF,labeltxt.Get(infoint))
                        width = 0
                        For h = 0 To str.Length-1
                            If bitmapfont.getBounds(str(0)).width > width Then
                                width = bitmapfont.getBounds(str(0)).width
                            End If
                        Next
                        width = width + (vpW*0.15)
                        GameBatch.DrawTex2(shildtexture,vpW*0.5-(width/2),vpH-(str.Length*linespace)-(linespace+(linespace/2)),width,linespace*(str.Length+1))
                        For i = 0 To str.Length-1
                            shadowtext(GameBatch,str(i),vpW*0.5,vpH-(i*linespace)-linespace,mycolor.WHITE,True,True)
                        Next     
                    Else
                        width = bitmapfont.getBounds(labeltxt.Get(infoint)).width + (vpW*0.15)
                        GameBatch.DrawTex2(shildtexture,vpW*0.5-(width/2),vpH-((linespace*2)+(linespace/2)),width,linespace*2)
                        shadowtext(GameBatch,labeltxt.Get(infoint),vpW*0.5,vpH-linespace,mycolor.WHITE,True,True) 
                    End If 
                End If 
             
                'draw pause button
                GameBatch.DrawTex2(pausetxt,playbtn(4).X,playbtn(4).Y,playbtn(4).Width,playbtn(4).Height)
                 
                'draw coin btn
                GameBatch.DrawTex2(coincount,playbtn(3).X,playbtn(3).Y,playbtn(3).Width,playbtn(3).Height)
                GameBatch.DrawTex2(levelcount,playbtn(5).X,playbtn(5).Y,playbtn(5).Width,playbtn(5).Height)
             
                bitmapfont.Scale(0.45)
                shadowtext(GameBatch,    cointint, playbtn(3).X+(playbtn(3).Width/1.8),playbtn(3).Y+(playbtn(3).Height/1.9),mycolor.WHITE,True,True)
                shadowtext(GameBatch, level, playbtn(5).X+(playbtn(5).Width/1.8),playbtn(5).Y+(playbtn(5).Height/1.9),mycolor.WHITE,True,True)
            End If     
        Else
            pausebtnGroup.Visible = True
            gamebtnGrooup.Visible = False
            GameBatch.SetColorRGBA(0.85,0.85,0.85,0.9)
            GameBatch.DrawTex2(pausetexture,deadtitle.X,deadtitle.Y,deadtitle.Width,deadtitle.Height)
            GameBatch.DrawTex2(backtomaintexture,deadbacktomenu.X,deadbacktomenu.Y,deadbacktomenu.Width,deadbacktomenu.Height)
            GameBatch.DrawTex2(playagaintexture,deadplayagain.X,deadplayagain.Y,deadplayagain.Width,deadplayagain.Height)         
        End If
    End If
End Sub

using the SAME code in this sub:

B4X:
Sub gamebtn_Draw(SpriteBatch As lgSpriteBatch, ParentAlpha As Float)

will take maybe x2-3 more time. i am a little bit confused. maybe i need to dispose the stage before drawing it?
I'm not sure to understand what I read.
When you run
B4X:
gamestage1.Act
gamestage1.Draw
are events raised for your actors? What did you do in these events?
 

ilan

Expert
Licensed User
Longtime User
are events raised for your actors? What did you do in these events?

sorry, i will explain.

i had in my box2d+libgdx game not a stable FPS. it was most of the time 60 but it goes down and up 50~60.
so i checked for each event i do how much ms it takes (with the Nanotime lib)

all drawing and all loops takes less then 1-2ms. only 2 thing takes much

1. drawing the tilemap from tiled
2. gamestage.draw

drawing the tilemap takes about 4ms
and gamestage.draw takes about 7-8ms

everything is drawn when i call gamestage.draw but i draw only few actors and 7-8ms for that is way to much.
so i moved the SAME code that was in the gamestage draw to a separate sub to see if the code is the problem and the same code in a separate sub takes much less time.

and the result is the same my buttons are drawn but i dont understand why doing it with the lgScn2DStage .draw command takes much more time then using the same code in a sub and call the sub in my LG_Render sub.
 

ilan

Expert
Licensed User
Longtime User
i will also explain how i create the actors maybe i do something wrong.

i have 4 lgScn2DGroup. i intialize all my actors with the same sub so i draw everything in the same sub.

this is the code where i create all my actors and add them to the lgscn2dgroups and then to the stage

B4X:
'    'gamestage
    gamestage1.Initialize("gamestage1")
    gamestage1.SetViewport(vpW,vpH,False)
 
'############ BUTTON&LABELS #############################################
  
    levelgroup.Initialize("gamebtn")
    gamebtnGrooup.Initialize("gamebtn")
    menubtnGroup.Initialize("gamebtn") 
    pausebtnGroup.Initialize("gamebtn")     
 
    playbtn(0).Initialize("gamebtn")
    playbtn(1).Initialize("gamebtn")
    playbtn(2).Initialize("gamebtn")
    playbtn(3).Initialize("gamebtn")
    playbtn(4).Initialize("gamebtn")
    playbtn(5).Initialize("gamebtn")
     
    playbtn(0).Tag = "left"
    playbtn(1).Tag = "right"
    playbtn(2).Tag = "fire"
    playbtn(3).Tag = "coin"
    playbtn(4).Tag = "pause"
    playbtn(5).Tag = "level"
    playbtn(0).SetBounds(vpW*0.03,vpH*0.06,vpW*0.11,vpW*0.11)
    playbtn(1).SetBounds(vpW*0.22,vpH*0.06,vpW*0.11,vpW*0.11)
    playbtn(2).SetBounds(vpW*0.81,vpH*0.05,vpW*0.15,vpW*0.15)
    playbtn(3).SetBounds(vpW*0.03,vpH-(vpW*0.1),vpW*0.08,vpW*0.08)
    playbtn(4).SetBounds(vpW*0.89,vpH-(vpW*0.1),vpW*0.08,vpW*0.08)
    playbtn(5).SetBounds(vpW*0.13,vpH-(vpW*0.1),vpW*0.08,vpW*0.08)
 
    For i = 0 To playbtn.Length - 1
        gamebtnGrooup.AddActor(playbtn(i))
        gamestageIP.Initialize("gamestageIP") 
        playbtn(i).AddCaptureListener(gamestageIP)
    Next     
     
    'dead buttons
    deadtitle.Initialize("gamebtn")
    deadbacktomenu.Initialize("gamebtn")
    deadplayagain.Initialize("gamebtn")
    deadtitle.Tag = "title"
    deadbacktomenu.Tag = "deadback"
    deadplayagain.Tag = "deadagain"
 
    deadtitle.SetBounds(vpW/2-(vpW*0.225),vpH*0.45,vpW*0.45,vpW*0.22)
    deadbacktomenu.SetBounds(vpW/2-(vpW*0.225),vpH*0.25,vpW*0.09,vpW*0.09)
    deadplayagain.SetBounds(vpW/2+(vpW*0.135),vpH*0.25,vpW*0.09,vpW*0.09)

    pausebtnGroup.AddActor(deadtitle)
    pausebtnGroup.AddActor(deadplayagain)
    pausebtnGroup.AddActor(deadbacktomenu)
 
    gamestageIP.Initialize("gamestageIP") 
    deadplayagain.AddCaptureListener(gamestageIP)
    deadbacktomenu.AddCaptureListener(gamestageIP)
 
    'menu btns
    menubutton(0).Initialize("gamebtn")
    menubutton(0).Tag = "play"
    menubutton(0).SetBounds(vpW/2 -(vpW*0.15),vpH*1.2,vpW*0.3,vpH*0.18)
    menubutton(1).Initialize("gamebtn")
    menubutton(1).Tag = "title"
    menubutton(1).SetBounds(vpW*0.1,vpH*1.2,vpW*0.8,vpH*0.8)
 
    bitmapfont.Initialize2(lGdx.Files.internal("myfont.fnt"))
    levelbackactor.Initialize("mainlbl")
    levelbackactor.Tag = "levelback"
    levelbackactor.SetBounds(vpW*0.05,vpH*1.2,vpW*0.9,vpH)
    levelgroup.addActor(levelbackactor)
    gamestageIP.Initialize("gamestageIP")
    levelbackactor.addCaptureListener(gamestageIP)

    Dim Style, Style2 As lgScn2DLabelStyle
    Style.Initialize(bitmapfont, bitmapfont.Color.LIGHT_GRAY)
    Style2.Initialize(bitmapfont, bitmapfont.Color.DARK_GRAY)
    Private levelW As Float = vpW*0.09
    Private ystep As Int = 3
 
    Dim centerX, centerY, originX, originY As Float
    centerX = levelbackactor.X + (levelbackactor.Width/2)
    centerY = levelbackactor.Y + (levelbackactor.Height/2)-(levelbackactor.Height*0.06)
  
    For x = 0 To levellbl.Length -1
        If x Mod 4 = 0 And x > 0 Then ystep = ystep -1
        Dim lbl As lgScn2DLabel = levellbl(x)
        lbl.Initialize("",Style2,"mainlbl")
        lbl.SetAlignment2(lbl.ALIGN_Center)
        If x Mod 4 = 0 Then 'left
            originX = centerX - (levelbackactor.Width/4)*1.1
        else if x Mod 4 = 1 Then 'center
            originX = centerX - (levelbackactor.Width/4)*0.375
        else if x Mod 4 = 2 Then 'right
        originX = centerX + (levelbackactor.Width/4)*0.375
        else if x Mod 4 = 3 Then 'right 
            originX = centerX + (levelbackactor.Width/4)*1.1
        End If
     
        If ystep = 3 Then
            originY = centerY + levelbackactor.Height/4
        else if ystep = 2 Then
            originY = centerY
        else if ystep = 1 Then
            originY = centerY - levelbackactor.Height/4 
        End If
     
        lbl.SetBounds(originX-((levelW*1.2)/2),originY-(levelW/2),levelW*1.2,levelW)
        lbl.FontScaleX = 0.85
        lbl.FontScaleY = 0.85
     
        Dim newlvl As levelbtn
        newlvl.Initialize
        newlvl.tag = "mainlbl"
        newlvl.islocked = True
        lbl.Tag = newlvl
        levelgroup.addActor(lbl)
        gamestageIP.Initialize("gamestageIP")
        lbl.addCaptureListener(gamestageIP)
    Next

    levelbackactorTitle.Initialize("Select Level",Style2,"mainlbl")
    levelbackactorTitle.SetAlignment2(levelbackactorTitle.ALIGN_Center)
    levelbackactorTitle.Tag = "levelbacktitle"
    levelbackactorTitle.FontScaleX = 1.2
    levelbackactorTitle.FontScaley = 1.2
    levelbackactorTitle.SetBounds(levelbackactor.x,(levelbackactor.Y+levelbackactor.Height)-vpH*0.25,levelbackactor.X+levelbackactor.Width,vpH*0.25)
    levelgroup.addActor(levelbackactorTitle)
 
    For i = 0 To menubutton.Length - 1
        menubtnGroup.AddActor(menubutton(i))
        gamestageIP.Initialize("gamestageIP") 
        menubutton(i).AddCaptureListener(gamestageIP)
    Next 
     
    gamestage1.AddActor(menubtnGroup)
    gamestage1.AddActor(levelgroup)
    gamestage1.AddActor(gamebtnGrooup)
    gamestage1.AddActor(pausebtnGroup)
 
 
'############ BUTTON&LABELS #############################################
 

Informatix

Expert
Licensed User
Longtime User
sorry, i will explain.

i had in my box2d+libgdx game not a stable FPS. it was most of the time 60 but it goes down and up 50~60.
so i checked for each event i do how much ms it takes (with the Nanotime lib)

all drawing and all loops takes less then 1-2ms. only 2 thing takes much

1. drawing the tilemap from tiled
2. gamestage.draw

drawing the tilemap takes about 4ms
and gamestage.draw takes about 7-8ms

everything is drawn when i call gamestage.draw but i draw only few actors and 7-8ms for that is way to much.
so i moved the SAME code that was in the gamestage draw to a separate sub to see if the code is the problem and the same code in a separate sub takes much less time.

and the result is the same my buttons are drawn but i dont understand why doing it with the lgScn2DStage .draw command takes much more time then using the same code in a sub and call the sub in my LG_Render sub.
Sorry but I understand nothing. All the stage is drawn with this single line:
gamestage.draw
So what is all this code you're talking about? When is it called? By what?
 

ilan

Expert
Licensed User
Longtime User
according to the explenation when i intialize an actor i have to give the event name where everything will be drawn

gamestage.jpg


and this is what i do.

i create an gamebtn event

B4X:
Sub gamebtn_Draw(SpriteBatch As lgSpriteBatch, ParentAlpha As Float)

'....

End sub

and inside i draw all textures on each actor position and size.

for example:

B4X:
SpriteBatch.DrawTex2(firebtnimg,playbtn(2).x+(vpW*0.015),playbtn(2).y+(vpW*0.015),playbtn(2).Width-(vpW*0.03),playbtn(2).Height-(vpW*0.03))

playbtn(2) is an actor and i draw each frame the lgtexture firebtnimg on the actor position and size.

so this is the whole code above.
is this wrong??
 

Informatix

Expert
Licensed User
Longtime User
according to the explenation when i intialize an actor i have to give the event name where everything will be drawn

View attachment 49982

and this is what i do.

i create an gamebtn event

B4X:
Sub gamebtn_Draw(SpriteBatch As lgSpriteBatch, ParentAlpha As Float)

'....

End sub

and inside i draw all textures on each actor position and size.

for example:

B4X:
SpriteBatch.DrawTex2(firebtnimg,playbtn(2).x+(vpW*0.015),playbtn(2).y+(vpW*0.015),playbtn(2).Width-(vpW*0.03),playbtn(2).Height-(vpW*0.03))

playbtn(2) is an actor and i draw each frame the lgtexture firebtnimg on the actor position and size.

so this is the whole code above.
is this wrong??
If the actor is an image or a button, why do you draw it in this event? You need the event only for specific purposes.
You should time the code of each event to know where's the bottleneck.
 
Top