Games [x2][solved] Can the arrangement of objects change during runtime?

fredo

Well-Known Member
Licensed User
Longtime User
If the character objects are moved in the course of the game, the touched steppad should briefly take on another graphic and then look like before again.

This works very well so far with "World_BeginContact()" and "AddFutureTask".

After adding code elsewhere ("Figure.Collision_WithCoin"), the figure appears behind the steppad.

2019-08-30_09-18-55.png


The order of the objects was also not deliberately changed.
2019-08-30_09-22-33.png

B4X:
----------------
game.bas
----------------
#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(3) As 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
    
    ' Font
    Private ScoreFont As B4XFont

    ' 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(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)
    
    
'    TextArea8.Text = MotorCorrFacNormal ' ####testingonly
'    TextArea7.Text = MotorCorrFacEnd ' ####testingonly
    
    ' ──────────────────────────────────
    ' 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
    
    Char1Points.Initialize
    Char2Points.Initialize
    
    ' ──────────────────────────────────
    ' Preparations for graphics
    ' ──────────────────────────────────
    GraphicCache_Put_Objects
    GraphicCache_Put_Characters
    #if B4J
        Dim fx As JFX
        ScoreFont = fx.LoadFont(File.DirAssets, "Fixedsys500c.ttf", 30)
    #else if B4i
        ScoreFont = Font.CreateNew2("FixedsysTTF", 30)
    #else
        ScoreFont = xui.CreateFont(Typeface.LoadFromAssets("Fixedsys500c.ttf"), 30 / xui.Scale)
    #End If
    
    
    ' ──────────────────────────────────
    ' 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 * 32, 18 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 1, 1, 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 * 32, 18 * RowHeightPixel, 2 * RowWidthPixel, 2 * RowHeightPixel)
    Dim CollGraph1List As List = X2.ReadSprites(CollGraph1, 1, 1, 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, 40dip, 40dip)
            ' /####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, 40dip, 40dip)
            ' /####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, CreateMap()) ' 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, CreateMap()) ' 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
            Border = TileMap.CreateObject2ByName(ObjectLayer, "border")

        else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase = "scoretemplate" Then
            TileMap.CreateObject(TileMapTemplateX)
        
        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
    For Each tt As TextTemplates In TextTemplatesList
        CreateCloneFromBw(CoinTemplate, tt.Template.Position.CreateCopy) ' CLONE the 1 animated "coin" object to all postions of "TextVal" objects
        CreateScoreGraphics(tt.Template.Name.SubString(7)) ' Cached graphic for coin collisions
    Next
    CoinBodywrapper.Body.SetTransform(X2.CreateVec2(-1, -1), 0) ' This object is no longer needed
    
    
End Sub
private Sub CreateScoreGraphics(TextValue As String)
    Dim GraphicName As String = "ScoreValue" & TextValue
'    Log("#-Sub gm.CreateScoreGraphics, TextValue=" & TextValue & ", GraphicName=" & GraphicName)
    
    If X2.GraphicCache.GetGraphicsCount(GraphicName) = 0 Then
        Dim cvs As B4XCanvas = X2.GraphicCache.GetCanvas(50)
        Dim bmps As List
        bmps.Initialize
        Dim FontSize As Float = 30 / xui.Scale
        Dim fnt As B4XFont = xui.CreateDefaultBoldFont(FontSize)
        Dim r As B4XRect = cvs.MeasureText(TextValue, fnt)
        Dim BaseLine As Int = cvs.TargetRect.CenterY - r.Height / 2 - r.Top
        
        'Creating new graphics is a relatively heavy operation. Use cached graphics whenever possible.
        For Each clr As Int In Array(0xFF00B2FF, xui.Color_White)
            cvs.ClearRect(cvs.TargetRect)
            cvs.DrawText(TextValue,  cvs.TargetRect.CenterX, BaseLine, fnt, clr, "CENTER")
            Dim sb As X2ScaledBitmap
            sb.Scale = 1
            'Use X2.CreateImmutableBitmap if not cropping.
            sb.Bmp = cvs.CreateBitmap.Crop(cvs.TargetRect.CenterX - r.Width / 2, cvs.TargetRect.CenterY - r.Height / 2, r.Width + 1, r.Height + 1)
            bmps.Add(sb)
        Next
        X2.GraphicCache.PutGraphic2(GraphicName, bmps, False, 5) 'no antialiasing.
    End If
    
