Games [x2][solved] Help needed regarding screen and objects Positions

fredo

Well-Known Member
Licensed User
Longtime User
The enclosed test project is supposed to be a turn-based top down RPG board game for 2 characters.
Planned course: Players shall roll their dice alternately and advance the corresponding number of fields, sometimes choosing which way to go.

The x2 examples were used to create the basic structures and load the graphics.

2019-08-01_18-31-53.png


2019-08-01_18-33-37.png

B4X:
' ------- -------------- ------
#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files"
' This is required to copy all files from the "Shared Files" folder to the "Files" folder of the respective platform.
'     Erel --> https://www.b4x.com/android/forum/threads/xui2d-cross-platform-tips.96815/#content
' ------- -------------- ------

' ------- -------------- ------
' X2Test1 "Basic structures"
'     x2notes    --> https://www.b4x.com/android/forum/threads/some-notes-to-better-understand-the-xui2d-examples.107702/
'     x2examples --> https://www.b4x.com/android/forum/threads/xui2d-example-pack.96454/
'   @Gunter’s roadmap into the game dev world --> https://www.b4x.com/android/forum/threads/video-tutorial.99172/#post-624377
' ------- -------------- ------

#if B4A
'ignore DIP related warnings as they are not relevant when working with BitmapCreator.
#IgnoreWarnings: 6
#end if

Sub Class_Globals
    '
    ' Basic x2 libraries that are needed in every game
    Private xui As XUI 'ignore
    Public X2 As X2Utils
    Public world As B2World
    '
    ' Layout elements
    Private ivForeground As B4XView
    Private ivBackground As B4XView
    Public lblStats As B4XView
    Private pnlTouch As B4XView
    '
    ' TileMap specific definitions
    Public TileMap As X2TileMap
    Public Const ObjectLayer As String = "Object Layer 1"
    '
    ' ExtraClasses
    Private mChar1 As Figure
    Private mChar2 As Figure
    
    ' Gamestate
    Private GameOverState As Boolean
    
    ' Bodies and joints
    Private Border As X2BodyWrapper
    Private MouseMotor As B2MotorJoint
    
'    Type EnemyTemplates(Template As X2TileObjectTemplate, XPosition As Float)
'    Private EnemyTemplatesList As List   
    
    Type FigureTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private FigureTemplatesList As List   
    
    Type PointTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private PointTemplatesList As List   
    
    Type TextTemplates (Template As X2TileObjectTemplate, XPosition As Float)
    Private TextTemplatesList As List   
    

End Sub

Public Sub Initialize (Parent As B4XView)
    '
    ' ──────────────────────────────────
    ' Preparations for Layout and Views
    ' ──────────────────────────────────
    Parent.LoadLayout("GameLayout")
    lblStats.TextColor = xui.Color_Black
    lblStats.Color = 0x88ffffff
    lblStats.Font = xui.CreateDefaultBoldFont(20)
    
    ' ──────────────────────────────────
    ' Preparations for World
    ' ──────────────────────────────────
    world.Initialize("world", world.CreateVec2(0, -10))
    X2.Initialize(Me, ivForeground, world)
    
    Dim WorldWidth As Float = 36 'meters
    Dim WorldHeight As Float = WorldWidth / 1.333 'same ratio as in the designer script
    X2.ConfigureDimensions(world.CreateVec2(WorldWidth / 2, WorldHeight / 2), WorldWidth)
    
    ' ──────────────────────────────────
    ' Preparations for graphics
    ' ──────────────────────────────────
    GraphicCache_Put_Characters
    
    ' ──────────────────────────────────
    ' Preparations for screen
    ' ──────────────────────────────────
    SetBackground
    'CreateStaticBackground
    'CreateBorder
    
    
    ' ──────────────────────────────────
    ' Preparations for sounds
    ' ──────────────────────────────────
    'X2.SoundPool.AddSound("small jump", File.DirAssets, "small_jump.mp3")
    'X2.SoundPool.AddSound("big jump", File.DirAssets, "big_jump.mp3")
    'X2.SoundPool.AddSound("powerup", File.DirAssets, "powerup.mp3")     
    
    ' ──────────────────────────────────
    ' Debug settings
    ' ──────────────────────────────────
    X2.EnableDebugDraw    ' Comment out to disable debug drawing
    
End Sub

Private Sub SetWorldCenter
    ' The map size will not be identical to the screen size.
    ' This happens because the tile size in (bc) pixels needs to be a whole number.
    ' So we need to update the world center and move the map to the center.
    X2.UpdateWorldCenter(TileMap.MapAABB.Center)
End Sub
Public Sub WorldCenterUpdated (gs As X2GameStep)
    CreateEnemies
End Sub
private Sub SetBackground
    'X2.SetBitmapWithFitOrFill(ivBackground, xui.LoadBitmapResize(File.DirAssets, "mybackgroundimage.jpg", ivBackground.Width / 2, ivBackground.Height / 2, False))
End Sub
 
