Games [XUI2D] Hello World - First app

Erel

Administrator
Staff member
Licensed User
A simpler example based on Tiled Map Editor is available: [XUI2D] Hello World 2 based on Tile Map

The purpose of this tutorial is to help you run your very first app based on XUI2D.

A great benefit of XUI2D being cross platform is that you can do most of the development in B4J. Developing directly on the desktop is simpler and faster.
Later you can create B4A and B4i projects and add references to the exact same modules.

Before we start, make sure to use the latest version of XUI2D: https://www.b4x.com/android/forum/threads/b4x-xui2d-box2d-game-engine.95208/#content

1. Create a new B4J UI project.
2. Add references to the following internal libraries: jBitmapCreator, jGameViewHelper (for the sounds), jXUI and XUI2D.
3. Add the five X2 class modules to the project. I'm always adding them as linked modules though it is possible that you will want to modify them in the future so it is fine to copy them to the project.

You will see an error in the logs about a missing game type.
You should add a new class module named Game. Unlike the other X2 modules which are generic and ideally will not need to be modified, the Game class is specific to your game.

A template is included inside XUI2D library (not available in v0.91.0):
SS-2018-07-18_14.40.55.png


4. Create the layout.

It should include two ImageViews named ivBackground and ivForeground and a label named lblStats at the bottom (anchored to the BOTTOM and BOTH sides).

The ImageViews layout is set in the designer script:
B4X:
GameRatio = 1.333 'width / height - change as needed

ScreenRatio = 100%x / 100%y
If ScreenRatio < GameRatio Then
 ivForeground.SetLeftAndRight(0, 100%x)
 ivForeground.Height = ivForeground.Width / GameRatio
 ivForeground.VerticalCenter = 50%y
Else
 ivForeground.SetTopAndBottom(0, 100%y)
 ivForeground.Width = ivForeground.Height * GameRatio
 ivForeground.HorizontalCenter = 50%x
End If
ivBackground.SetLeftAndRight(ivForeground.Left, ivForeground.Right)
ivBackground.SetTopAndBottom(ivForeground.Top, ivForeground.Bottom)
The above code makes sure that the width to height ratio is always as defined in GameRatio variable.
Set the main form color to black.

Main module code to:
B4X:
#Region Project Attributes
   #MainFormWidth: 800
   #MainFormHeight: 600
#End Region

Sub Process_Globals
   Private fx As JFX
   Private MainForm As Form
   Private gm As Game
End Sub

Sub AppStart (Form1 As Form, Args() As String)
   MainForm = Form1
   MainForm.Show
   gm.Initialize(MainForm.RootPane)
   gm.X2.Start
End Sub

Sub MainForm_Resize (Width As Double, Height As Double)
   gm.Resize
End Sub

You can run the program now. You should see this:

SS-2018-07-18_15.03.36.png


The small crosses are drawn because DebugDraw is enabled. They mark the world grid.
The next tutorial will cover the units used in XUI2D. It is very important to understand them. For now you should know that all the dimensions are measured in meters.
Unlike the pixels coordinates which start at the top of the screen and go downwards, the world coordinates start at the bottom and go upwards.

5. Lets add a few bodies.

This code creates a rectangle body and position it at the top of the screen:
B4X:
Private Sub CreateBlock
   'create the body
   Dim bd As B2BodyDef
   bd.BodyType = bd.TYPE_DYNAMIC 'regular body
   Dim x As Float = X2.RndFloat(X2.ScreenAABB.BottomLeft.X, X2.ScreenAABB.TopRight.X)
   bd.Position = X2.CreateVec2(x, 5)
   Dim wrapper As X2BodyWrapper = X2.CreateBodyAndWrapper(bd, Null, "Block")
   'add fixture
   Dim rect As B2PolygonShape
   rect.Initialize
   rect.SetAsBox(0.25, 0.5) 'parameters are half width and half height. Size is 0.5 x 1 meters.
   wrapper.Body.CreateFixture2(rect, 1)
End Sub

Adding a new block approximately every 500ms:
B4X:
Public Sub Tick (GS As X2GameStep)
   If X2.RndFloat(0, 500) < X2.TimeStepMs Then CreateBlock
End Sub