End Sub
Private 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(0xFF61584C, 0xFF61584C), 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(0, 0) ' 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
    Log("#-Sub gm.PlayerFinished")
    CharIsMoving = False
    SwapPlayer
    Log("#-   x546, ~~~~")
    Log("#-")
    Log("#-")
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)

    
    ' ──────────────────────────────────
    ' 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
            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
            Dim HitValue As Int = bodies.OtherBody.Name.SubString(7)
            'CreateScore(bodies.OtherBody.Body.Position, HitValue)
            X2.SoundPool.PlaySound("Pickup__008")
            If ActiveCharIndex = 1 Then
                mChar1.ItemAddValue("collectedvalues", HitValue)
                mChar2.ItemAddValue("collectedvalues", "-" & (HitValue/2))
                X2.AddFutureTask2(mChar1, "Collision_WithCoin", 0, bodies.OtherBody, True)
            else If ActiveCharIndex = 2 Then
                mChar2.ItemAddValue("collectedvalues", HitValue)
                mChar1.ItemAddValue("collectedvalues", "-" & (HitValue/2))
                X2.AddFutureTask2(mChar2, "Collision_WithCoin", 0, bodies.OtherBody, True)
            End If
            X2.AddFutureTask2(Me, "FuTask_TextVal_Hit", 1000, bodies.OtherBody, True)
        End If
    End If
    
End Sub

private 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, ActiveCharIndex=" & ActiveCharIndex & ", bwhit.Name=" & bwhit.Name)
    UI_RefreshPlayerStates
    
End Sub
private Sub FuTask_StopPoint_Highlight (ft As X2FutureTask)
   Dim point As X2BodyWrapper = ft.Value
   point.GraphicName = "steppad_normal"
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 gm.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(3, 7)
    Label2.Text = LastDiceRollVal
    ActiveCharAdvance(LastDiceRollVal)
End Sub

Private Sub UI_RefreshPlayerStates
    Log("#-Sub gm.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(CRLF, TAB))
    '/####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


------------
figure.bas
------------
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
    Public CollectedItemsMap As Map
    Public DeletesCollidedCoins As Boolean = False
    
    
    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, ParameterMap As Map)
'    Log("#-Sub figure.Initialize")
    CollectedItemsMap.Initialize
    LastPositionIndex = 0
    bw = wrapper
    x2 = bw.X2
    bw.DelegateTo = Me
    UpdateImpulseVector
    CreateChar1Legs
    
    DeletesCollidedCoins = ParameterMap.GetDefault("DeletesCollidedCoins", False)
    
End Sub

Private Sub CreateChar1Legs
    ' see --> https://www.b4x.com/android/forum/threads/xui2d-super-mario-example-2.96236/
'    Dim rect As B2PolygonShape
'    rect.Initialize
'    'rect.SetAsBox2(0.5, 0.5, x2.CreateVec2(0, -x2.GetShapeWidthAndHeight(bw.Body.FirstFixture.Shape).y / 2 + 0.05), 0)
'    rect.SetAsBox2(0.5, 0.5, x2.CreateVec2(0, -x2.GetShapeWidthAndHeight(bw.Body.FirstFixture.Shape).y / 2 + 0.5), 0)
'    Dim f As B2Fixture = bw.Body.CreateFixture2(rect, 0.1)
'    'f.Friction = 1
'    f.Friction = 0
'    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)
    If LastTickTime = GS.GameTimeMs Then Return
    LastTickTime = GS.GameTimeMs
'    Log("#-Sub figure.Tick --> " & bw.Name)
    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
 
public Sub ItemSet(Key As String, value As String)
    If value = "" Then
        CollectedItemsMap.Remove(Key)
    Else
        CollectedItemsMap.Put(Key, value)
    End If
End Sub
public Sub ItemGet(Key As String, default As String) As String
     Return CollectedItemsMap.GetDefault(Key, default)
End Sub
public Sub ItemAddValue(Key As String, Summand As String)
    Dim IntValue As Int = CollectedItemsMap.GetDefault(Key, 0)
    ItemSet(Key, IntValue +Summand)
End Sub

Public Sub Collision_WithCoin (ft As X2FutureTask)
    Log("Sub figure.Collision_WithCoin")
    Dim coin As X2BodyWrapper = ft.Value
    If coin.IsDeleted Then Return
    
    ' The moving scores are not physical bodies.
    ' Their movement is managed in the MovingScore class.
    ' They don't have a shape.
    ' bw.X2.SoundPool.PlaySound("coin")
    Dim bwhit As X2BodyWrapper = ft.Value
    Dim score As Int = bwhit.Name.SubString(7)
    Dim TimeToLive As Int = 2000
    bw.X2.AddFutureTask(Me, "Add_Score",TimeToLive, score) 'add the score when the moving score reaches the corner
    Dim bd As B2BodyDef
    bd.BodyType = bd.TYPE_STATIC
    bd.Position = coin.Body.Position
    If DeletesCollidedCoins Then
        coin.Delete(bw.X2.gs)
    End If

    Dim mscore As MovingScore
    Dim wrapper As X2BodyWrapper = bw.X2.CreateBodyAndWrapper(bd, mscore, "score")
    mscore.Initialize(wrapper, score)
    wrapper.TimeToLiveMs = TimeToLive
    
    'uncomment to add a slow down effect
'    bw.X2.SlowDownPhysicsScale = 2
'    bw.X2.UpdateTimeParameters
'    bw.X2.AddFutureTask(Me, "Reset_PhysicsScore", 500, Null)

End Sub
Private Sub Add_Score (ft As X2FutureTask)
    Log("#-Sub figure.Add_Score, ft.value=" & ft.Value)
    'bw.mGame.mScore.IncreaseScore(ft.Value)
End Sub


-------
movingscore.bas
--------------
' From the "Walking character" example from Erel's Example Pack --> https://www.b4x.com/android/forum/threads/xui2d-example-pack.96454/#content
Sub Class_Globals
    Private xui As XUI
    Private bw As X2BodyWrapper
    Private StartBCPosition, TargetBCPosition As B2Vec2
    Private degrees As Int
End Sub

Public Sub Initialize (wrapper As X2BodyWrapper, score As Int)
    bw = wrapper
    
    'This body doesn't have any fixture so it is always considered to be invisible.
    'We must set TickIfInvisible to True or it will never tick. A better option would have to create a fixture...
    bw.TickIfInvisible = True

    bw.GraphicName = "ScoreValue" & score ' Bitmap was created in Game.CreateScoreGraphics()
    bw.SwitchFrameIntervalMs = 200
    'StartBCPosition = bw.X2.WorldPointToMainBC(bw.Body.Position.X, bw.Body.Position.Y)
    StartBCPosition = bw.X2.WorldPointToMainBC(bw.Body.Position.X, bw.Body.Position.Y)
    
    
    Dim target As B2Vec2
    'target.Set(bw.X2.ScreenAABB.BottomLeft.X + 0.5, bw.X2.ScreenAABB.TopRight.Y - 0.2)
    target.Set(bw.Body.Position.X + 5, bw.Body.Position.Y + 5)
    
    TargetBCPosition = bw.X2.WorldPointToMainBC(target.X, target.Y)
End Sub
 
Public Sub Tick (GS As X2GameStep)
    bw.SwitchFrameIfNeeded(GS)
    Dim CurrentTime As Int = bw.GetCurrentTime(GS)
    '(target - start) * CurrentTime / TimeToLive + start
    
    Dim position As B2Vec2 = TargetBCPosition.CreateCopy
    position.SubtractFromThis(StartBCPosition)
    position.MultiplyThis(CurrentTime / bw.TimeToLiveMs)
    position.AddToThis(StartBCPosition)
    degrees = degrees + 2 'making the score rotate will work, however rotating "temporary" graphics can have an impact on the performance.
    
    If GS.ShouldDraw Then
        GS.DrawingTasks.Add(bw.X2.GraphicCache.GetDrawTask(bw.GraphicName, bw.CurrentFrame, degrees, False, False, position.X, position.Y))
    End If
    If CurrentTime > bw.TimeToLiveMs Then
        bw.Delete(GS)
    End If
End Sub

What is to be done to keep the arrangement of the objects on top of each other under control?
 

Attachments

  • Testproject_01_040.zip
    500.5 KB · Views: 396
Top