Private Sub GraphicCache_Put_Characters
    Log("#-Sub game.GraphicCache_Put_Characters")

    ' Use bitmap without the transparency placeholdercolor:
    Dim bc As BitmapCreator = X2.BitmapToBC( xui.LoadBitmap(File.DirAssets, "RPGCharacterSprites32x32.png"), 1)
    RemovePseudoTransparentColor(bc, "#ff00ff") ' pink, magenta
    Dim bmp As B4XBitmap = bc.Bitmap
    
    Dim NumberOfSprites As Int = 12
    Dim RowWidth As Int = 32
    Dim RowHeight As Int = 32
    Dim CharHeightMeters As Int = 3
    '
    Dim RowOfChar1 As Int = 2
    Dim character1 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar1, NumberOfSprites * RowWidth, RowHeight)
    Dim AllChar1 As List = X2.ReadSprites(character1, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
    X2.GraphicCache.PutGraphic("character1 front walking", Array(AllChar1.Get(0), AllChar1.Get(1), AllChar1.Get(2), AllChar1.Get(3)))
    X2.GraphicCache.PutGraphic("character1 front standing", Array(AllChar1.Get(3)))
    X2.GraphicCache.PutGraphic("character1 back walking", Array(AllChar1.Get(4), AllChar1.Get(5), AllChar1.Get(6), AllChar1.Get(7)))
    X2.GraphicCache.PutGraphic("character1 back standing", Array(AllChar1.Get(7)))
    X2.GraphicCache.PutGraphic("character1 side walking", Array(AllChar1.Get(8), AllChar1.Get(9), AllChar1.Get(10)) )
    X2.GraphicCache.PutGraphic("character1 side standing", Array(AllChar1.Get(9)) )
    '
    Dim RowOfChar2 As Int = 3
    Dim character2 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar2, NumberOfSprites * RowWidth,  RowHeight)
    Dim AllChar2 As List = X2.ReadSprites(character2, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
    X2.GraphicCache.PutGraphic("character2 front walking", Array(AllChar2.Get(0), AllChar2.Get(1), AllChar2.Get(2), AllChar2.Get(3)))
    X2.GraphicCache.PutGraphic("character2 front standing", Array(AllChar2.Get(3)))
    X2.GraphicCache.PutGraphic("character2 back walking", Array(AllChar2.Get(4), AllChar2.Get(5), AllChar2.Get(6), AllChar2.Get(7)))
    X2.GraphicCache.PutGraphic("character2 back standing", Array(AllChar2.Get(7)))
    X2.GraphicCache.PutGraphic("character2 side walking", Array(AllChar2.Get(8), AllChar2.Get(9), AllChar2.Get(10)) )
    X2.GraphicCache.PutGraphic("character2 side standing", Array(AllChar2.Get(9)) )
    '
    
End Sub

Private Sub RemovePseudoTransparentColor(TilesBC As BitmapCreator, clrstring As String)
    ' Erel --> https://www.b4x.com/android/forum/threads/x2-how-to-use-transparent-color.108173/#post-676534
    Dim clr As Int = 0xff000000 + Bit.ParseInt(clrstring.SubString(1), 16)
    Dim ptranspm As PremultipliedColor
    Dim trans As PremultipliedColor
    Dim pm As PremultipliedColor
    Dim argb As ARGBColor
    TilesBC.ColorToARGB(clr, argb)
    TilesBC.ARGBToPremultipliedColor(argb, ptranspm)
    For y = 0 To TilesBC.mHeight - 1
        For x = 0 To TilesBC.mWidth - 1
            TilesBC.GetPremultipliedColor(x, y, pm)
            If Bit.And(0xff, pm.r) = ptranspm.r And Bit.And(0xff, pm.g) = ptranspm.g And Bit.And(0xff, pm.b) = ptranspm.b And Bit.And(0xff, pm.a) = ptranspm.a Then
                TilesBC.SetPremultipliedColor(x, y, trans)
            End If
        Next
    Next
End Sub

private Sub CreateObjects
    Log("#-Sub game.CreateObjects")
    FigureTemplatesList.Initialize
    PointTemplatesList.Initialize
    TextTemplatesList.Initialize

    Dim ol As X2ObjectsLayer = TileMap.Layers.Get(ObjectLayer)
    For Each TileMapTemplateX As X2TileObjectTemplate In ol.ObjectsById.Values
        
        If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("p") Then
            Dim pt As PointTemplates
            pt.Template = TileMapTemplateX
            pt.XPosition = TileMapTemplateX.Position.X
            PointTemplatesList.Add(pt)
            TileMap.CreateObject(TileMapTemplateX)
            
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("text") Then
            Dim tt As TextTemplates
            tt.Template = TileMapTemplateX
            tt.XPosition = TileMapTemplateX.Position.X
            TextTemplatesList.Add(tt)
            TileMap.CreateObject(TileMapTemplateX)
        
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("char") Then
            Dim ft As FigureTemplates
            ft.Template = TileMapTemplateX
            ft.XPosition = TileMapTemplateX.Position.X
            FigureTemplatesList.Add(ft)
            
            Dim bwChX As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
            bwChX.Body.BodyType = bwChX.Body.TYPE_KINEMATIC
            
            If TileMapTemplateX.Name.ToLowerCase.EndsWith("1") Then
                mChar1.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar1.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
            else If TileMapTemplateX.Name.ToLowerCase.EndsWith("2") Then
                mChar2.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar2.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
            End If
            
        End If
    Next
    
    FigureTemplatesList.SortType("XPosition", True)
    PointTemplatesList.SortType("XPosition", True)
    TextTemplatesList.SortType("XPosition", True)
    
    Log("#-  x206, FigureTemplatesList.size = " & FigureTemplatesList.Size)
    Log("#-  x207, PointTemplatesList.size  = " & PointTemplatesList.Size)
    Log("#-  x208, TextTemplatesList.size   = " & TextTemplatesList.Size)
    
End Sub
Private Sub CreateEnemies
'    Do While EnemyTemplatesList.Size > 0
'        Dim et As EnemyTemplates = EnemyTemplatesList.Get(0)
'        If et.XPosition <= X2.ScreenAABB.TopRight.X Then
'            Dim bw As X2BodyWrapper = TileMap.CreateObject(et.Template)
'            Dim enX As Enemy
'            enX.Initialize(bw) 'this sets the delegate
'            enX.IsGhost = (bw.GraphicName = "ghost")
'            EnemyTemplatesList.RemoveAt(0)
'        Else
'            Exit
'        End If
'    Loop
End Sub
Private Sub CreateRevJoint12
'    Dim template As X2TileObjectTemplate = TileMap.GetObjectTemplateByName(ObjectLayer, "hinge")
'    Dim revdef As B2RevoluteJointDef
'    revdef.Initialize(BodyXy1.Body, BodyXy2.Body, template.BodyDef.Position)
'    revdef.SetLimits(0, cPI / 2)
'    revdef.LimitEnabled = True
'    revdef.MaxMotorTorque = 10
'    revjoint12 = world.CreateJoint(revdef)
'    revjoint12.MotorEnabled = True
'    BodyXy2.Body.GravityScale = 0
End Sub
Private Sub CreateStaticBackground
    Dim bc As BitmapCreator
    bc.Initialize(ivBackground.Width / xui.Scale / 2, ivBackground.Height / xui.Scale / 2)
    bc.FillGradient(Array As Int(0xFF001AAC, 0xFFC5A400), bc.TargetRect, "TOP_BOTTOM")
    X2.SetBitmapWithFitOrFill(ivBackground, bc.Bitmap)
End Sub
Private Sub CreateBorder
    TileMap.CreateObject(TileMap.GetObjectTemplateByName(ObjectLayer, "border"))
End Sub

' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub Resize
    X2.ImageViewResized
End Sub

Public Sub DrawingComplete
    TileMap.DrawingComplete
End Sub

'Return True to stop the game loop.
Public Sub BeforeTimeStep (GS As X2GameStep) As Boolean
    If GameOverState Then
        Return True
    End If

    Return False
End Sub

Public Sub Tick (GS As X2GameStep)
'    Log("#-Sub game.Tick, gs.GameTimeMs = " & GS.GameTimeMs)
    TileMap.DrawScreen(Array("Tile Layer 1"), GS.DrawingTasks)
    mChar1.Tick(GS)
    mChar2.Tick(GS)
End Sub

' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub GameOver
    X2.SoundPool.StopMusic
    X2.SoundPool.PlaySound("gameover")
    X2.AddFutureTask(Me, "Set_GameOver", 3500, Null)
End Sub
Private Sub Set_GameOver (ft As X2FutureTask)
    GameOverState = True
    Sleep(500)
    StartGame
End Sub
Public Sub StopGame
    X2.SoundPool.StopMusic
    X2.Stop
End Sub

Public Sub StartGame
    If X2.IsRunning Then Return
    X2.Reset
    X2.UpdateWorldCenter(X2.CreateVec2(X2.ScreenAABB.Width / 2, X2.ScreenAABB.Height / 2))
    GameOverState = False

    ' ──────────────────────────────────
    ' Preparations for Tilemap
    ' ──────────────────────────────────
    TileMap.Initialize(X2, File.DirAssets, "TiledMapFile_proj01.json", ivBackground)
    Dim TileSizeMeters As Float = X2.ScreenAABB.Height / TileMap.TilesPerColumn
    TileMap.SetSingleTileDimensionsInMeters(TileSizeMeters, TileSizeMeters)
    SetWorldCenter    ' Update the world center based on the map size
    TileMap.PrepareObjectsDef(ObjectLayer)

    Border = TileMap.CreateObject2ByName(ObjectLayer, "border")   
    
    
    ' ──────────────────────────────────
    ' Draw Tilemap
    ' ──────────────────────────────────
    Dim tasks As List
    tasks.Initialize
    TileMap.Draw(Array("Tile Layer 1"), TileMap.MapAABB, tasks)
    For Each dt As DrawTask In tasks
        If dt.IsCompressedSource Then
            TileMap.CurrentBC.DrawCompressedBitmap(dt.Source, dt.SrcRect, dt.TargetX, dt.TargetY)
        End If
    Next
    
    
    ' ──────────────────────────────────
    ' Preparations for bodies
    ' ──────────────────────────────────
    CreateObjects


    ' ──────────────────────────────────
    ' Motors for Characters
    ' ──────────────────────────────────
    Dim MotorDefChar1 As B2MotorJointDef
    MotorDefChar1.Initialize(Border.Body, mChar1.bw.Body )
    MotorDefChar1.MaxMotorTorque = 1
    MotorDefChar1.MaxMotorForce = 1
    MotorDefChar1.CollideConnected = True 'let the Char1 collide with the borders
    MouseMotor = X2.mWorld.CreateJoint(MotorDefChar1)
    MouseMotor.CorrectionFactor = 0.1

    Dim MotorDefChar2 As B2MotorJointDef
    MotorDefChar2.Initialize(Border.Body, mChar2.bw.Body )
    MotorDefChar2.MaxMotorTorque = 1
    MotorDefChar2.MaxMotorForce = 1
    MotorDefChar2.CollideConnected = True 'let the Char2 collide with the borders
    MouseMotor = X2.mWorld.CreateJoint(MotorDefChar2)
    MouseMotor.CorrectionFactor = 0.1

    
    ' ──────────────────────────────────
    ' Start the Main loop
    ' ──────────────────────────────────
    X2.Start
    
End Sub
 
Sub pnlTouch_Touch (Action As Int, X As Float, Y As Float)
    If Action = pnlTouch.TOUCH_ACTION_MOVE_NOTOUCH Then Return

    Dim WorldPoint As B2Vec2 = X2.ScreenPointToWorld(X, Y)
    Dim MainBCPoint As B2Vec2 = X2.WorldPointToMainBC(WorldPoint.X, WorldPoint.Y)
    
    If Action = pnlTouch.TOUCH_ACTION_DOWN Then
        Dim FirstPointBC As B2Vec2 = X2.WorldPointToMainBC(mChar1.bw.Body.Position.X, mChar1.bw.Body.Position.Y)
        
'        'Clone the paths before modifying them.
'        PathMainBCForward = PathMainBCForward.Clone
'        PathMainBCForward.Reset(FirstPointBC.X, FirstPointBC.Y)
'        PathMainBCBackwards = PathMainBCBackwards.Clone
'        PathMainBCBackwards.Reset(FirstPointBC.X, FirstPointBC.Y)
'        PathWorld.Clear
        
    End If
    
'    If PathWorld.Size > 0 Then
'        Dim PrevPoint As B2Vec2 = PathWorld.Get(PathWorld.Size - 1)
'        Dim distance As B2Vec2 = PrevPoint.CreateCopy
'        distance.SubtractFromThis(WorldPoint)
'        'to improve performance we skip very close points.
'        If distance.LengthSquared < 0.1 Then
'            Return
'        End If
'    End If
    
'    PathMainBCForward = PathMainBCForward.Clone
'    PathMainBCForward.LineTo(MainBCPoint.X, MainBCPoint.Y)
'    PathWorld.Add(WorldPoint)
    
End Sub

Since the interaction of the X2 framework components has not yet been sufficiently understood, concrete references to the required "best practices" would be needed.

In order to continue with the development, the following problem points need to be solved:
  1. The Bitmap of the Tilemap should have the same area of extension as the area of the form.
  2. The Characters should have their starting position on the sand areas on the left side.
  3. The characters should "drive" by motorjoint from one object-point (e.g. "p1") to a certain other object-point (e.g. "p6") on a path along the points series.
  4. If a character collides with a text object, an event should be triggered.
 

Attachments

  • Testproject_01_013.zip
    378.4 KB · Views: 345

Erel

B4X founder
Staff member
Licensed User
Longtime User
Tip: developers who download the project should comment the #CustomBuildAction line.

You want the world to be 36 x 36 meters. Your current world is set to be 36 x (36 * 1.333) meters.
Set GameRatio in the designer script to 1.
And change the world configurations in Game.Initialize to:
B4X:
Dim WorldWidth As Float = 36 'meters
Dim WorldHeight As Float = WorldWidth / 1 'same ratio as in the designer script
Change the bodies type to dynamic. You cannot use forces and motors with kinematic types.
I guess that you should set the gravity to 0, 0 and maybe also check 'is sensor'.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User

fredo

Well-Known Member
Licensed User
Longtime User
... use a MotorJoint to move to a different location ...

OK, inspired by the mouse example
B4X:
' ------- -------------- ------
#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files"

' This is required to copy all files from the "Shared Files" folder to the "Files" folder of the respective platform.
'     Erel --> https://www.b4x.com/android/forum/threads/xui2d-cross-platform-tips.96815/#content
' ------- -------------- ------

' ------- -------------- ------
' X2Test1 "Basic structures"
'     x2notes    --> https://www.b4x.com/android/forum/threads/some-notes-to-better-understand-the-xui2d-examples.107702/
'     x2examples --> https://www.b4x.com/android/forum/threads/xui2d-example-pack.96454/
'   @Gunter’s roadmap into the game dev world --> https://www.b4x.com/android/forum/threads/video-tutorial.99172/#post-624377
' ------- -------------- ------

#if B4A
'ignore DIP related warnings as they are not relevant when working with BitmapCreator.
#IgnoreWarnings: 6
#end if

Sub Class_Globals
    '
    ' Basic x2 libraries that are needed in every game
    Private xui As XUI 'ignore
    Public X2 As X2Utils
    Public world As B2World
 
    '
    ' TileMap specific definitions
    Public TileMap As X2TileMap
    Public Const ObjectLayer As String = "Object Layer 1"
    '
    ' ExtraClasses
    Private mChar1 As Figure
    Private mChar2 As Figure
 
    ' Gamestate
    Private GameOverState As Boolean
 
    ' Bodies and joints
    Private Border As X2BodyWrapper
    Private CharMotor As B2MotorJoint
 
    Private PathMainBCForward As BCPath
    Private PathMainBCBackwards As BCPath
    Private PathWorld As List
    Private BrushRed As BCBrush
 
 
    ' Templates
'    Type EnemyTemplates(Template As X2TileObjectTemplate, XPosition As Float)
'    Private EnemyTemplatesList As List
 
    Type FigureTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private FigureTemplatesList As List
 
    Type PointTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private PointTemplatesList As List
 
    Type TextTemplates (Template As X2TileObjectTemplate, XPosition As Float)
    Private TextTemplatesList As List
 

    ' Layout elements
    Private ivForeground As B4XView
    Private ivBackground As B4XView
    Public lblStats As B4XView
    Private pnlTouch As B4XView
    Private RadioButton1 As RadioButton
    Private RadioButton2 As RadioButton
End Sub

Public Sub Initialize (Parent As B4XView)
    '
    ' ──────────────────────────────────
    ' Preparations for Layout and Views
    ' ──────────────────────────────────
    Parent.LoadLayout("GameLayout")
    lblStats.TextColor = xui.Color_Black
    lblStats.Color = 0x88ffffff
    lblStats.Font = xui.CreateDefaultBoldFont(20)
 
    ' ──────────────────────────────────
    ' Preparations for World
    ' ──────────────────────────────────
    world.Initialize("world", world.CreateVec2(0, 0))
    X2.Initialize(Me, ivForeground, world)
 
    Dim WorldWidth As Float = 36 'meters
    Dim WorldHeight As Float = WorldWidth / 1 'same ratio as in the designer script (in GameLayout.bjl)!!!
    X2.ConfigureDimensions(world.CreateVec2(WorldWidth / 2, WorldHeight / 2), WorldWidth)
 
    ' ──────────────────────────────────
    ' Preparations for movements
    ' ──────────────────────────────────
    PathMainBCForward.Initialize(0, 0)
     PathMainBCBackwards.Initialize(0, 0)
    PathWorld.Initialize
    BrushRed = X2.MainBC.CreateBrushFromColor(xui.Color_Red)
 
    ' ──────────────────────────────────
    ' Preparations for graphics
    ' ──────────────────────────────────
    GraphicCache_Put_Characters
 
    ' ──────────────────────────────────
    ' Preparations for screen
    ' ──────────────────────────────────
    SetBackground
    'CreateStaticBackground
    'CreateBorder
 
 
    ' ──────────────────────────────────
    ' Preparations for sounds
    ' ──────────────────────────────────
    'X2.SoundPool.AddSound("small jump", File.DirAssets, "small_jump.mp3")
    'X2.SoundPool.AddSound("big jump", File.DirAssets, "big_jump.mp3")
    'X2.SoundPool.AddSound("powerup", File.DirAssets, "powerup.mp3")  
 
    ' ──────────────────────────────────
    ' Debug settings
    ' ──────────────────────────────────
    X2.EnableDebugDraw    ' Comment out to disable debug drawing
 
End Sub

Private Sub SetWorldCenter
    ' The map size will not be identical to the screen size.
    ' This happens because the tile size in (bc) pixels needs to be a whole number.
    ' So we need to update the world center and move the map to the center.
    X2.UpdateWorldCenter(TileMap.MapAABB.Center)
End Sub
Public Sub WorldCenterUpdated (gs As X2GameStep)
    CreateEnemies
End Sub
private Sub SetBackground
    'X2.SetBitmapWithFitOrFill(ivBackground, xui.LoadBitmapResize(File.DirAssets, "mybackgroundimage.jpg", ivBackground.Width / 2, ivBackground.Height / 2, False))
End Sub
 
Private Sub GraphicCache_Put_Characters
    Log("#-Sub game.GraphicCache_Put_Characters")

    ' Use bitmap without the transparency placeholdercolor:
    Dim bc As BitmapCreator = X2.BitmapToBC( xui.LoadBitmap(File.DirAssets, "RPGCharacterSprites32x32.png"), 1)
    RemovePseudoTransparentColor(bc, "#ff00ff") ' pink, magenta
    Dim bmp As B4XBitmap = bc.Bitmap
 
    Dim NumberOfSprites As Int = 12
    Dim RowWidth As Int = 32
    Dim RowHeight As Int = 32
    Dim CharHeightMeters As Int = 3
    '
    Dim RowOfChar1 As Int = 2
    Dim character1 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar1, NumberOfSprites * RowWidth, RowHeight)
    Dim AllChar1 As List = X2.ReadSprites(character1, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
    X2.GraphicCache.PutGraphic("character1 front walking", Array(AllChar1.Get(0), AllChar1.Get(1), AllChar1.Get(2), AllChar1.Get(3)))
    X2.GraphicCache.PutGraphic("character1 front standing", Array(AllChar1.Get(3)))
    X2.GraphicCache.PutGraphic("character1 back walking", Array(AllChar1.Get(4), AllChar1.Get(5), AllChar1.Get(6), AllChar1.Get(7)))
    X2.GraphicCache.PutGraphic("character1 back standing", Array(AllChar1.Get(7)))
    X2.GraphicCache.PutGraphic("character1 side walking", Array(AllChar1.Get(8), AllChar1.Get(9), AllChar1.Get(10)) )
    X2.GraphicCache.PutGraphic("character1 side standing", Array(AllChar1.Get(9)) )
    '
    Dim RowOfChar2 As Int = 3
    Dim character2 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar2, NumberOfSprites * RowWidth,  RowHeight)
    Dim AllChar2 As List = X2.ReadSprites(character2, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
    X2.GraphicCache.PutGraphic("character2 front walking", Array(AllChar2.Get(0), AllChar2.Get(1), AllChar2.Get(2), AllChar2.Get(3)))
    X2.GraphicCache.PutGraphic("character2 front standing", Array(AllChar2.Get(3)))
    X2.GraphicCache.PutGraphic("character2 back walking", Array(AllChar2.Get(4), AllChar2.Get(5), AllChar2.Get(6), AllChar2.Get(7)))
    X2.GraphicCache.PutGraphic("character2 back standing", Array(AllChar2.Get(7)))
    X2.GraphicCache.PutGraphic("character2 side walking", Array(AllChar2.Get(8), AllChar2.Get(9), AllChar2.Get(10)) )
    X2.GraphicCache.PutGraphic("character2 side standing", Array(AllChar2.Get(9)) )
    '
 
End Sub

Private Sub RemovePseudoTransparentColor(TilesBC As BitmapCreator, clrstring As String)
    ' Erel --> https://www.b4x.com/android/forum/threads/x2-how-to-use-transparent-color.108173/#post-676534
    Dim clr As Int = 0xff000000 + Bit.ParseInt(clrstring.SubString(1), 16)
    Dim ptranspm As PremultipliedColor
    Dim trans As PremultipliedColor
    Dim pm As PremultipliedColor
    Dim argb As ARGBColor
    TilesBC.ColorToARGB(clr, argb)
    TilesBC.ARGBToPremultipliedColor(argb, ptranspm)
    For y = 0 To TilesBC.mHeight - 1
        For x = 0 To TilesBC.mWidth - 1
            TilesBC.GetPremultipliedColor(x, y, pm)
            If Bit.And(0xff, pm.r) = ptranspm.r And Bit.And(0xff, pm.g) = ptranspm.g And Bit.And(0xff, pm.b) = ptranspm.b And Bit.And(0xff, pm.a) = ptranspm.a Then
                TilesBC.SetPremultipliedColor(x, y, trans)
            End If
        Next
    Next
End Sub

private Sub CreateObjects
    Log("#-Sub game.CreateObjects")
    FigureTemplatesList.Initialize
    PointTemplatesList.Initialize
    TextTemplatesList.Initialize

    Dim ol As X2ObjectsLayer = TileMap.Layers.Get(ObjectLayer)
    For Each TileMapTemplateX As X2TileObjectTemplate In ol.ObjectsById.Values
     
        If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("p") Then
            Dim pt As PointTemplates
            pt.Template = TileMapTemplateX
            pt.XPosition = TileMapTemplateX.Position.X
            PointTemplatesList.Add(pt)
            TileMap.CreateObject(TileMapTemplateX)
         
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("text") Then
            Dim tt As TextTemplates
            tt.Template = TileMapTemplateX
            tt.XPosition = TileMapTemplateX.Position.X
            TextTemplatesList.Add(tt)
            TileMap.CreateObject(TileMapTemplateX)
     
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("char") Then
            Dim ft As FigureTemplates
            ft.Template = TileMapTemplateX
            ft.XPosition = TileMapTemplateX.Position.X
            FigureTemplatesList.Add(ft)
         
            Dim bwChX As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
            bwChX.Body.BodyType = bwChX.Body.TYPE_DYNAMIC   ' not .TYPE_KINEMATIC: You cannot use forces and motors with kinematic types.
         
            If TileMapTemplateX.Name.ToLowerCase.EndsWith("1") Then
                mChar1.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar1.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
            else If TileMapTemplateX.Name.ToLowerCase.EndsWith("2") Then
                mChar2.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar2.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
            End If
         
        End If
    Next
 
    FigureTemplatesList.SortType("XPosition", True)
    PointTemplatesList.SortType("XPosition", True)
    TextTemplatesList.SortType("XPosition", True)
 
    Log("#-  x206, FigureTemplatesList.size = " & FigureTemplatesList.Size)
    Log("#-  x207, PointTemplatesList.size  = " & PointTemplatesList.Size)
    Log("#-  x208, TextTemplatesList.size   = " & TextTemplatesList.Size)
 
End Sub
Private Sub CreateEnemies
'    Do While EnemyTemplatesList.Size > 0
'        Dim et As EnemyTemplates = EnemyTemplatesList.Get(0)
'        If et.XPosition <= X2.ScreenAABB.TopRight.X Then
'            Dim bw As X2BodyWrapper = TileMap.CreateObject(et.Template)
'            Dim enX As Enemy
'            enX.Initialize(bw) 'this sets the delegate
'            enX.IsGhost = (bw.GraphicName = "ghost")
'            EnemyTemplatesList.RemoveAt(0)
'        Else
'            Exit
'        End If
'    Loop
End Sub
Private Sub CreateStaticBackground
    Dim bc As BitmapCreator
    bc.Initialize(ivBackground.Width / xui.Scale / 2, ivBackground.Height / xui.Scale / 2)
    bc.FillGradient(Array As Int(0xFF001AAC, 0xFFC5A400), bc.TargetRect, "TOP_BOTTOM")
    X2.SetBitmapWithFitOrFill(ivBackground, bc.Bitmap)
End Sub
Private Sub CreateBorder
    TileMap.CreateObject(TileMap.GetObjectTemplateByName(ObjectLayer, "border"))
End Sub

' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub Resize
    X2.ImageViewResized
End Sub

Public Sub DrawingComplete
    TileMap.DrawingComplete
End Sub

'Return True to stop the game loop.
Public Sub BeforeTimeStep (GS As X2GameStep) As Boolean
    If GameOverState Then
        Return True
    End If

    Return False
End Sub

Public Sub Tick (GS As X2GameStep)
'    Log("#-Sub game.Tick, gs.GameTimeMs = " & GS.GameTimeMs)
    TileMap.DrawScreen(Array("Tile Layer 1"), GS.DrawingTasks)
 
    MoveChar(GS)
 
'    mChar1.Tick(GS)
'    mChar2.Tick(GS)
 
 
End Sub

' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub GameOver
    X2.SoundPool.StopMusic
    X2.SoundPool.PlaySound("gameover")
    X2.AddFutureTask(Me, "Set_GameOver", 3500, Null)
End Sub
Private Sub Set_GameOver (ft As X2FutureTask)
    GameOverState = True
    Sleep(500)
    StartGame
End Sub
Public Sub StopGame
    X2.SoundPool.StopMusic
    X2.Stop
End Sub

Public Sub StartGame
    If X2.IsRunning Then Return
    X2.Reset
    X2.UpdateWorldCenter(X2.CreateVec2(X2.ScreenAABB.Width / 2, X2.ScreenAABB.Height / 2))
    GameOverState = False

    ' ──────────────────────────────────
    ' Preparations for Tilemap
    ' ──────────────────────────────────
    TileMap.Initialize(X2, File.DirAssets, "TiledMapFile_proj01.json", ivBackground)
    Dim TileSizeMeters As Float = X2.ScreenAABB.Height / TileMap.TilesPerColumn
    TileMap.SetSingleTileDimensionsInMeters(TileSizeMeters, TileSizeMeters)
    SetWorldCenter    ' Update the world center based on the map size
    TileMap.PrepareObjectsDef(ObjectLayer)

    Border = TileMap.CreateObject2ByName(ObjectLayer, "border")
 
    ' ──────────────────────────────────
    ' Draw Tilemap
    ' ──────────────────────────────────
    Dim tasks As List
    tasks.Initialize
    TileMap.Draw(Array("Tile Layer 1"), TileMap.MapAABB, tasks)
    For Each dt As DrawTask In tasks
        If dt.IsCompressedSource Then
            TileMap.CurrentBC.DrawCompressedBitmap(dt.Source, dt.SrcRect, dt.TargetX, dt.TargetY)
        End If
    Next
     
    ' ──────────────────────────────────
    ' Preparations for bodies
    ' ──────────────────────────────────
    CreateObjects

    ' ──────────────────────────────────
    ' Motors for Characters
    ' ──────────────────────────────────
    Dim MotorDefChar1 As B2MotorJointDef
    MotorDefChar1.Initialize(Border.Body, mChar1.bw.Body )
    MotorDefChar1.MaxMotorTorque = 1
    MotorDefChar1.MaxMotorForce = 1
    MotorDefChar1.CollideConnected = True 'let the Char1 collide with the borders
    CharMotor = X2.mWorld.CreateJoint(MotorDefChar1)
    CharMotor.CorrectionFactor = 0.1

    Dim MotorDefChar2 As B2MotorJointDef
    MotorDefChar2.Initialize(Border.Body, mChar2.bw.Body )
    MotorDefChar2.MaxMotorTorque = 1
    MotorDefChar2.MaxMotorForce = 1
    MotorDefChar2.CollideConnected = True 'let the Char2 collide with the borders
    CharMotor = X2.mWorld.CreateJoint(MotorDefChar2)
    CharMotor.CorrectionFactor = 0.1

 
    ' ──────────────────────────────────
    ' Start the Main loop
    ' ──────────────────────────────────
    X2.Start
 
End Sub
 
Sub pnlTouch_Touch (Action As Int, X As Float, Y As Float)
    ' Click is "FormPoint": x and y are the form-coordinates topleft=0,0 and bottomright=formheight,formwidth
    '
    If Action = pnlTouch.TOUCH_ACTION_MOVE_NOTOUCH Then Return
    Log("#-Sub pnlTouch_Touch, Action=" & Action & ", x=" & x & ", y=" & y)
 
    If RadioButton1.Selected Then
        Dim bwcAct As X2BodyWrapper = mChar1.bw
    Else
        Dim bwcAct As X2BodyWrapper = mChar2.bw
    End If
 
    Dim WorldPoint As B2Vec2 = X2.ScreenPointToWorld(X, Y)
    Dim MainBCPoint As B2Vec2 = X2.WorldPointToMainBC(WorldPoint.X, WorldPoint.Y)
 
    If Action = pnlTouch.TOUCH_ACTION_DOWN Then
        Dim FirstPointBC As B2Vec2 = X2.WorldPointToMainBC(bwcAct.Body.Position.X, bwcAct.Body.Position.Y)
     
        ' CLONE the paths before modifying them.
        PathMainBCForward = PathMainBCForward.Clone
        PathMainBCForward.Reset(FirstPointBC.X, FirstPointBC.Y)
        PathMainBCBackwards = PathMainBCBackwards.Clone
        PathMainBCBackwards.Reset(FirstPointBC.X, FirstPointBC.Y)
        PathWorld.Clear
     
    End If
 
    If PathWorld.Size > 0 Then
        Dim PrevPoint As B2Vec2 = PathWorld.Get(PathWorld.Size - 1)
        Dim distance As B2Vec2 = PrevPoint.CreateCopy
        distance.SubtractFromThis(WorldPoint)
        'to improve performance we skip very close points.
        If distance.LengthSquared < 0.1 Then
            Return
        End If
    End If
 
    PathMainBCForward = PathMainBCForward.Clone
    PathMainBCForward.LineTo(MainBCPoint.X, MainBCPoint.Y)
    PathWorld.Add(WorldPoint)
End Sub

Sub MoveChar(gs As X2GameStep)
    If RadioButton1.Selected Then
        Dim bwcAct As X2BodyWrapper = mChar1.bw
    Else
        Dim bwcAct As X2BodyWrapper = mChar2.bw
    End If

    'This loop might be a bit confusing. The loop ends after the first far enough point.
    Do While PathWorld.Size > 0
        Dim NextPoint As B2Vec2 = PathWorld.Get(0)
        Dim CurrentPoint As B2Vec2 = bwcAct.Body.Position
        Dim distance As B2Vec2 = NextPoint.CreateCopy
        distance.SubtractFromThis(CurrentPoint)
        If (distance.Length < 0.3 And PathWorld.Size > 1) Or (distance.Length < 0.1) Then
            PathWorld.RemoveAt(0)
         
            Dim v As B2Vec2 = X2.WorldPointToMainBC(NextPoint.X, NextPoint.Y)
         
            'clone the paths before modifying them as they are being drawn asynchronously.
            PathMainBCForward = PathMainBCForward.Clone
            PathMainBCBackwards = PathMainBCBackwards.Clone
         
            'remove the first point from the "forward" path and add it to the "backwards" path.
            PathMainBCForward.Points.RemoveAt(0)
            PathMainBCForward.Invalidate 'need to call Invalidate after we directly access the points list.
            PathMainBCBackwards.LineTo(v.X, v.Y)
            Continue 'skip to the next point
        End If
     
        CharMotor.AngularOffset = FindAngleToTarget(bwcAct.Body, NextPoint)
     
        Dim delta As B2Vec2 = NextPoint.CreateCopy
        delta.SubtractFromThis(Border.Body.Position)
        CharMotor.LinearOffset = delta
     
        'draw the small red circle
        v = X2.WorldPointToMainBC(NextPoint.X, NextPoint.Y)
        gs.DrawingTasks.Add(X2.MainBC.AsyncDrawCircle(v.X, v.Y, 5,  BrushRed, True, 0))
        Exit '<-----
    Loop
End Sub
Sub FindAngleToTarget(Body As B2Body, Target As B2Vec2) As Float
    If Abs(Body.Angle) > 2 * cPI Then
        'make sure that the current angle is between -2*cPI to 2*cPI
        Body.SetTransform(Body.Position, X2.ModFloat(Body.Angle, 2 * cPI))
    End If
    Dim angle As Float = ATan2(Target.Y - Body.Position.Y, Target.X - Body.Position.X) + cPI / 2
    Dim CurrentAngle As Float = Body.Angle
    'find the shortest direction
    Dim anglediff As Float = angle - CurrentAngle
    If anglediff > cPI Then
        angle = -(2 * cPI - angle)
    Else If anglediff < -cPI Then
        angle = angle + 2 * cPI
    End If
    Return angle
End Sub


'must handle this event if we want to handle the PreSolve event.
Private Sub World_BeginContact (Contact As B2Contact)
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "char1")
    Log("#-Sub World_BeginContact, bodies.OtherBody.Name = " & bodies.OtherBody.Name)
    If bodies <> Null Then