6. Drawing the background:
B4X:
Private Sub CreateStaticBackground
   Dim bc As BitmapCreator
   bc.Initialize(ivBackground.Width / xui.Scale, ivBackground.Height / xui.Scale)
   bc.FillGradient(Array As Int(0xFF006EFF, 0xFFB04700), bc.TargetRect, "TOP_BOTTOM")
   ivBackground.SetBitmap(bc.Bitmap)
End Sub
The background is static so it is only needed to be drawn once when Game is initialized.

At this point it should look like this:

SS-2018-07-18_15.27.34.png


Notes:
- The blocks fall out of the screen.
- The blocks do not yet have real graphics. We can only see them because debug drawing is enabled.

To fix the first issue we need to add a ground body:
B4X:
Private Sub CreateGround
   Dim bd As B2BodyDef
   bd.BodyType = bd.TYPE_STATIC 'the engine should not move it
   Dim wrapper As X2BodyWrapper = X2.CreateBodyAndWrapper(bd, Null, "ground")
   Dim edge As B2EdgeShape
   edge.Initialize(X2.CreateVec2(-20, 1), X2.CreateVec2(20, 1))
   wrapper.Body.CreateFixture2(edge, 1)
End Sub
This sub should be called once. Note that we can use X2.ScreenAABB to get the screen coordinates.

7. The last step is to add graphics to the blocks. There are all kinds of ways to do it. In most cases the graphics will come from image files. The best way to kill the game performance is by frequently creating new graphics, especially with antialiasing enabled.

I will use a canvas here to draw a rectangle:
B4X:
'The canvas is returned from a cache of square canvases.
'It will be larger than the size requests.
Dim CvsMinSize As Int = X2.MetersToBCPixels(Max(BoxSize.X, BoxSize.Y))
'We need to divide the size by X2.BmpSmoothScale.
Dim Cvs As B4XCanvas = X2.GraphicCache.GetCanvas(CvsMinSize / X2.BmpSmoothScale)
Dim r As B4XRect
r.Initialize(0, 0, X2.MetersToBCPixels(BoxSize.X) + 1, X2.MetersToBCPixels(BoxSize.Y) + 1)
Cvs.ClearRect(r)
Cvs.DrawRect(r, 0xFF2100AB, True, 0)
'Create a scaled bitmap type. This holds the bitmap and its scale (1 in this case)
Dim sb As X2ScaledBitmap
sb.Bmp = Cvs.CreateBitmap.Crop(0, 0, r.Right, r.Bottom)
sb.Scale = 1
'last step is to add the scaled bitmap to the cache.
X2.GraphicCache.PutGraphic("block", Array(sb))
Note that I've also set the graphic name property:
B4X:
wrapper.GraphicName = "Block"

The project is attached. I've also added a circle body based on an image file.


test.gif
 

Attachments

  • HelloWorld.zip
    36.5 KB · Views: 234
Last edited:

sorex

Expert
Licensed User
Anyway, if you pay close attention to that tutorial you will see that the class template is not available in v0.9.

Indeed, I read that but I have 1.00 in my libs panel.

Custom view (Xui) was added in the class menu tho.
 

ilan

Expert
Licensed User
hi erel, i have tried the example but something doesn't look very much "box2d".
the collisions are not precise. you can see sometimes that 2 bodies are going through each other.

i am not sure what is the reason but trying a simple example using jbox2d gives me a better (more realistic) result.

https://drive.google.com/open?id=1IJKmhUysImkD00_HL0_9yvEOqk0e_0V5
 

sorex

Expert
Licensed User
you can see it in the animated image above aswell.

for example the red block at the bottom left should be at angle 0 since it's on a flat surface.

I had some test with it yesterday (B4J version) and it's very interesting altho it's a lot of code to understand.
 

sorex

Expert
Licensed User
nono, I meant the creation of an image + fixtures etc mainly the code in the main game module.

the only thing missing to go really advanced is the joints.

well done for the current state by the way.
 

ilan

Expert
Licensed User
2. Set bd.Bullet = True

this setting is only for very fast moving bodies. so if i add a very high velocity to a body i could use that but for simple bodies that fall down this is not the right way to fix it. a bullet body will use much more calculation time than a non bullet body so change all bodies to a bullet body will decrease performance.

i see that you dont set the density in your example. what is the default density that i set when you create a body?

do you want to have a look on how the bodies are set in my example using jbox2d?

EDIT: do u set the world.step in your example?
in my projects i use this settings

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

it is recommended to update the world with this step. this is the time the all collisions are updated.

