1. *** New version of B4J is available ***
    B4J v7.8
    Dismiss Notice

Games [x2][solved] Help needed regarding creating object clones

Discussion in 'Game Development' started by fredo, Aug 28, 2019.

  1. fredo

    fredo Active Member Licensed User

    This is a follow-up request to https://www.b4x.com/android/forum/threads/x2-solved-help-needed-regarding-object-collision.108455/

    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.

    Code:
    '
    ' 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
        
    '
        Private ISDEBUG As Boolean = False ' It's easier to change it here instead of find it further down
        '
        ' Collision filtering
        ' Gunther --> https://www.b4x.com/android/forum/threads/tool-collision-bits-and-masks-calculator-collision-filtering.103320/
        Public const COLL_CATEGORY_PLAYER As Int  = 0x0001 ' 0000000000000001 in binary
        Public const COLL_CATEGORY_MONSTER As Int = 0x0002 ' 0000000000000010 in binary
        Public const COLL_CATEGORY_SCENERY As Int = 0x0004 ' 0000000000000100 in binary
     
        
    Public const COLL_MASK_PLAYERHITS_MONSTERORSCENERY As Int  = 0xFFFE ' 1111111111111110 in binary
        Public const COLL_MASK_MONSTERHITS_PLAYER As Int           = 0xFFF9 ' 1111111111111001 in binary
        Public const COLL_MASK_SCENERYHITS_PLAYER As Int           = 0xFFF9 ' 1111111111111001 in binary
     
        
    ' The group index can be used to override the category/mask settings for a given set of fixtures.
        ' --> https://www.iforce2d.net/b2dtut/collision-filtering
        Public const COLL_GROUP_PLAYER As Int = -1
        
    Public const COLL_GROUP_MONSTER As Int = -2
        
    Public const COLL_GROUP_SCENERY As Int = 1
     
        
    ' Basic x2 libraries that are needed in every game
        Public  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
     
        
    ' Parameters
        Public const MotorCorrFacInitVal As Float = 0.03
        
    Public const MotorCorrFacNormal As Float = 0.07
        
    Public const MotorCorrFacEnd As Float = 0.06
     
        
    ' Gamestate
        Private GameOverState As Boolean = False
        
    Private CharIsMoving As Boolean = False
        
    Private ActiveCharIndex As Int = 1
        
    Private LastDiceRollVal As Int = 0
        
    Private CharLastPositionIndex(3As Int
        
    Type PointXy(x As Float, y As Float)
        
    Private Char1Points As List
        
    Private Char2Points As List
     
        
    ' 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 Pane2 As Pane' ####testingonly
        Private fx As JFX' ####testingonly
        Private TempTestPane As Pane' ####testingonly
        Private Label5 As Label' ####testingonly
    '    Private TextArea1 As TextArea ' Restitution
    '    Private TextArea3 As TextArea ' Friction
    '    Private TextArea4 As TextArea ' Density
    '    Private TextArea5 As TextArea ' Category bits
    '    Private TextArea6 As TextArea ' Mask bits
    '    Private TextArea7 As TextArea ' mcfA
    '    Private TextArea8 As TextArea ' mcfB
        Private Label4 As Label ' coll pl1
        Private Label12 As Label ' coll pl2
    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
        CSSUtils.SetBackgroundColor(Pane2, fx.Colors.LightGray)' ####testingonly
     
        
    ' ──────────────────────────────────
        ' Preparations for World
        ' ──────────────────────────────────
        world.Initialize("world", world.CreateVec2(00))
        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)
     
     
    '    TextArea8.Text = MotorCorrFacNormal ' ####testingonly
    '    TextArea7.Text = MotorCorrFacEnd ' ####testingonly
     
        
    ' ──────────────────────────────────
        ' Preparations for movements
        ' ──────────────────────────────────
        PathMainBCForward.Initialize(00)
        PathMainBCBackwards.Initialize(
    00)
        PathWorld.Initialize
    '    BrushRed = X2.MainBC.CreateBrushFromColor(xui.Color_Red)
        CharLastPositionIndex(0) = -1 ' not used
        CharLastPositionIndex(1) = 0 ' for Char1
        CharLastPositionIndex(2) = 0 ' for Char2
     
        Char1Points.Initialize
        Char2Points.Initialize
     
        
    ' ──────────────────────────────────
        ' Preparations for graphics
        ' ──────────────────────────────────
        GraphicCache_Put_Objects
        GraphicCache_Put_Characters
     
     
        
    ' ──────────────────────────────────
        ' Preparations for screen
        ' ──────────────────────────────────
        'SetBackground
        CreateStaticBackground
        
    'CreateBorder
     
     
        
    ' ──────────────────────────────────
        ' Preparations for sounds
        ' ──────────────────────────────────
        X2.SoundPool.AddSound("rolldice"File.DirAssets, "dice-4.wav")
        X2.SoundPool.AddSound(
    "stepstone_1"File.DirAssets, "stepstone_1.wav")
        
    'X2.SoundPool.AddSound("metal-clash", File.DirAssets, "metal-clash.wav")
        X2.SoundPool.AddSound("Pickup__008"File.DirAssets, "Pickup__008.wav")
     
     
        
    ' ──────────────────────────────────
        ' Debug settings
        ' ──────────────────────────────────
        If ISDEBUG Then
            X2.EnableDebugDraw    
    ' Comment out to disable debug drawing
        End If
     
    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_Objects
        
    Log("#-Sub game.GraphicCache_Put_Objects")
        
    '
        Dim RowWidthPixel As Int = 32
        
    Dim RowHeightPixel As Int = 32
        
    Dim CollGraphHeightMeters As Int = 2
     
        
    ' -------------------------------
        ' Step-pads
        ' -------------------------------
        Dim bc1 As BitmapCreator = X2.BitmapToBC(xui.LoadBitmap(File.DirAssets, "dungeon_tilesetmodrollo.png"), 1)
        
    Dim bmp As B4XBitmap = bc1.Bitmap
        
    Dim CollGraph1 As B4XBitmap = bmp.Crop(1 * 3218 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
        
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 11, CollGraphHeightMeters, CollGraphHeightMeters)
        X2.GraphicCache.PutGraphic(
    "steppad_collision"Array(CollGraph1List.Get(0)) )
     
        
    Dim bc2 As BitmapCreator = X2.BitmapToBC(xui.LoadBitmap(File.DirAssets, "dungeon_tilesetmodrollo.png"), 1)
        
    Dim bmp As B4XBitmap = bc2.Bitmap
        
    Dim CollGraph1 As B4XBitmap = bmp.Crop(5 * 3218 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
        
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 11, CollGraphHeightMeters, CollGraphHeightMeters)
        X2.GraphicCache.PutGraphic(
    "steppad_normal"Array(CollGraph1List.Get(0)) )
         
    End Sub
    Private Sub GraphicCache_Put_Characters
        
    Log("#-Sub game.GraphicCache_Put_Characters")
        
    ' -------------------------------
        ' 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
                pt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                PointTemplatesList.Add(pt)
             
                
    Dim bwobj As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
                bwobj.Graphicname = 
    "steppad_normal"
                
    If Not(TileMapTemplateX.Name.Contains(".")) And Not(TileMapTemplateX.Name.Contains("x")) Then
                    
    'Log("#-    x250, " & $"${TileMapTemplateX.Name} --> PointTemplatesListLoop0(${tempcounter_pt0})= $1.0{pt.Template.Position.X}, $1.0{pt.Template.Position.Y}  "$)
                    Dim xy0 As PointXy
                    xy0.Initialize
                    xy0.x = pt.Template.Position.X
                    xy0.y = pt.Template.Position.y
                    PointTemplatesListLoop0.Add(xy0)
                    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, 
    40dip40dip)
                
    ' /####testingonly
             
             
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("textval"Then
                
    Dim tt As TextTemplates
                tt.Template = TileMapTemplateX
                tt.XPosition = TileMapTemplateX.Position.X
                tt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                TextTemplatesList.Add(tt)
                TileMap.CreateObject(TileMapTemplateX )
             
                
    ' ####testingonly
                ' Show the labels of text
                Dim lblx As Label
                lblx.Initialize(
    "")
                lblx.Text = TileMapTemplateX.Name.Replace(
    "TextVal","")
                
    If TileMapTemplateX.Name.ToLowerCase.StartsWith("textval"Then
                    lblx.TextSize = 
    20
                
    Else
                    lblx.TextSize = 
    10
                
    End If
                lblx.TextColor = fx.Colors.Magenta
                
    'lblx.Alignment = "CENTER_RIGHT"
                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, 
    40dip40dip)
                
    ' /####testingonly
             
         
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("char"Then
                
    Dim ft As FigureTemplates
                ft.Template = TileMapTemplateX
                ft.XPosition = TileMapTemplateX.Position.X
                ft.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_PLAYER, COLL_MASK_PLAYERHITS_MONSTERORSCENERY)
                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
                tt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                LocationTemplatesList.Add(lt)
                TileMap.CreateObject(TileMapTemplateX)
             
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase = "border" Then
                
    'nothing to do here. It is created in StartGame()
         
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("area"Then
                TileMap.CreateObject(TileMapTemplateX)
             
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("coin"Then
                
    ' Creates the 1 and only object with the animated coin
                Dim bwobjcoin1 As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX )
             
            
    Else
                
    Log("#-  x313, " & $"Unhandled object --> id=${TileMapTemplateX.Id}, ${TileMapTemplateX.Name}"$)
            
    End If
        
    Next
     
        FigureTemplatesList.SortType(
    "XPosition"True)
        PointTemplatesList.SortType(
    "XPosition"True)
        TextTemplatesList.SortType(
    "XPosition"True)
        LocationTemplatesList.SortType(
    "XPosition"True)
     
        
    ' After all positions of the "TextVal" objects are determined:
        ' CLONE the 1 animated "coin" object to all postions of "TextVal" objects
        For Each tt As TextTemplates In TextTemplatesList
            CreateCloneFromBw(bwobjcoin1, tt.Template.Position.CreateCopy)
        
    Next
     
    End Sub
    Sub CreateCloneFromBw(obj0 As X2BodyWrapper, TargetPosition As B2Vec2)
    '    Dim bwclone As X2BodyWrapper = obj0.Body.
    '    TileMap.CreateObject(obj0.Body.)
     
        
    ' ??? 
     
    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")
        bc.FillGradient(Array As Int(0xFF61584C0xFF61584C), bc.TargetRect, "TOP_BOTTOM"' brownish
        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 MotorDefForChar As B2MotorJointDef
        MotorDefForChar.Initialize(Border.Body, character.bw.Body)
        MotorDefForChar.MaxMotorForce = 
    1500
        MotorDefForChar.CollideConnected = 
    True 'let the Char collide with the borders
        Dim mc As B2MotorJoint = X2.mWorld.CreateJoint(MotorDefForChar)
        
    'mc.CorrectionFactor = 0.02
        mc.CorrectionFactor = MotorCorrFacInitVal
        
    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)
        
    #region ---Move active character by motor
    '    Dim voff As B2Vec2 = X2.CreateVec2(0, 1.1) ' offset, so that the char is slightly beneath the pad
        Dim voff As B2Vec2 = X2.CreateVec2(00' offset, so that the char is slightly beneath the pad
        Select Case ActiveCharIndex
            
    Case 1
                
    If Char1Points.Size > 0 Then
                    
    Dim NextPoint As B2Vec2 = Char1Points.Get(0)
                    NextPoint = NextPoint.CreateCopy
                    
    If Char1Points.Size < 2 Then
                        MotorChar1.CorrectionFactor = MotorCorrFacEnd
                        NextPoint.AddToThis(voff)
                    
    Else
                        MotorChar1.CorrectionFactor = MotorCorrFacNormal
                    
    End If
                 
                    
    Dim vec As B2Vec2 = mChar1.bw.Body.Position.CreateCopy
                    vec.SubtractFromThis(NextPoint)
                    
    If vec.Length < 1 Then
                        Char1Points.RemoveAt(
    0)
                        
    If Char1Points.Size = 0 Then
                            PlayerFinished
                        
    End If
                    
    Else
                        MoveCharByMotorTo(ActiveCharIndex , NextPoint)
                    
    End If
                
    End If
             
            
    Case 2
                
    ' ~~  ~~  ~~  ~~  ~~  ~~  ~~
                ' duplicate code from "case 1" in order to avoid a timeconsuming call to a separate sub
                ' ~~  ~~  ~~  ~~  ~~  ~~  ~~
                If Char2Points.Size > 0 Then
                    
    Dim NextPoint As B2Vec2 = Char2Points.Get(0)
                    NextPoint = NextPoint.CreateCopy
                    
    Dim vec As B2Vec2 = mChar2.bw.Body.Position.CreateCopy
                    
    If Char2Points.Size < 2 Then
                        MotorChar2.CorrectionFactor = MotorCorrFacEnd
                        NextPoint.AddToThis(voff)
                    
    Else
                        MotorChar2.CorrectionFactor = MotorCorrFacNormal
                    
    End If
                    vec.SubtractFromThis(NextPoint)
                    
    If vec.Length < 2 Then
                        Char2Points.RemoveAt(
    0)
                        
    If Char2Points.Size = 0 Then
                            PlayerFinished
                        
    End If
                    
    Else
                        MoveCharByMotorTo(ActiveCharIndex , NextPoint)
                    
    End If
                
    End If
        
    End Select
     
    #end region
    End Sub
    private Sub PlayerFinished
        CharIsMoving = 
    False
        SwapPlayer
    End Sub
    ' ────────────────────────────────────────────────────────────────────────────────────────────────
    Public Sub GameOver
        X2.SoundPool.StopMusic
    '    X2.SoundPool.PlaySound("gameover")
        X2.AddFutureTask(Me, "Set_GameOver"3500Null)
    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
     
        
    ' ──────────────────────────────────
        ' Preparations for Players
        ' ──────────────────────────────────
        mChar1.CollectedItemsMap.Initialize
        mChar2.CollectedItemsMap.Initialize
         
        
    ' ──────────────────────────────────
        ' Start the Main loop
        ' ──────────────────────────────────
        X2.Start
     
    End Sub
    private 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
    private Sub MoveCharByMotorTo (CharIndex As Int, vec As B2Vec2)
        CharIsMoving = 
    True
        vec.SubtractFromThis(Border.Body.Position)
        
    Select Case CharIndex
            
    Case 1
                MotorChar1.LinearOffset = vec
            
    Case 2
                MotorChar2.LinearOffset = vec
        
    End Select
    End Sub
    private 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
    Private Sub World_BeginContact (Contact As B2Contact)
    'must handle this event if we want to handle the PreSolve event.
    '    Log("#-")
    '    Log("#-Sub World_BeginContact")
        If ActiveCharIndex = 1 Then
            
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact"char1")
        
    else If ActiveCharIndex = 2 Then
            
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact"char2")
        
    End If
     
        
    If bodies <> Null Then
            
    Log("#-  x614, World_BeginContact, bodies.OtherBody.Name = " & bodies.OtherBody.Name)
            
    'If bodies.OtherBody.Name.ToLowerCase.StartsWith("p") And bodies.OtherBody.GraphicName = "" Then
            If bodies.OtherBody.Name.ToLowerCase.StartsWith("p")  Then
                
    Log("#-    x679, stepstone_1")
                X2.SoundPool.PlaySound(
    "stepstone_1")
                bodies.OtherBody.GraphicName = 
    "steppad_collision"
                bodies.OtherBody.SwitchFrameIntervalMs = 
    100
                X2.AddFutureTask2(Me, 
    "FuTask_StopPoint_Highlight"1000, bodies.OtherBody, True)
         
            
    else if bodies.OtherBody.Name.ToLowerCase.StartsWith("textval"Then
                X2.SoundPool.PlaySound(
    "Pickup__008")
                X2.AddFutureTask2(Me, 
    "FuTask_TextVal_Hit"1000, bodies.OtherBody, True)
             
            
    End If
        
    End If
     
    End Sub
    Sub FuTask_TextVal_Hit (ft As X2FutureTask)
        
    Dim bwhit As X2BodyWrapper = ft.Value
        
    Dim HitValue As Int = bwhit.Name.SubString(7)
        
    Log("#-Sub gm.FuTask_TextVal_Hit, bwhit.Name=" & bwhit.Name)
        TextValScore(bwhit, HitValue)
        
    If ActiveCharIndex = 1 Then
            mChar1.ItemAddValue(
    "collectedvalues", HitValue)
            mChar2.ItemAddValue(
    "collectedvalues""-" & (HitValue/2))
                 
        
    else If ActiveCharIndex = 2 Then
            mChar2.ItemAddValue(
    "collectedvalues", HitValue)
            mChar1.ItemAddValue(
    "collectedvalues""-" & (HitValue/2))
                 
        
    End If
        UI_RefreshPlayerStates
     
    End Sub
    Sub FuTask_StopPoint_Highlight (ft As X2FutureTask)
        
    Log("#-Sub gm.FuTask_StopPoint_Highlight")
       
    Dim point As X2BodyWrapper = ft.Value
       
    point.GraphicName = "steppad_normal"
    End Sub
    Public Sub TextValScore (bw As X2BodyWrapper, Score As Int)
        
    ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 1.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 2.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 3.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 4.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 5.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 6.png
     
    End Sub 
     
     
    Private Sub World_PreSolve (Contact As B2Contact, OldManifold As B2Manifold)
    '    Log("#-Sub World_PreSolve")
    '    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
    '    Log("#-  x637, BodyA.Name=" & BodyA.Name &", BodyB.Name=" & BodyB.Name)
        '    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)
    '    Log("#-Sub World_PostSolve")
    '    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
    '    Log("#-  x649, BodyA.Name=" & BodyA.Name &", BodyB.Name=" & BodyB.Name)
    End Sub
    private Sub ActiveCharacterSelect_SelectedChange(Selected As Boolean) ' ####testingonly
        LastDiceRollVal = 0
        
    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
    private Sub SwapPlayer
        
    Log("#-Sub game.SwapPlayer")
        
    If CharIsMoving Then Return
     
        
    If ActiveCharIndex =1 Then
            RadioButton2.Selected = 
    True
        
    Else
            RadioButton1.Selected = 
    True
        
    End If
     
    '    ' ####testingonly
    '    mChar1.bw.Body.FirstFixture.SetFilterBits(TextArea5.Text, TextArea6.Text)
    '    mChar1.bw.Body.FirstFixture.Density = TextArea4.Text
    '    mChar1.bw.Body.FirstFixture.Friction = TextArea3.Text
    '    mChar1.bw.Body.FirstFixture.Restitution = TextArea1.Text
    '    mChar2.bw.Body.FirstFixture.SetFilterBits(TextArea5.Text, TextArea6.Text)
    '    mChar2.bw.Body.FirstFixture.Density = TextArea4.Text
    '    mChar2.bw.Body.FirstFixture.Friction = TextArea3.Text
    '    mChar2.bw.Body.FirstFixture.Restitution = TextArea1.Text
    '    '/####testingonly
     
    End Sub
    private Sub Button1_Click ' ####testingonly
        Log("#-")
        
    Log("#-  x708, ROLL DICE ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ O")
        
    If CharIsMoving Then Return
        X2.SoundPool.PlaySound(
    "rolldice")
        Sleep(
    200)
        LastDiceRollVal = 
    Rnd(37)
        Label2.Text = LastDiceRollVal
        ActiveCharAdvance(LastDiceRollVal)
    End Sub
    Private Sub UI_RefreshPlayerStates
        
    Log("#-Sub game.UI_RefreshPlayerStates")
        Label4.Text  = mChar1.ItemGet(
    "collectedvalues""0")
        Label12.Text = mChar2.ItemGet(
    "collectedvalues""0")
    '    Log("#-  x747, " &  fcn.logm(mChar1.CollectedItemsMap))
    '    Log("#-  x748, " &  fcn.logm(mChar2.CollectedItemsMap))
    End Sub
    private Sub ActiveCharAdvance(NumberOfSteps As Int)
        
    Log("#-Sub ActiveCharAdvance")
        
    ' Fill the CharPoints list that is used in Tick()
     
        
    Dim NewPointTemplatesListIndex As Int =    CharLastPositionIndex(ActiveCharIndex) +NumberOfSteps
        
    Dim GotToPosIndex As Int = NewPointTemplatesListIndex Mod PointTemplatesListLoop0.Size
        
    '####temp 
        Dim xy0 As PointXy = PointTemplatesListLoop0.Get(GotToPosIndex)
        Label5.Text = 
    $"ActiveCharIndex=${ActiveCharIndex}, ${CRLF}DiceRollVal=${LastDiceRollVal}, ${CRLF}MoveCharTo p${(GotToPosIndex +1)}  = $1.0{xy0.x}, $1.0{xy0.y}  "$    '####test
        Log("#-  x593, Label5.Text = " & Label5.Text.Replace(CRLFTAB))
        
    '/####temp 
       
        
    Dim TempList As List
        TempList.Initialize
        
    For i = CharLastPositionIndex(ActiveCharIndex) To NewPointTemplatesListIndex
            
    Dim GotToPosIndex As Int = i Mod PointTemplatesListLoop0.Size
            
    Dim xy0 As PointXy = PointTemplatesListLoop0.Get(GotToPosIndex)
            TempList.Add(X2.CreateVec2(xy0.x, xy0.y) )
        
    Next
        
    Select Case ActiveCharIndex
            
    Case 1
                Char1Points.Initialize2(TempList)
            
    Case 2
                Char2Points.Initialize2(TempList)
        
    End Select
        CharLastPositionIndex(ActiveCharIndex) = GotToPosIndex
    End Sub
    'private Sub Touch_Object_P(ft As X2FutureTask)
    '    Dim Object_px As X2BodyWrapper = ft.Value
    '    Log("#-Sub game.Touch_Object_P, Object_px.Name = " & Object_px.Name)
    '    If Object_px.Name.ToLowerCase.StartsWith("p") Then
    '        X2.SoundPool.PlaySound("stepstone_1")
    '    Else If Object_px.Name.ToLowerCase.StartsWith("text") Then
    '        X2.SoundPool.PlaySound("metal-clash")
    '    End If
    'End Sub

    2019-08-28_17-39-03.jpg 2019-08-28_17-42-28.jpg

    The animated graphic ("coin") should appear in several places on the playing field. The positions should correspond to those of certain tilemap objects ("TextVal...").

    It is intended to create several clones from a single object ("coin") in Sub CreateObjects after traversing the templates. These clones should be placed next to the objects whose names begin with "TextVal".

    Code:
    private Sub CreateObjects
    ...
        
    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

    ...
          
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("textval"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("coin"Then

                
    ' Creates the 1 and only object with the animated coin
                Dim bwobjcoin1 As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX )
              
    ...

        
    Next

    ...
      
        
    ' After all positions of the "TextVal" objects are determined:
        ' CLONE the 1 animated "coin" object to all postions of "TextVal" objects
        For Each tt As TextTemplates In TextTemplatesList
            CreateCloneFromBw(bwobjcoin1, tt.Template.Position.CreateCopy)
        
    Next
      
    End Sub

    Sub CreateCloneFromBw(obj0 As X2BodyWrapper, TargetPosition As B2Vec2)
    '    Dim bwclone As X2BodyWrapper = obj0.Body.
    '    TileMap.CreateObject(obj0.Body.)
     
        
    ' ??? 
     
    End Sub
    How can I create multiple clones from an object defined in the tilemap?

    (A relevant test project is attached.)
     

    Attached Files:

    Last edited: Aug 29, 2019
  2. Erel

    Erel Administrator Staff Member Licensed User

    upload_2019-8-29_8-45-4.png

    Several ways to do it. The simplest one is to use the 'copy from id' property.

    Just add points objects, set their name to 'coins' and set the 'copy from id' field in Tiled to 99. 99 is the id of the original coin.

    Code:
    Code:
    Else If TileMapTemplateX.Name = "coins" Then
               TileMap.CreateObject(TileMapTemplateX)
    The way to do it programmatically is by creating multiple bodies from the same template in different positions:
    Code:
    Sub CreateCloneFromBw(template As X2TileObjectTemplate, TargetPosition As B2Vec2)
       template.BodyDef.Position = TargetPosition
       TileMap.CreateObject(template)
    End Sub
    And:
    Code:
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("coin"Then
               
    ' Creates the 1 and only object with the animated coin
               Dim CoinTemplate As X2TileObjectTemplate = TileMapTemplateX
               TileMap.CreateObject(TileMapTemplateX )
     
    fredo likes this.
  3. fredo

    fredo Active Member Licensed User

    Thank you very much.

    I think this is the best solution in this case, because I only have to move the "TextVal" objects in the tilemap if necessary. The coins will then appear next to them automatically.

    2019-08-29_17-45-51.png

    Code:
    '
    ' 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
        
    '
        Private ISDEBUG As Boolean = False ' It's easier to change it here instead of find it further down
        '
        ' Collision filtering
        ' Gunther --> https://www.b4x.com/android/forum/threads/tool-collision-bits-and-masks-calculator-collision-filtering.103320/
        Public const COLL_CATEGORY_PLAYER As Int  = 0x0001 ' 0000000000000001 in binary
        Public const COLL_CATEGORY_MONSTER As Int = 0x0002 ' 0000000000000010 in binary
        Public const COLL_CATEGORY_SCENERY As Int = 0x0004 ' 0000000000000100 in binary
        
        
    Public const COLL_MASK_PLAYERHITS_MONSTERORSCENERY As Int  = 0xFFFE ' 1111111111111110 in binary
        Public const COLL_MASK_MONSTERHITS_PLAYER As Int           = 0xFFF9 ' 1111111111111001 in binary
        Public const COLL_MASK_SCENERYHITS_PLAYER As Int           = 0xFFF9 ' 1111111111111001 in binary
        
        
    ' The group index can be used to override the category/mask settings for a given set of fixtures.
        ' --> https://www.iforce2d.net/b2dtut/collision-filtering
        Public const COLL_GROUP_PLAYER As Int = -1
        
    Public const COLL_GROUP_MONSTER As Int = -2
        
    Public const COLL_GROUP_SCENERY As Int = 1
        
        
    ' Basic x2 libraries that are needed in every game
        Public  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
        
        
    ' Parameters
        Public const MotorCorrFacInitVal As Float = 0.03
        
    Public const MotorCorrFacNormal As Float = 0.07
        
    Public const MotorCorrFacEnd As Float = 0.06
        
        
    ' Gamestate
        Private GameOverState As Boolean = False
        
    Private CharIsMoving As Boolean = False
        
    Private ActiveCharIndex As Int = 1
        
    Private LastDiceRollVal As Int = 0
        
    Private CharLastPositionIndex(3As Int
        
    Type PointXy(x As Float, y As Float)
        
    Private Char1Points As List
        
    Private Char2Points As List
        
        
    ' 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 Pane2 As Pane' ####testingonly
        Private fx As JFX' ####testingonly
        Private TempTestPane As Pane' ####testingonly
        Private Label5 As Label' ####testingonly
    '    Private TextArea1 As TextArea ' Restitution
    '    Private TextArea3 As TextArea ' Friction
    '    Private TextArea4 As TextArea ' Density
    '    Private TextArea5 As TextArea ' Category bits
    '    Private TextArea6 As TextArea ' Mask bits
    '    Private TextArea7 As TextArea ' mcfA
    '    Private TextArea8 As TextArea ' mcfB
        Private Label4 As Label ' coll pl1
        Private Label12 As Label ' coll pl2
    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
        CSSUtils.SetBackgroundColor(Pane2, fx.Colors.LightGray)' ####testingonly
        
        
    ' ──────────────────────────────────
        ' Preparations for World
        ' ──────────────────────────────────
        world.Initialize("world", world.CreateVec2(00))
        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)
        
        
    '    TextArea8.Text = MotorCorrFacNormal ' ####testingonly
    '    TextArea7.Text = MotorCorrFacEnd ' ####testingonly
        
        
    ' ──────────────────────────────────
        ' Preparations for movements
        ' ──────────────────────────────────
        PathMainBCForward.Initialize(00)
        PathMainBCBackwards.Initialize(
    00)
        PathWorld.Initialize
    '    BrushRed = X2.MainBC.CreateBrushFromColor(xui.Color_Red)
        CharLastPositionIndex(0) = -1 ' not used
        CharLastPositionIndex(1) = 0 ' for Char1
        CharLastPositionIndex(2) = 0 ' for Char2
        
        Char1Points.Initialize
        Char2Points.Initialize
        
        
    ' ──────────────────────────────────
        ' Preparations for graphics
        ' ──────────────────────────────────
        GraphicCache_Put_Objects
        GraphicCache_Put_Characters
        
        
        
    ' ──────────────────────────────────
        ' Preparations for screen
        ' ──────────────────────────────────
        'SetBackground
        CreateStaticBackground
        
    'CreateBorder
        
        
        
    ' ──────────────────────────────────
        ' Preparations for sounds
        ' ──────────────────────────────────
        X2.SoundPool.AddSound("rolldice"File.DirAssets, "dice-4.wav")
        X2.SoundPool.AddSound(
    "stepstone_1"File.DirAssets, "stepstone_1.wav")
        
    'X2.SoundPool.AddSound("metal-clash", File.DirAssets, "metal-clash.wav")
        X2.SoundPool.AddSound("Pickup__008"File.DirAssets, "Pickup__008.wav")
        
        
        
    ' ──────────────────────────────────
        ' Debug settings
        ' ──────────────────────────────────
        If ISDEBUG Then
            X2.EnableDebugDraw    
    ' Comment out to disable debug drawing
        End If
        
    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_Objects
        
    Log("#-Sub game.GraphicCache_Put_Objects")
        
    '
        Dim RowWidthPixel As Int = 32
        
    Dim RowHeightPixel As Int = 32
        
    Dim CollGraphHeightMeters As Int = 2
        
        
    ' -------------------------------
        ' Step-pads
        ' -------------------------------
        Dim bc1 As BitmapCreator = X2.BitmapToBC(xui.LoadBitmap(File.DirAssets, "dungeon_tilesetmodrollo.png"), 1)
        
    Dim bmp As B4XBitmap = bc1.Bitmap
        
    Dim CollGraph1 As B4XBitmap = bmp.Crop(1 * 3218 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
        
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 11, CollGraphHeightMeters, CollGraphHeightMeters)
        X2.GraphicCache.PutGraphic(
    "steppad_collision"Array(CollGraph1List.Get(0)) )
        
        
    Dim bc2 As BitmapCreator = X2.BitmapToBC(xui.LoadBitmap(File.DirAssets, "dungeon_tilesetmodrollo.png"), 1)
        
    Dim bmp As B4XBitmap = bc2.Bitmap
        
    Dim CollGraph1 As B4XBitmap = bmp.Crop(5 * 3218 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
        
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 11, CollGraphHeightMeters, CollGraphHeightMeters)
        X2.GraphicCache.PutGraphic(
    "steppad_normal"Array(CollGraph1List.Get(0)) )
            
    End Sub
    Private Sub GraphicCache_Put_Characters
        
    Log("#-Sub game.GraphicCache_Put_Characters")

        
    ' -------------------------------
        ' 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
                pt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                PointTemplatesList.Add(pt)
                
                
    Dim bwobj As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
                bwobj.Graphicname = 
    "steppad_normal"
                
    If Not(TileMapTemplateX.Name.Contains(".")) And Not(TileMapTemplateX.Name.Contains("x")) Then
                    
    'Log("#-    x250, " & $"${TileMapTemplateX.Name} --> PointTemplatesListLoop0(${tempcounter_pt0})= $1.0{pt.Template.Position.X}, $1.0{pt.Template.Position.Y}  "$)
                    Dim xy0 As PointXy
                    xy0.Initialize
                    xy0.x = pt.Template.Position.X
                    xy0.y = pt.Template.Position.y
                    PointTemplatesListLoop0.Add(xy0)
                    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, 
    40dip40dip)
                
    ' /####testingonly
                
                
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("textval"Then
                
    Dim tt As TextTemplates
                tt.Template = TileMapTemplateX
                tt.XPosition = TileMapTemplateX.Position.X
                tt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                TextTemplatesList.Add(tt)
                TileMap.CreateObject(TileMapTemplateX )
                
                
    ' ####testingonly
                ' Show the labels of text
                Dim lblx As Label
                lblx.Initialize(
    "")
                lblx.Text = TileMapTemplateX.Name.Replace(
    "TextVal","")
                
    If TileMapTemplateX.Name.ToLowerCase.StartsWith("textval"Then
                    lblx.TextSize = 
    20
                
    Else
                    lblx.TextSize = 
    10
                
    End If
                lblx.TextColor = fx.Colors.Magenta
                
    'lblx.Alignment = "CENTER_RIGHT"
                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, 
    40dip40dip)
                
    ' /####testingonly
                
            
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("char"Then
                
    Dim ft As FigureTemplates
                ft.Template = TileMapTemplateX
                ft.XPosition = TileMapTemplateX.Position.X
                ft.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_PLAYER, COLL_MASK_PLAYERHITS_MONSTERORSCENERY)
                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
                tt.Template.FixtureDef.SetFilterBits(COLL_CATEGORY_SCENERY, COLL_MASK_SCENERYHITS_PLAYER)
                LocationTemplatesList.Add(lt)
                TileMap.CreateObject(TileMapTemplateX)
                
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase = "border" Then
                
    'nothing to do here. It is created in StartGame()
            
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("area"Then
                TileMap.CreateObject(TileMapTemplateX)
                
            
    else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("coin"Then
                
    ' Creates the 1 and only object with the animated coin
                ' It will be moved out of sight after all "coin" clones are redy
                Dim CoinTemplate As X2TileObjectTemplate = TileMapTemplateX
                
    Dim CoinBodywrapper As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
                
            
    Else
                
    Log("#-  x313, " & $"Unhandled object --> id=${TileMapTemplateX.Id}, ${TileMapTemplateX.Name}"$)
            
    End If
        
    Next
        
        FigureTemplatesList.SortType(
    "XPosition"True)
        PointTemplatesList.SortType(
    "XPosition"True)
        TextTemplatesList.SortType(
    "XPosition"True)
        LocationTemplatesList.SortType(
    "XPosition"True)
        
        
    ' After all positions of the "TextVal" objects are determined:
        ' CLONE the 1 animated "coin" object to all postions of "TextVal" objects
        For Each tt As TextTemplates In TextTemplatesList
            CreateCloneFromBw(CoinTemplate, tt.Template.Position.CreateCopy)
        
    Next
        CoinBodywrapper.Body.SetTransform(X2.CreateVec2(-
    1, -1), 0' This object is no longer needed
        
        
    End Sub

    Sub CreateCloneFromBw(template As X2TileObjectTemplate, TargetPosition As B2Vec2)
       template.BodyDef.Position = TargetPosition
       TileMap.CreateObject(template)
    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")
        bc.FillGradient(Array As Int(0xFF61584C0xFF61584C), bc.TargetRect, "TOP_BOTTOM"' brownish
        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 MotorDefForChar As B2MotorJointDef
        MotorDefForChar.Initialize(Border.Body, character.bw.Body)
        MotorDefForChar.MaxMotorForce = 
    1500
        MotorDefForChar.CollideConnected = 
    True 'let the Char collide with the borders
        Dim mc As B2MotorJoint = X2.mWorld.CreateJoint(MotorDefForChar)
        
    'mc.CorrectionFactor = 0.02
        mc.CorrectionFactor = MotorCorrFacInitVal
        
    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)

        
    #region ---Move active character by motor
    '    Dim voff As B2Vec2 = X2.CreateVec2(0, 1.1) ' offset, so that the char is slightly beneath the pad
        Dim voff As B2Vec2 = X2.CreateVec2(00' offset, so that the char is slightly beneath the pad
        Select Case ActiveCharIndex
            
    Case 1
                
    If Char1Points.Size > 0 Then
                    
    Dim NextPoint As B2Vec2 = Char1Points.Get(0)
                    NextPoint = NextPoint.CreateCopy
                    
    If Char1Points.Size < 2 Then
                        MotorChar1.CorrectionFactor = MotorCorrFacEnd
                        NextPoint.AddToThis(voff)
                    
    Else
                        MotorChar1.CorrectionFactor = MotorCorrFacNormal
                    
    End If
                    
                    
    Dim vec As B2Vec2 = mChar1.bw.Body.Position.CreateCopy
                    vec.SubtractFromThis(NextPoint)
                    
    If vec.Length < 1 Then
                        Char1Points.RemoveAt(
    0)
                        
    If Char1Points.Size = 0 Then
                            PlayerFinished
                        
    End If
                    
    Else
                        MoveCharByMotorTo(ActiveCharIndex , NextPoint)
                    
    End If
                
    End If
                
            
    Case 2
                
    ' ~~  ~~  ~~  ~~  ~~  ~~  ~~
                ' duplicate code from "case 1" in order to avoid a timeconsuming call to a separate sub
                ' ~~  ~~  ~~  ~~  ~~  ~~  ~~
                If Char2Points.Size > 0 Then
                    
    Dim NextPoint As B2Vec2 = Char2Points.Get(0)
                    NextPoint = NextPoint.CreateCopy
                    
    Dim vec As B2Vec2 = mChar2.bw.Body.Position.CreateCopy
                    
    If Char2Points.Size < 2 Then
                        MotorChar2.CorrectionFactor = MotorCorrFacEnd
                        NextPoint.AddToThis(voff)
                    
    Else
                        MotorChar2.CorrectionFactor = MotorCorrFacNormal
                    
    End If
                    vec.SubtractFromThis(NextPoint)
                    
    If vec.Length < 2 Then
                        Char2Points.RemoveAt(
    0)
                        
    If Char2Points.Size = 0 Then
                            PlayerFinished
                        
    End If
                    
    Else
                        MoveCharByMotorTo(ActiveCharIndex , NextPoint)
                    
    End If
                
    End If
        
    End Select
        
    #end region
    End Sub
    private Sub PlayerFinished
        CharIsMoving = 
    False
        SwapPlayer
    End Sub
    ' ────────────────────────────────────────────────────────────────────────────────────────────────
    Public Sub GameOver
        X2.SoundPool.StopMusic
    '    X2.SoundPool.PlaySound("gameover")
        X2.AddFutureTask(Me, "Set_GameOver"3500Null)
    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
        
        
    ' ──────────────────────────────────
        ' Preparations for Players
        ' ──────────────────────────────────
        mChar1.CollectedItemsMap.Initialize
        mChar2.CollectedItemsMap.Initialize
            
        
    ' ──────────────────────────────────
        ' Start the Main loop
        ' ──────────────────────────────────
        X2.Start
        
    End Sub
     
    private 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

    private Sub MoveCharByMotorTo (CharIndex As Int, vec As B2Vec2)
    '    Log("#-Sub MoveCharByMotorTo, CharIndex=" & CharIndex & ", vec.X=" & vec.X & ", vec.y=" & vec.y)
        CharIsMoving = True
        vec.SubtractFromThis(Border.Body.Position)
        
    Select Case CharIndex
            
    Case 1
                MotorChar1.LinearOffset = vec
            
    Case 2
                MotorChar2.LinearOffset = vec
        
    End Select
    End Sub
    private 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

    Private Sub World_BeginContact (Contact As B2Contact)
    'must handle this event if we want to handle the PreSolve event.
    '    Log("#-")
    '    Log("#-Sub World_BeginContact")
        If ActiveCharIndex = 1 Then
            
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact"char1")
        
    else If ActiveCharIndex = 2 Then
            
    Dim bodies As X2BodiesFromContact = X2.GetBodiesFromContact(Contact"char2")
        
    End If
      
        
    If bodies <> Null Then
            
    Log("#-  x614, World_BeginContact, bodies.OtherBody.Name = " & bodies.OtherBody.Name)
            
    'If bodies.OtherBody.Name.ToLowerCase.StartsWith("p") And bodies.OtherBody.GraphicName = "" Then
            If bodies.OtherBody.Name.ToLowerCase.StartsWith("p")  Then
                
    Log("#-    x679, stepstone_1")
                X2.SoundPool.PlaySound(
    "stepstone_1")
                bodies.OtherBody.GraphicName = 
    "steppad_collision"
                bodies.OtherBody.SwitchFrameIntervalMs = 
    100
                X2.AddFutureTask2(Me, 
    "FuTask_StopPoint_Highlight"1000, bodies.OtherBody, True)
            
            
    else if bodies.OtherBody.Name.ToLowerCase.StartsWith("textval"Then
                X2.SoundPool.PlaySound(
    "Pickup__008")
                X2.AddFutureTask2(Me, 
    "FuTask_TextVal_Hit"1000, bodies.OtherBody, True)
                
            
    End If
        
    End If
        
    End Sub
    Sub FuTask_TextVal_Hit (ft As X2FutureTask)
        
    Dim bwhit As X2BodyWrapper = ft.Value
        
    Dim HitValue As Int = bwhit.Name.SubString(7)
        
    Log("#-Sub gm.FuTask_TextVal_Hit, bwhit.Name=" & bwhit.Name)
        TextValScore(bwhit, HitValue)
        
    If ActiveCharIndex = 1 Then
            mChar1.ItemAddValue(
    "collectedvalues", HitValue)
            mChar2.ItemAddValue(
    "collectedvalues""-" & (HitValue/2))
                    
        
    else If ActiveCharIndex = 2 Then
            mChar2.ItemAddValue(
    "collectedvalues", HitValue)
            mChar1.ItemAddValue(
    "collectedvalues""-" & (HitValue/2))
                    
        
    End If
        UI_RefreshPlayerStates
        
    End Sub
    Sub FuTask_StopPoint_Highlight (ft As X2FutureTask)
        
    Log("#-Sub gm.FuTask_StopPoint_Highlight")
       
    Dim point As X2BodyWrapper = ft.Value
       
    point.GraphicName = "steppad_normal"
    End Sub
    Public Sub TextValScore (bw As X2BodyWrapper, Score As Int)

        
    ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 1.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 2.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 3.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 4.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 5.png
        ' D:\_Rollo\rolloApps\_SchatzJäger\Shared Files\star coin rotate 6.png
        
    End Sub   
        
        
    Private Sub World_PreSolve (Contact As B2Contact, OldManifold As B2Manifold)
    '    Log("#-Sub World_PreSolve")
    '    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
    '    Log("#-  x637, BodyA.Name=" & BodyA.Name &", BodyB.Name=" & BodyB.Name)
        '    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)
    '    Log("#-Sub World_PostSolve")
    '    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
    '    Log("#-  x649, BodyA.Name=" & BodyA.Name &", BodyB.Name=" & BodyB.Name)
    End Sub

    private Sub ActiveCharacterSelect_SelectedChange(Selected As Boolean) ' ####testingonly
        LastDiceRollVal = 0
        
    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

    private Sub SwapPlayer
        
    Log("#-Sub game.SwapPlayer")
        
    If CharIsMoving Then Return
        
        
    If ActiveCharIndex =1 Then
            RadioButton2.Selected = 
    True
        
    Else
            RadioButton1.Selected = 
    True
        
    End If
        
    '    ' ####testingonly
    '    mChar1.bw.Body.FirstFixture.SetFilterBits(TextArea5.Text, TextArea6.Text)
    '    mChar1.bw.Body.FirstFixture.Density = TextArea4.Text
    '    mChar1.bw.Body.FirstFixture.Friction = TextArea3.Text
    '    mChar1.bw.Body.FirstFixture.Restitution = TextArea1.Text
    '    mChar2.bw.Body.FirstFixture.SetFilterBits(TextArea5.Text, TextArea6.Text)
    '    mChar2.bw.Body.FirstFixture.Density = TextArea4.Text
    '    mChar2.bw.Body.FirstFixture.Friction = TextArea3.Text
    '    mChar2.bw.Body.FirstFixture.Restitution = TextArea1.Text
    '    '/####testingonly
        
    End Sub
    private Sub Button1_Click ' ####testingonly
        Log("#-")
        
    Log("#-  x708, ROLL DICE ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ # ~~~~~ O")
        
    If CharIsMoving Then Return
        X2.SoundPool.PlaySound(
    "rolldice")
        Sleep(
    200)
        LastDiceRollVal = 
    Rnd(37)
        Label2.Text = LastDiceRollVal
        ActiveCharAdvance(LastDiceRollVal)
    End Sub

    Private Sub UI_RefreshPlayerStates
        
    Log("#-Sub game.UI_RefreshPlayerStates")
        Label4.Text  = mChar1.ItemGet(
    "collectedvalues""0")
        Label12.Text = mChar2.ItemGet(
    "collectedvalues""0")
    '    Log("#-  x747, " &  fcn.logm(mChar1.CollectedItemsMap))
    '    Log("#-  x748, " &  fcn.logm(mChar2.CollectedItemsMap))
    End Sub

    private Sub ActiveCharAdvance(NumberOfSteps As Int)
        
    Log("#-Sub ActiveCharAdvance")
        
    ' Fill the CharPoints list that is used in Tick()
        
        
    Dim NewPointTemplatesListIndex As Int =    CharLastPositionIndex(ActiveCharIndex) +NumberOfSteps
        
    Dim GotToPosIndex As Int = NewPointTemplatesListIndex Mod PointTemplatesListLoop0.Size

        
    '####temp   
        Dim xy0 As PointXy = PointTemplatesListLoop0.Get(GotToPosIndex)
        Label5.Text = 
    $"ActiveCharIndex=${ActiveCharIndex}, ${CRLF}DiceRollVal=${LastDiceRollVal}, ${CRLF}MoveCharTo p${(GotToPosIndex +1)}  = $1.0{xy0.x}, $1.0{xy0.y}  "$    '####test
        Log("#-  x593, Label5.Text = " & Label5.Text.Replace(CRLFTAB))
        
    '/####temp   
        
        
    Dim TempList As List
        TempList.Initialize
        
    For i = CharLastPositionIndex(ActiveCharIndex) To NewPointTemplatesListIndex
            
    Dim GotToPosIndex As Int = i Mod PointTemplatesListLoop0.Size
            
    Dim xy0 As PointXy = PointTemplatesListLoop0.Get(GotToPosIndex)
            TempList.Add(X2.CreateVec2(xy0.x, xy0.y) )
        
    Next
        
    Select Case ActiveCharIndex
            
    Case 1
                Char1Points.Initialize2(TempList)
            
    Case 2
                Char2Points.Initialize2(TempList)
        
    End Select
        CharLastPositionIndex(ActiveCharIndex) = GotToPosIndex
    End Sub

    'private Sub Touch_Object_P(ft As X2FutureTask)
    '    Dim Object_px As X2BodyWrapper = ft.Value
    '    Log("#-Sub game.Touch_Object_P, Object_px.Name = " & Object_px.Name)
    '    If Object_px.Name.ToLowerCase.StartsWith("p") Then
    '        X2.SoundPool.PlaySound("stepstone_1")
    '    Else If Object_px.Name.ToLowerCase.StartsWith("text") Then
    '        X2.SoundPool.PlaySound("metal-clash")
    '    End If
    'End Sub


    ' The project questions & solution is continued here: https://www.b4x.com/android/forum/threads/x2-help-needed-regarding-creating-object-clones.109094/

    Now I have to come up with something that triggers a fascinating animation on the coin on contact.
     

    Attached Files:

Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice