Games [x2][solved] Help needed regarding Object Movement

fredo

Well-Known Member
Licensed User
This is a follow-up request to https://www.b4x.com/android/forum/t...egarding-screen-and-objects-positions.108254/
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.

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

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

B4X:
'
' Java source --> file:///.\Objects\src\b4j\gametest\sn\game.java
'
' ------- -------------- ------
'''#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
    Private LastDiceRollVal As Int = 0 ' ####testingonly
   
    ' 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
   
    'Private ActiveCharacterBody As X2BodyWrapper
    Private ActiveCharacterFigure As Figure
   
    ' Templates
    Type FigureTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private FigureTemplatesList As List  
   
    Type PointTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private PointTemplatesList As List  
    Private PointTemplatesListLoop0 As List  
   
    Type TextTemplates (Template As X2TileObjectTemplate, XPosition As Float)
    Private TextTemplatesList As List  
   
    Type LocationTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private LocationTemplatesList As List  
   

    ' Layout elements
    Private ivForeground As B4XView
    Private ivBackground As B4XView
    Public lblStats As B4XView
    Private pnlTouch As B4XView
    Private RadioButton1 As RadioButton' ####testingonly
    Private RadioButton2 As RadioButton' ####testingonly
    Private Button1 As Button' ####testingonly
    Private Label2 As Label' ####testingonly
    Private Pane1 As Pane' ####testingonly
    Private fx As JFX' ####testingonly
    Private TempTestPane As Pane' ####testingonly
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)
   
    CSSUtils.SetBackgroundColor(Pane1, fx.Colors.LightGray)' ####testingonly
   
    ' ──────────────────────────────────
    ' 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 PositionObjects
    For Each LocationTemplateX As LocationTemplates In LocationTemplatesList
        ' Initial position on location-objects (works ok)
        Select Case True
            Case LocationTemplateX.Template.Name.ToLowerCase.EndsWith("c1")
                mChar1.bw.Body.SetTransform(LocationTemplateX.Template.Position, 0)
            Case LocationTemplateX.Template.Name.ToLowerCase.EndsWith("c2")
                mChar2.bw.Body.SetTransform(LocationTemplateX.Template.Position, 0)
        End Select
    Next
End Sub
private Sub CreateObjects
    Log("#-Sub game.CreateObjects")
    FigureTemplatesList.Initialize
    PointTemplatesList.Initialize
    PointTemplatesListLoop0.Initialize
    TextTemplatesList.Initialize
    LocationTemplatesList.Initialize

    TempTestPane.RemoveAllNodes    ' ####testingonly

    Dim ol As X2ObjectsLayer = TileMap.Layers.Get(ObjectLayer)
    For Each TileMapTemplateX As X2TileObjectTemplate In ol.ObjectsById.Values
        'Log("#-    x203, TileMapTemplateX.Name = " & TileMapTemplateX.Name)
        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)
            If Not(TileMapTemplateX.Name.Contains(".")) And Not(TileMapTemplateX.Name.Contains("x")) Then
                PointTemplatesListLoop0.Add(pt)
            End If
           
            ' ####testingonly
            Dim lblx As Label
            lblx.Initialize("")
            lblx.Text = TileMapTemplateX.Name
            lblx.TextColor = fx.Colors.Yellow
            lblx.Alignment = "CENTER"
            Dim bp As B2Vec2 = X2.WorldPointToMainBC( TileMapTemplateX.Position.X, TileMapTemplateX.Position.Y)
            Private DebugScale As Float = 1.1 ' found by trial and error
            bp.MultiplyThis(DebugScale)
            TempTestPane.AddNode(lblx, bp.X, bp.Y, 40dip, 40dip)
            ' /####testingonly
           
           
        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
                Log("#-      x252, mChar1.Initialize, bwChX=" & bwChX.Name)
                mChar1.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar1.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id

                ' not working...
                Dim MotorDefChar1 As B2MotorJointDef
                MotorDefChar1.Initialize(Border.Body, mChar1.bw.Body)
                MotorDefChar1.MaxMotorTorque = 1
                MotorDefChar1.MaxMotorForce = 1
                MotorDefChar1.CollideConnected = True 'let the Char collide with the borders
                CharMotor = X2.mWorld.CreateJoint(MotorDefChar1)
                CharMotor.CorrectionFactor = 0.1
               


            else If TileMapTemplateX.Name.ToLowerCase.EndsWith("2") Then
                Log("#-      x268, mChar2.Initialize, bwChX=" & bwChX.Name)
                mChar2.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar2.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
               
                '' not working...
                'Dim MotorDefChar2 As B2MotorJointDef
                'MotorDefChar2.Initialize(Border.Body, mChar2.bw.Body)
                'MotorDefChar2.MaxMotorTorque = 1
                'MotorDefChar2.MaxMotorForce = 1
                'MotorDefChar2.CollideConnected = True 'let the Char collide with the borders
                'CharMotor = X2.mWorld.CreateJoint(MotorDefChar2)
                'CharMotor.CorrectionFactor = 0.1

            End If
       
               

       
       
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("loc") Then
            Dim lt As LocationTemplates
            lt.Template = TileMapTemplateX
            lt.XPosition = TileMapTemplateX.Position.X
            LocationTemplatesList.Add(lt)
            TileMap.CreateObject(TileMapTemplateX)
           
'            If TileMapTemplateX.Name.ToLowerCase
'            LocC1Body = TileMap.CreateObject2ByName(ObjectLayer, "locC1")
           
        End If
    Next
   
    FigureTemplatesList.SortType("XPosition", True)
    PointTemplatesList.SortType("XPosition", True)
    TextTemplatesList.SortType("XPosition", True)
    LocationTemplatesList.SortType("XPosition", True)
   
    Log("#-  x206, FigureTemplatesList.size   = " & FigureTemplatesList.Size)
    Log("#-  x207, PointTemplatesList.size    = " & PointTemplatesList.Size)
    Log("#-  x208, TextTemplatesList.size     = " & TextTemplatesList.Size)
    Log("#-  x209, LocationTemplatesList.size = " & LocationTemplatesList.Size)
   
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
    PositionObjects
    ActiveCharacterFigure = mChar1
   
    ' ──────────────────────────────────
    ' Start the Main loop
    ' ──────────────────────────────────
    X2.Start
   
End Sub
 
Sub pnlTouch_Touch (Action As Int, X As Float, Y As Float)
    ' Click is on a "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)
   
    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(ActiveCharacterFigure.bw.Body.Position.X, ActiveCharacterFigure.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

Sub MoveChar(gs As X2GameStep)
   
    '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 = ActiveCharacterFigure.bw.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
       
'        Log("#-  x467, ActiveCharacterBody = " & ActiveCharacterBody.Name)
        CharMotor.AngularOffset = FindAngleToTarget(ActiveCharacterFigure.bw.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
Private Sub World_PostSolve (Contact As B2Contact, Impulse As B2ContactImpulse)
   
End Sub

Sub ActiveCharacterSelect_SelectedChange(Selected As Boolean) ' ####testingonly
    LastDiceRollVal = 0
    Label2.Text = "..."
    If Not(Selected) Then Return
   
    Log("#-ActiveCharacterSelect_SelectedChange, Selected=" & Selected)
    Dim rbx As RadioButton = Sender
    Dim tagx As String = rbx.Tag
    Select Case True
        Case tagx.ToLowerCase = "rbc1"
            ActiveCharacterFigure = mChar1
           
        Case tagx.ToLowerCase = "rbc2"
            ActiveCharacterFigure = mChar2
           
    End Select
    Log("#-  x536, tagx.ToLowerCase = " & tagx.ToLowerCase)
    Log("#-  x537, ActiveCharacterBody.Name = " & ActiveCharacterFigure.bw.Name)
End Sub
Sub Button1_Click ' ####testingonly
    LastDiceRollVal = Rnd(1,12)
    Label2.Text = LastDiceRollVal
    CharAdvance(LastDiceRollVal)
End Sub
Sub CharAdvance(NumberOfSteps As Int)
    Log("#-Sub game.CharAdvance, NumberOfSteps=" & NumberOfSteps)
    Dim NewPointTemplatesListIndex As Int =    ActiveCharacterFigure.LastPositionIndex +NumberOfSteps
    Log("#-  x579, NewPointTemplatesListIndex=" & NewPointTemplatesListIndex)
   
    For i=ActiveCharacterFigure.LastPositionIndex To NewPointTemplatesListIndex
        Dim GotToPosIndex As Int = i Mod PointTemplatesListLoop0.Size
       
        Dim pt As PointTemplates = PointTemplatesListLoop0.Get(GotToPosIndex)
        Log("#-    x580, GotToPosIndex=" & GotToPosIndex & ", pt.Template.Position=" & pt.Template.Position)
       
       
        '##### An experiment to move the active character the "nonmotor" way      
        Dim v As B2Vec2 = ActiveCharacterFigure.bw.Body.Position.CreateCopy
        ActiveCharacterFigure.bw.Body.LinearVelocity = v
        '/####
       
               
    Next
    ActiveCharacterFigure.LastPositionIndex = NewPointTemplatesListIndex
End Sub
B4X:
' Class "Figure"

Sub Class_Globals
    Public FigureNameAndId As String = ""
    Public bw As X2BodyWrapper
    Public InAir As Boolean
    Public IsSmall As Boolean = True
    Public LastPositionIndex As Int = 0
   
    Private x2 As X2Utils 'ignore
    Private LastTickTime As Int
    Private SpecialState As Boolean
    Private FaceRight As Boolean = True
    Private MaxVelocity As Float = 10
    Private ImpulseVector As B2Vec2
    Private ProtectedTime As Int
End Sub

Public Sub Initialize (wrapper As X2BodyWrapper)
'    Log("#-Sub figure.Initialize")
    LastPositionIndex = 0
    bw = wrapper
    x2 = bw.X2
    bw.DelegateTo = Me
    UpdateImpulseVector
    CreateChar1Legs
End Sub

Private Sub CreateChar1Legs
    ' see 1--> https://www.b4x.com/android/forum/threads/xui2d-super-mario-example-1.96151/
    ' see 2--> https://www.b4x.com/android/forum/threads/xui2d-super-mario-example-2.96236/
    Dim rect As B2PolygonShape
    rect.Initialize
    rect.SetAsBox2(0.45, 0.05, x2.CreateVec2(0, -x2.GetShapeWidthAndHeight(bw.Body.FirstFixture.Shape).y / 2 + 0.05), 0)
    Dim f As B2Fixture = bw.Body.CreateFixture2(rect, 0.1)
    f.Friction = 1
    f.Tag = "legs"
End Sub

Private Sub UpdateImpulseVector
    Log("#-Sub figure.UpdateImpulseVector, bw.Name=" & bw.Name)
    ImpulseVector = x2.CreateVec2(0.5 * bw.Body.Mass * x2.TimeStepMs / 16, 0)
End Sub

Public Sub Hit_Start (ft As X2FutureTask)
    If SpecialState Then Return
    If x2.gs.GameTimeMs < ProtectedTime Then Return


   
End Sub

Private Sub StartGameOver
    SpecialState = True
    bw.GraphicName = "Char1 small strike"
    bw.Body.FirstFixture.SetFilterBits(0, 0)
    bw.Body.FirstFixture.NextFixture.SetFilterBits(0, 0)
    bw.Body.LinearVelocity = x2.CreateVec2(0, 15)
    bw.mGame.GameOver
End Sub

Public Sub Tick (GS As X2GameStep)
'    Log("#-Sub figure.Tick")
    If LastTickTime = GS.GameTimeMs Then Return
    LastTickTime = GS.GameTimeMs

    ' ???
   
    If GS.ShouldDraw Then
        bw.UpdateGraphic(GS, True)
    End If
End Sub

Public Sub IsLegsFixture (Fixture As B2Fixture) As Boolean
    Return Fixture.Tag <> Null And Fixture.Tag = "legs"
End Sub

2019-08-06_13-49-32.png

While positioning works quite well so far, there are still problems understanding the movement of objects from one point to another.

  1. By reviewing the examples, the MotorJoint was found in the mouse example and tested without success.
  2. A movement with Body.LinearVelocity was tried, but without real control over the behavior.

The intended process is as follows:
  • In Game.CreateObjects a list with the positions of certain points from TiledMap is created (works)
  • After building the screen the first character is selected and a random integer (1..12) is created (by button)
  • The figure Char1 is moved from the current position along the list of points to the next position (oldindex +randomvalue).

As it looks, I currently need concrete help on code level.

I would be grateful if experienced X2 experts could take a quick look at the test project and help me to implement the movement using motorjoint.
 

Attachments

Erel

Administrator
Staff member
Licensed User
Code to move char 1 with the motor joint:
B4X:
Sub MoveChar1To (vec As B2Vec2)
   vec.SubtractFromThis(Border.Body.Position)
   CharMotor.LinearOffset = vec
End Sub
For example to move it to 20, 20:
B4X:
MoveChar1To(X2.CreateVec2(20, 20))
I've tested it with these settings:
B4X:
mChar1.bw.Body.LinearDamping = 1
Dim MotorDefChar1 As B2MotorJointDef
mChar1.bw.Body.SleepingAllowed = False
MotorDefChar1.Initialize(Border.Body, mChar1.bw.Body)
MotorDefChar1.MaxMotorForce = 20
MotorDefChar1.CollideConnected = True 'let the Char collide with the borders
CharMotor = X2.mWorld.CreateJoint(MotorDefChar1)
CharMotor.CorrectionFactor = 0.05
 

Erel

Administrator
Staff member
Licensed User
Always try to avoid repeating the same code. In this case you should create a sub that creates the motor:
B4X:
Sub CreateMotor(character As Figure) As B2MotorJoint
   Dim MotorDefChar2 As B2MotorJointDef
   MotorDefChar2.Initialize(Border.Body, character.bw.Body)
   MotorDefChar2.MaxMotorForce = 500
   MotorDefChar2.CollideConnected = True 'let the Char collide with the borders
   Dim mc As B2MotorJoint = X2.mWorld.CreateJoint(MotorDefChar2)
   mc.CorrectionFactor = 0.02
   Return mc
End Sub
I'm getting good results with the above code (in release mode).
 

fredo

Well-Known Member
Licensed User
... the above code ...
Sure, you're right

B4X:
'
' Java source --> file:///.\Objects\src\b4j\gametest\sn\game.java
'
' ------- -------------- ------
'#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
    Private ActiveCharIndex As Int = 1
    Private LastDiceRollVal As Int = 0
    Private CharLastPositionIndex(3) As Int
    
    
    ' Bodies and joints
    Private Border As X2BodyWrapper
    Private MotorChar1 As B2MotorJoint
    Private MotorChar2 As B2MotorJoint
    
    Private PathMainBCForward As BCPath
    Private PathMainBCBackwards As BCPath
    Private PathWorld As List
    Private BrushRed As BCBrush
    
    
    ' Templates
    Type FigureTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private FigureTemplatesList As List
    
    Type PointTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private PointTemplatesList As List
    Private PointTemplatesListLoop0 As List
    
    Type TextTemplates (Template As X2TileObjectTemplate, XPosition As Float)
    Private TextTemplatesList As List
    
    Type LocationTemplates(Template As X2TileObjectTemplate, XPosition As Float)
    Private LocationTemplatesList As List
    

    ' Layout elements
    Private ivForeground As B4XView
    Private ivBackground As B4XView
    Public lblStats As B4XView
    Private pnlTouch As B4XView
    Private RadioButton1 As RadioButton' ####testingonly
    Private RadioButton2 As RadioButton' ####testingonly
    Private Button1 As Button' ####testingonly
    Private Label2 As Label' ####testingonly
    Private Pane1 As Pane' ####testingonly
    Private fx As JFX' ####testingonly
    Private TempTestPane As Pane' ####testingonly
    Private Label3 As Label' ####testingonly
    Private Label4 As Label' ####testingonly
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)
    
    CSSUtils.SetBackgroundColor(Pane1, fx.Colors.LightGray)' ####testingonly
    
    ' ──────────────────────────────────
    ' 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)
    CharLastPositionIndex(0) = -1 ' not used
    CharLastPositionIndex(1) = 0 ' for Char1
    CharLastPositionIndex(2) = 0 ' for Char2
    
    ' ──────────────────────────────────
    ' Preparations for graphics
    ' ──────────────────────────────────
    GraphicCache_Put_Characters
    
    ' ──────────────────────────────────
    ' Preparations for screen
    ' ──────────────────────────────────
    SetBackground
    'CreateStaticBackground
    'CreateBorder
    
    
    ' ──────────────────────────────────
    ' Preparations for sounds
    ' ──────────────────────────────────
    X2.SoundPool.AddSound("rolldice", File.DirAssets, "dice-4.wav")
    '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 PositionObjects
    For Each LocationTemplateX As LocationTemplates In LocationTemplatesList
        ' Initial position on location-objects (works ok)
        Select Case True
            Case LocationTemplateX.Template.Name.ToLowerCase.EndsWith("c1")
                mChar1.bw.Body.SetTransform(LocationTemplateX.Template.Position, 0)
            Case LocationTemplateX.Template.Name.ToLowerCase.EndsWith("c2")
                mChar2.bw.Body.SetTransform(LocationTemplateX.Template.Position, 0)
        End Select
    Next
End Sub
private Sub CreateObjects
    Log("#-Sub game.CreateObjects")
    FigureTemplatesList.Initialize
    PointTemplatesList.Initialize
    PointTemplatesListLoop0.Initialize
    TextTemplatesList.Initialize
    LocationTemplatesList.Initialize

    TempTestPane.RemoveAllNodes    ' ####testingonly

    Dim tempcounter_pt0 As Int = 0'####temp
    Dim ol As X2ObjectsLayer = TileMap.Layers.Get(ObjectLayer)
    For Each TileMapTemplateX As X2TileObjectTemplate In ol.ObjectsById.Values
        'Log("#-    x203, TileMapTemplateX.Name = " & TileMapTemplateX.Name)
        
        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)
            If Not(TileMapTemplateX.Name.Contains(".")) And Not(TileMapTemplateX.Name.Contains("x")) Then
                Log("#-    x250, " & $"PointTemplatesListLoop0(${tempcounter_pt0})= $1.0{pt.Template.Position.X}, $1.0{pt.Template.Position.Y}  "$)
                PointTemplatesListLoop0.Add(pt)
                tempcounter_pt0 = tempcounter_pt0 +1
            End If
            
            ' ####testingonly
            ' Show the labels of points
            Dim lblx As Label
            lblx.Initialize("")
            lblx.Text = TileMapTemplateX.Name
            lblx.TextColor = fx.Colors.Yellow
            lblx.Alignment = "CENTER"
            Dim bp As B2Vec2 = X2.WorldPointToMainBC( TileMapTemplateX.Position.X, TileMapTemplateX.Position.Y)
            Private DebugScale As Float = 1.1 ' found by trial and error
            bp.MultiplyThis(DebugScale)
            TempTestPane.AddNode(lblx, bp.X, bp.Y, 40dip, 40dip)
            ' /####testingonly
            
            
        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
                mChar1.bw.Body.LinearDamping = 0.01
                mChar1.bw.Body.SleepingAllowed = False
                MotorChar1 = CreateMotor(mChar1)


            else If TileMapTemplateX.Name.ToLowerCase.EndsWith("2") Then
                mChar2.Initialize(bwChX) ' Set the delegate (the class "Figure")
                mChar2.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
                mChar2.bw.Body.LinearDamping = 0.01
                mChar2.bw.Body.SleepingAllowed = False
                MotorChar2 = CreateMotor(mChar2)
                
            End If
        
        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("loc") Then
            Dim lt As LocationTemplates
            lt.Template = TileMapTemplateX
            lt.XPosition = TileMapTemplateX.Position.X
            LocationTemplatesList.Add(lt)
            TileMap.CreateObject(TileMapTemplateX)
            
'            If TileMapTemplateX.Name.ToLowerCase
'            LocC1Body = TileMap.CreateObject2ByName(ObjectLayer, "locC1")
            
        End If
    Next
    
    FigureTemplatesList.SortType("XPosition", True)
    PointTemplatesList.SortType("XPosition", True)
    TextTemplatesList.SortType("XPosition", True)
    LocationTemplatesList.SortType("XPosition", True)
    
    Log("#-  x206, FigureTemplatesList.size   = " & FigureTemplatesList.Size)
    Log("#-  x207, PointTemplatesList.size    = " & PointTemplatesList.Size)
    Log("#-  x208, TextTemplatesList.size     = " & TextTemplatesList.Size)
    Log("#-  x209, LocationTemplatesList.size = " & LocationTemplatesList.Size)
    
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
private Sub CreateMotor(character As Figure) As B2MotorJoint
    ' Erel --> https://www.b4x.com/android/forum/threads/x2-help-needed-regarding-object-movement.108390/#post-677540
    Dim MotorDefChar2 As B2MotorJointDef
    MotorDefChar2.Initialize(Border.Body, character.bw.Body)
    MotorDefChar2.MaxMotorForce = 500
    MotorDefChar2.CollideConnected = True 'let the Char collide with the borders
    Dim mc As B2MotorJoint = X2.mWorld.CreateJoint(MotorDefChar2)
    'mc.CorrectionFactor = 0.02
    mc.CorrectionFactor = 0.03
    Return mc
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
    PositionObjects
    ActiveCharacterFigure = mChar1
    
    ' ──────────────────────────────────
    ' Start the Main loop
    ' ──────────────────────────────────
    X2.Start
    
End Sub
 
Sub pnlTouch_Touch (Action As Int, X As Float, Y As Float)
    ' Click is on a "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)
    
    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(ActiveCharacterFigure.bw.Body.Position.X, ActiveCharacterFigure.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


Sub MoveCharTo (CharIndex As Int, vec As B2Vec2)
    vec.SubtractFromThis(Border.Body.Position)
    Select Case CharIndex
        Case 1
            MotorChar1.LinearOffset = vec
        Case 2
            MotorChar2.LinearOffset = vec
    End Select
End Sub

'Sub MoveChar(gs As X2GameStep)
'   
'    '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 = ActiveCharacterFigure.bw.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
'       
''        Log("#-  x467, ActiveCharacterBody = " & ActiveCharacterBody.Name)
'        CharMotor.AngularOffset = FindAngleToTarget(ActiveCharacterFigure.bw.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
Private Sub World_PostSolve (Contact As B2Contact, Impulse As B2ContactImpulse)
    
End Sub

Sub ActiveCharacterSelect_SelectedChange(Selected As Boolean) ' ####testingonly
    LastDiceRollVal = 0
    Label2.Text = "..."
    If Not(Selected) Then Return
    Dim rbx As RadioButton = Sender
    Dim tagx As String = rbx.Tag
    Select Case True
        Case tagx.ToLowerCase = "rbc1"
            ActiveCharIndex = 1
        Case tagx.ToLowerCase = "rbc2"
            ActiveCharIndex = 2
    End Select
End Sub
Sub Button1_Click ' ####testingonly
    X2.SoundPool.PlaySound("rolldice")
    Sleep(500)
    LastDiceRollVal = Rnd(1,6)
    Label2.Text = LastDiceRollVal
    ActiveCharAdvance(LastDiceRollVal)
End Sub
Sub ActiveCharAdvance(NumberOfSteps As Int)
'    Log("#-Sub game.CharAdvance, NumberOfSteps=" & NumberOfSteps)
    Dim NewPointTemplatesListIndex As Int =    CharLastPositionIndex(ActiveCharIndex) +NumberOfSteps
    Dim GotToPosIndex As Int = NewPointTemplatesListIndex Mod PointTemplatesListLoop0.Size
    Dim pt As PointTemplates = PointTemplatesListLoop0.Get(GotToPosIndex)

    '####test
    Log("#-  x594, " & $"ActiveCharAdvance --> ActiveCharIndex=${ActiveCharIndex}, DiceRollVal=${LastDiceRollVal}, MoveCharTo(${GotToPosIndex})= $1.0{pt.Template.Position.X}, $1.0{pt.Template.Position.Y}  "$)
    If ActiveCharIndex =1 Then
        Label3.Text = "Go p" & (GotToPosIndex +1)
    else If ActiveCharIndex =2 Then
        Label4.Text = "Go p" & (GotToPosIndex +1)
    End If
    '/####
    
    MoveCharTo(ActiveCharIndex, pt.Template.Position)
    CharLastPositionIndex(ActiveCharIndex) = GotToPosIndex
End Sub

test1.gif
 

Attachments

Erel

Administrator
Staff member
Licensed User
Another way to move objects is demonstrated in the mouse example. This code is called from the game's Tick event:
B4X:
Private Sub MoveEnemy
   Dim v As B2Vec2 = Mouse.Body.Position.CreateCopy
   v.SubtractFromThis(Enemy.Body.Position)
   If v.Length > 0.7 Then
       v.NormalizeThis
       Enemy.Body.LinearVelocity = v
       Enemy.Body.SetTransform(Enemy.Body.Position, ATan2(v.Y, v.X) + cPI / 2)
   Else
       Enemy.Body.LinearVelocity = X2.CreateVec2(0, 0)
   End If
End Sub
It causes the second mouse, the enemy, to move (and point) towards the first one.
When the second mouse is close enough it stops immediately.
 

fredo

Well-Known Member
Licensed User
This code is called from the game's Tick event
Can I execute a fornext within the game.tick sub to simulate a relative slow step-by-step setting of the figure?

Or can I have an asynchronous task list processed with the movements while the main process continues?


2019-08-07_13-54-57.png
 

Attachments

Erel

Administrator
Staff member
Licensed User
Can I execute a fornext within the game.tick sub to simulate a relative slow step-by-step setting of the figure?
There is no such thing. Each game step represents a single moment. It should complete as fast as possible.

Or can I have an asynchronous task list processed with the movements while the main process continues?
You don't need to. It is quite simple to move an object between several points.

Example:
B4X:
Public Sub Tick (GS As X2GameStep)
   TileMap.DrawScreen(Array("Tile Layer 1"), GS.DrawingTasks)
   If Char1Points.Size > 0 Then
       If Char1Points.Size = 1 Then
           MotorChar1.CorrectionFactor = 0.02
       Else
           MotorChar1.CorrectionFactor = 0.05
       End If
       Dim NextPoint As B2Vec2 = Char1Points.Get(0)
       Dim vec As B2Vec2 = mChar1.bw.Body.Position.CreateCopy
       vec.SubtractFromThis(NextPoint)
       If vec.Length < 1 Then
           Char1Points.RemoveAt(0)
       Else
           MoveCharTo(1, NextPoint.CreateCopy)
       End If
   End If
End Sub
B4X:
Char1Points.AddAll(Array(X2.CreateVec2(10, 1), X2.CreateVec2(10, 10), X2.CreateVec2(20, 20)))
You will probably need to change the max force and correction factors.
 

fredo

Well-Known Member
Licensed User
... It is quite simple ....
Sure, it's also easy to create catchy guitar riffs in gm pentatonic if you're Richie Blackmore.

Thank you for your guidance.
It's starting to dawn on me what it's all about with the ticks.


test5.gif
 

Attachments

Top