worldstep.png
 

ilan

Expert
Licensed User
ok i found the wrong settings:

change this:

B4X:
World.TimeStep(TimeStepMs / 1000, 2, 3)

to this:

B4X:
World.TimeStep(TimeStepMs / 1000, 8, 3)
 

Erel

Administrator
Staff member
Licensed User
see that you dont set the density in your example. what is the default density that i set when you create a body?
The Density is set here:
B4X:
Dim f As B2Fixture = wrapper.Body.CreateFixture2(rect, 1)

ok i found the wrong settings:
There is no right or wrong settings. You need to find out the correct balance between performance and accuracy (and you must test it on all relevant platforms).

I will make it configurable so you won't need to change X2Utils code.
 

ilan

Expert
Licensed User
The Density is set here:
B4X:
Dim f As B2Fixture = wrapper.Body.CreateFixture2(rect, 1)

ok. so f.density does nothing?
 

walterf25

Expert
Licensed User
The purpose of this tutorial is to help you run your very first app based on XUI2D.

A great benefit of XUI2D being cross platform is that you can do most of the development in B4J. Developing directly on the desktop is simpler and faster.
Later you can create B4A and B4i projects and add references to the exact same modules.

Before we start, make sure to use the latest version of XUI2D: https://www.b4x.com/android/forum/threads/b4x-xui2d-box2d-game-engine.95208/#content

1. Create a new B4J UI project.
2. Add references to the following internal libraries: jBitmapCreator, jGameViewHelper (for the sounds), jXUI and XUI2D.
3. Add the five X2 class modules to the project. I'm always adding them as linked modules though it is possible that you will want to modify them in the future so it is fine to copy them to the project.

You will see an error in the logs about a missing game type.
You should add a new class module named Game. Unlike the other X2 modules which are generic and ideally will not need to be modified, the Game class is specific to your game.

A template is included inside XUI2D library (not available in v0.91.0):
SS-2018-07-18_14.40.55.png


4. Create the layout.

It should include two ImageViews named ivBackground and ivForeground and a label named lblStats at the bottom (anchored to the BOTTOM and BOTH sides).

The ImageViews layout is set in the designer script:
B4X:
GameRatio = 1.333 'width / height - change as needed

ScreenRatio = 100%x / 100%y
If ScreenRatio < GameRatio Then
 ivForeground.SetLeftAndRight(0, 100%x)
 ivForeground.Height = ivForeground.Width / GameRatio
 ivForeground.VerticalCenter = 50%y
Else
 ivForeground.SetTopAndBottom(0, 100%y)
 ivForeground.Width = ivForeground.Height * GameRatio
 ivForeground.HorizontalCenter = 50%x
End If
ivBackground.SetLeftAndRight(ivForeground.Left, ivForeground.Right)
ivBackground.SetTopAndBottom(ivForeground.Top, ivForeground.Bottom)
The above code makes sure that the width to height ratio is always as defined in GameRatio variable.
Set the main form color to black.

Main module code to:
B4X:
#Region Project Attributes
   #MainFormWidth: 800
   #MainFormHeight: 600
#End Region

Sub Process_Globals
   Private fx As JFX
   Private MainForm As Form
   Private gm As Game
End Sub

Sub AppStart (Form1 As Form, Args() As String)
   MainForm = Form1
   MainForm.Show
   gm.Initialize(MainForm.RootPane)
   gm.X2.Start
End Sub

Sub MainForm_Resize (Width As Double, Height As Double)
   gm.Resize
End Sub

You can run the program now. You should see this:

SS-2018-07-18_15.03.36.png


The small crosses are drawn because DebugDraw is enabled. They mark the world grid.
The next tutorial will cover the units used in XUI2D. It is very important to understand them. For now you should know that all the dimensions are measured in meters.
Unlike the pixels coordinates which start at the top of the screen and go downwards, the world coordinates start at the bottom and go upwards.

5. Lets add a few bodies.

This code creates a rectangle body and position it at the top of the screen:
B4X:
Private Sub CreateBlock
   'create the body
   Dim bd As B2BodyDef
   bd.BodyType = bd.TYPE_DYNAMIC 'regular body
   Dim x As Float = X2.RndFloat(X2.ScreenAABB.BottomLeft.X, X2.ScreenAABB.TopRight.X)
   bd.Position = X2.CreateVec2(x, 5)
   Dim wrapper As X2BodyWrapper = X2.CreateBodyAndWrapper(bd, Null, "Block")
   'add fixture
   Dim rect As B2PolygonShape
   rect.Initialize
   rect.SetAsBox(0.25, 0.5) 'parameters are half width and half height. Size is 0.5 x 1 meters.
   wrapper.Body.CreateFixture2(rect, 1)
End Sub

Adding a new block approximately every 500ms:
B4X:
Public Sub Tick (GS As X2GameStep)
   If X2.RndFloat(0, 500) < X2.TimeStepMs Then CreateBlock
End Sub

6. Drawing the background:
B4X:
Private Sub CreateStaticBackground
   Dim bc As BitmapCreator
   bc.Initialize(ivBackground.Width / xui.Scale, ivBackground.Height / xui.Scale)
   bc.FillGradient(Array As Int(0xFF006EFF, 0xFFB04700), bc.TargetRect, "TOP_BOTTOM")
   ivBackground.SetBitmap(bc.Bitmap)
End Sub
The background is static so it is only needed to be drawn once when Game is initialized.

At this point it should look like this:

SS-2018-07-18_15.27.34.png


Notes:
- The blocks fall out of the screen.
- The blocks do not yet have real graphics. We can only see them because debug drawing is enabled.

To fix the first issue we need to add a ground body:
B4X:
Private Sub CreateGround
   Dim bd As B2BodyDef
   bd.BodyType = bd.TYPE_STATIC 'the engine should not move it
   Dim wrapper As X2BodyWrapper = X2.CreateBodyAndWrapper(bd, Null, "ground")
   Dim edge As B2EdgeShape
   edge.Initialize(X2.CreateVec2(-20, 1), X2.CreateVec2(20, 1))
   wrapper.Body.CreateFixture2(edge, 1)
End Sub
This sub should be called once. Note that we can use X2.ScreenAABB to get the screen coordinates.

7. The last step is to add graphics to the blocks. There are all kinds of ways to do it. In most cases the graphics will come from image files. The best way to kill the game performance is by frequently creating new graphics, especially with antialiasing enabled.

I will use a canvas here to draw a rectangle:
B4X:
'The canvas is returned from a cache of square canvases.
'It will be larger than the size requests.
Dim CvsMinSize As Int = X2.MetersToBCPixels(Max(BoxSize.X, BoxSize.Y))
'We need to divide the size by X2.BmpSmoothScale.
Dim Cvs As B4XCanvas = X2.GraphicCache.GetCanvas(CvsMinSize / X2.BmpSmoothScale)
Dim r As B4XRect
r.Initialize(0, 0, X2.MetersToBCPixels(BoxSize.X) + 1, X2.MetersToBCPixels(BoxSize.Y) + 1)
Cvs.ClearRect(r)
Cvs.DrawRect(r, 0xFF2100AB, True, 0)
'Create a scaled bitmap type. This holds the bitmap and its scale (1 in this case)
Dim sb As X2ScaledBitmap
sb.Bmp = Cvs.CreateBitmap.Crop(0, 0, r.Right, r.Bottom)
sb.Scale = 1
'last step is to add the scaled bitmap to the cache.
X2.GraphicCache.PutGraphic("block", Array(sb))
Note that I've also set the graphic name property:
B4X:
wrapper.GraphicName = "Block"

The project is attached. I've also added a circle body based on an image file.


View attachment 70086
Can't run the HelloWorld example, i have some errors in the X2DebugDraw class right at this function

B4X:
Private Sub DrawContactPoints
    Dim contact As B2Contact = X2.mWorld.FirstContact
    Dim wm As B2WorldManifold
    Do While contact <> Null
        contact.GetWorldManifold(wm)
        For i = 0 To wm.PointCount - 1
            Dim WorldPoint As B2Vec2 = wm.GetPoint(i)
            Dim vec As B2Vec2 = X2.WorldPointToMainBC(WorldPoint.X, WorldPoint.Y)
            vec.MultiplyThis(DebugScale)
            cvs.DrawCircle(vec.X, vec.Y, 3, 0xFFFF2E00, True, 0)
        Next
        contact = contact.NextContact
    Loop
End Sub

the variable wm doesn't have a PointCount property and it also complains that the vec variable doesn't have a X property.

Am I missing another module/class?

I have all X2 classes in the project.

Walter
 

walterf25

Expert
Licensed User
Top