'        If bodies.OtherBody.Name = "mushroom" Then
'            'we cannot modify the world state inside these events. So we add a future task with time = 0.
'            X2.AddFutureTask(mChar1, "Touch_Mushroom", 0, bodies.OtherBody)
'        End If
    End If
 
'    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact, "left edge")
'    If bodies <> Null And bodies.OtherBody.DelegateTo Is Enemy Then
'        X2.AddFutureTask(Me, "Delete_Enemy", 0, bodies.OtherBody)
'        Return
'    End If
 
End Sub
Private Sub World_PreSolve (Contact As B2Contact, OldManifold As B2Manifold)
    Dim BodyA As X2BodyWrapper = Contact.FixtureA.Body.Tag
    Dim BodyB As X2BodyWrapper = Contact.FixtureB.Body.Tag
    If BodyA.IsVisible = False Or BodyB.IsVisible = False Then Return
'    CheckMarioCollisions (Contact, X2.GetBodiesFromContact(Contact, "mario"))
'    CheckEnemyCollisions(Contact, X2.GetBodiesFromContact(Contact, "enemy bug"))
'    CheckEnemyCollisions(Contact, X2.GetBodiesFromContact(Contact, "enemy turtle"))
End Sub


Result


2019-08-04_19-34-25.png

Although it is still far from controlled steering, it is at least the first measurable result.
 

Attachments

  • Testproject_01_016.zip
    380.3 KB · Views: 353
Last edited:
Top