The enclosed test project is supposed to be a turn-based top down RPG board game for 2 characters.
The x2 examples were used to create the basic structures and load the graphics.
Since the interaction of the X2 framework components has not yet been sufficiently understood, concrete references to the required "best practices" would be needed.
In order to continue with the development, the following problem points need to be solved:
Planned course: Players shall roll their dice alternately and advance the corresponding number of fields, sometimes choosing which way to go.
The x2 examples were used to create the basic structures and load the graphics.
B4X:
' ------- -------------- ------
#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files"
' This is required to copy all files from the "Shared Files" folder to the "Files" folder of the respective platform.
' Erel --> https://www.b4x.com/android/forum/threads/xui2d-cross-platform-tips.96815/#content
' ------- -------------- ------
' ------- -------------- ------
' X2Test1 "Basic structures"
' x2notes --> https://www.b4x.com/android/forum/threads/some-notes-to-better-understand-the-xui2d-examples.107702/
' x2examples --> https://www.b4x.com/android/forum/threads/xui2d-example-pack.96454/
' @Gunter’s roadmap into the game dev world --> https://www.b4x.com/android/forum/threads/video-tutorial.99172/#post-624377
' ------- -------------- ------
#if B4A
'ignore DIP related warnings as they are not relevant when working with BitmapCreator.
#IgnoreWarnings: 6
#end if
Sub Class_Globals
'
' Basic x2 libraries that are needed in every game
Private xui As XUI 'ignore
Public X2 As X2Utils
Public world As B2World
'
' Layout elements
Private ivForeground As B4XView
Private ivBackground As B4XView
Public lblStats As B4XView
Private pnlTouch As B4XView
'
' TileMap specific definitions
Public TileMap As X2TileMap
Public Const ObjectLayer As String = "Object Layer 1"
'
' ExtraClasses
Private mChar1 As Figure
Private mChar2 As Figure
' Gamestate
Private GameOverState As Boolean
' Bodies and joints
Private Border As X2BodyWrapper
Private MouseMotor As B2MotorJoint
' Type EnemyTemplates(Template As X2TileObjectTemplate, XPosition As Float)
' Private EnemyTemplatesList As List
Type FigureTemplates(Template As X2TileObjectTemplate, XPosition As Float)
Private FigureTemplatesList As List
Type PointTemplates(Template As X2TileObjectTemplate, XPosition As Float)
Private PointTemplatesList As List
Type TextTemplates (Template As X2TileObjectTemplate, XPosition As Float)
Private TextTemplatesList As List
End Sub
Public Sub Initialize (Parent As B4XView)
'
' ──────────────────────────────────
' Preparations for Layout and Views
' ──────────────────────────────────
Parent.LoadLayout("GameLayout")
lblStats.TextColor = xui.Color_Black
lblStats.Color = 0x88ffffff
lblStats.Font = xui.CreateDefaultBoldFont(20)
' ──────────────────────────────────
' Preparations for World
' ──────────────────────────────────
world.Initialize("world", world.CreateVec2(0, -10))
X2.Initialize(Me, ivForeground, world)
Dim WorldWidth As Float = 36 'meters
Dim WorldHeight As Float = WorldWidth / 1.333 'same ratio as in the designer script
X2.ConfigureDimensions(world.CreateVec2(WorldWidth / 2, WorldHeight / 2), WorldWidth)
' ──────────────────────────────────
' Preparations for graphics
' ──────────────────────────────────
GraphicCache_Put_Characters
' ──────────────────────────────────
' Preparations for screen
' ──────────────────────────────────
SetBackground
'CreateStaticBackground
'CreateBorder
' ──────────────────────────────────
' Preparations for sounds
' ──────────────────────────────────
'X2.SoundPool.AddSound("small jump", File.DirAssets, "small_jump.mp3")
'X2.SoundPool.AddSound("big jump", File.DirAssets, "big_jump.mp3")
'X2.SoundPool.AddSound("powerup", File.DirAssets, "powerup.mp3")
' ──────────────────────────────────
' Debug settings
' ──────────────────────────────────
X2.EnableDebugDraw ' Comment out to disable debug drawing
End Sub
Private Sub SetWorldCenter
' The map size will not be identical to the screen size.
' This happens because the tile size in (bc) pixels needs to be a whole number.
' So we need to update the world center and move the map to the center.
X2.UpdateWorldCenter(TileMap.MapAABB.Center)
End Sub
Public Sub WorldCenterUpdated (gs As X2GameStep)
CreateEnemies
End Sub
private Sub SetBackground
'X2.SetBitmapWithFitOrFill(ivBackground, xui.LoadBitmapResize(File.DirAssets, "mybackgroundimage.jpg", ivBackground.Width / 2, ivBackground.Height / 2, False))
End Sub
Private Sub GraphicCache_Put_Characters
Log("#-Sub game.GraphicCache_Put_Characters")
' Use bitmap without the transparency placeholdercolor:
Dim bc As BitmapCreator = X2.BitmapToBC( xui.LoadBitmap(File.DirAssets, "RPGCharacterSprites32x32.png"), 1)
RemovePseudoTransparentColor(bc, "#ff00ff") ' pink, magenta
Dim bmp As B4XBitmap = bc.Bitmap
Dim NumberOfSprites As Int = 12
Dim RowWidth As Int = 32
Dim RowHeight As Int = 32
Dim CharHeightMeters As Int = 3
'
Dim RowOfChar1 As Int = 2
Dim character1 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar1, NumberOfSprites * RowWidth, RowHeight)
Dim AllChar1 As List = X2.ReadSprites(character1, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
X2.GraphicCache.PutGraphic("character1 front walking", Array(AllChar1.Get(0), AllChar1.Get(1), AllChar1.Get(2), AllChar1.Get(3)))
X2.GraphicCache.PutGraphic("character1 front standing", Array(AllChar1.Get(3)))
X2.GraphicCache.PutGraphic("character1 back walking", Array(AllChar1.Get(4), AllChar1.Get(5), AllChar1.Get(6), AllChar1.Get(7)))
X2.GraphicCache.PutGraphic("character1 back standing", Array(AllChar1.Get(7)))
X2.GraphicCache.PutGraphic("character1 side walking", Array(AllChar1.Get(8), AllChar1.Get(9), AllChar1.Get(10)) )
X2.GraphicCache.PutGraphic("character1 side standing", Array(AllChar1.Get(9)) )
'
Dim RowOfChar2 As Int = 3
Dim character2 As B4XBitmap = bmp.Crop(0, RowHeight * RowOfChar2, NumberOfSprites * RowWidth, RowHeight)
Dim AllChar2 As List = X2.ReadSprites(character2, 1, NumberOfSprites, CharHeightMeters, CharHeightMeters)
X2.GraphicCache.PutGraphic("character2 front walking", Array(AllChar2.Get(0), AllChar2.Get(1), AllChar2.Get(2), AllChar2.Get(3)))
X2.GraphicCache.PutGraphic("character2 front standing", Array(AllChar2.Get(3)))
X2.GraphicCache.PutGraphic("character2 back walking", Array(AllChar2.Get(4), AllChar2.Get(5), AllChar2.Get(6), AllChar2.Get(7)))
X2.GraphicCache.PutGraphic("character2 back standing", Array(AllChar2.Get(7)))
X2.GraphicCache.PutGraphic("character2 side walking", Array(AllChar2.Get(8), AllChar2.Get(9), AllChar2.Get(10)) )
X2.GraphicCache.PutGraphic("character2 side standing", Array(AllChar2.Get(9)) )
'
End Sub
Private Sub RemovePseudoTransparentColor(TilesBC As BitmapCreator, clrstring As String)
' Erel --> https://www.b4x.com/android/forum/threads/x2-how-to-use-transparent-color.108173/#post-676534
Dim clr As Int = 0xff000000 + Bit.ParseInt(clrstring.SubString(1), 16)
Dim ptranspm As PremultipliedColor
Dim trans As PremultipliedColor
Dim pm As PremultipliedColor
Dim argb As ARGBColor
TilesBC.ColorToARGB(clr, argb)
TilesBC.ARGBToPremultipliedColor(argb, ptranspm)
For y = 0 To TilesBC.mHeight - 1
For x = 0 To TilesBC.mWidth - 1
TilesBC.GetPremultipliedColor(x, y, pm)
If Bit.And(0xff, pm.r) = ptranspm.r And Bit.And(0xff, pm.g) = ptranspm.g And Bit.And(0xff, pm.b) = ptranspm.b And Bit.And(0xff, pm.a) = ptranspm.a Then
TilesBC.SetPremultipliedColor(x, y, trans)
End If
Next
Next
End Sub
private Sub CreateObjects
Log("#-Sub game.CreateObjects")
FigureTemplatesList.Initialize
PointTemplatesList.Initialize
TextTemplatesList.Initialize
Dim ol As X2ObjectsLayer = TileMap.Layers.Get(ObjectLayer)
For Each TileMapTemplateX As X2TileObjectTemplate In ol.ObjectsById.Values
If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("p") Then
Dim pt As PointTemplates
pt.Template = TileMapTemplateX
pt.XPosition = TileMapTemplateX.Position.X
PointTemplatesList.Add(pt)
TileMap.CreateObject(TileMapTemplateX)
else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("text") Then
Dim tt As TextTemplates
tt.Template = TileMapTemplateX
tt.XPosition = TileMapTemplateX.Position.X
TextTemplatesList.Add(tt)
TileMap.CreateObject(TileMapTemplateX)
else If TileMapTemplateX.FirstTime And TileMapTemplateX.Name.ToLowerCase.StartsWith("char") Then
Dim ft As FigureTemplates
ft.Template = TileMapTemplateX
ft.XPosition = TileMapTemplateX.Position.X
FigureTemplatesList.Add(ft)
Dim bwChX As X2BodyWrapper = TileMap.CreateObject(TileMapTemplateX)
bwChX.Body.BodyType = bwChX.Body.TYPE_KINEMATIC
If TileMapTemplateX.Name.ToLowerCase.EndsWith("1") Then
mChar1.Initialize(bwChX) ' Set the delegate (the class "Figure")
mChar1.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
else If TileMapTemplateX.Name.ToLowerCase.EndsWith("2") Then
mChar2.Initialize(bwChX) ' Set the delegate (the class "Figure")
mChar2.FigureNameAndId = TileMapTemplateX.Name & "~" & TileMapTemplateX.id
End If
End If
Next
FigureTemplatesList.SortType("XPosition", True)
PointTemplatesList.SortType("XPosition", True)
TextTemplatesList.SortType("XPosition", True)
Log("#- x206, FigureTemplatesList.size = " & FigureTemplatesList.Size)
Log("#- x207, PointTemplatesList.size = " & PointTemplatesList.Size)
Log("#- x208, TextTemplatesList.size = " & TextTemplatesList.Size)
End Sub
Private Sub CreateEnemies
' Do While EnemyTemplatesList.Size > 0
' Dim et As EnemyTemplates = EnemyTemplatesList.Get(0)
' If et.XPosition <= X2.ScreenAABB.TopRight.X Then
' Dim bw As X2BodyWrapper = TileMap.CreateObject(et.Template)
' Dim enX As Enemy
' enX.Initialize(bw) 'this sets the delegate
' enX.IsGhost = (bw.GraphicName = "ghost")
' EnemyTemplatesList.RemoveAt(0)
' Else
' Exit
' End If
' Loop
End Sub
Private Sub CreateRevJoint12
' Dim template As X2TileObjectTemplate = TileMap.GetObjectTemplateByName(ObjectLayer, "hinge")
' Dim revdef As B2RevoluteJointDef
' revdef.Initialize(BodyXy1.Body, BodyXy2.Body, template.BodyDef.Position)
' revdef.SetLimits(0, cPI / 2)
' revdef.LimitEnabled = True
' revdef.MaxMotorTorque = 10
' revjoint12 = world.CreateJoint(revdef)
' revjoint12.MotorEnabled = True
' BodyXy2.Body.GravityScale = 0
End Sub
Private Sub CreateStaticBackground
Dim bc As BitmapCreator
bc.Initialize(ivBackground.Width / xui.Scale / 2, ivBackground.Height / xui.Scale / 2)
bc.FillGradient(Array As Int(0xFF001AAC, 0xFFC5A400), bc.TargetRect, "TOP_BOTTOM")
X2.SetBitmapWithFitOrFill(ivBackground, bc.Bitmap)
End Sub
Private Sub CreateBorder
TileMap.CreateObject(TileMap.GetObjectTemplateByName(ObjectLayer, "border"))
End Sub
' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub Resize
X2.ImageViewResized
End Sub
Public Sub DrawingComplete
TileMap.DrawingComplete
End Sub
'Return True to stop the game loop.
Public Sub BeforeTimeStep (GS As X2GameStep) As Boolean
If GameOverState Then
Return True
End If
Return False
End Sub
Public Sub Tick (GS As X2GameStep)
' Log("#-Sub game.Tick, gs.GameTimeMs = " & GS.GameTimeMs)
TileMap.DrawScreen(Array("Tile Layer 1"), GS.DrawingTasks)
mChar1.Tick(GS)
mChar2.Tick(GS)
End Sub
' ────────────────────────────────────────────────────────────────────────────────────────────────
Public Sub GameOver
X2.SoundPool.StopMusic
X2.SoundPool.PlaySound("gameover")
X2.AddFutureTask(Me, "Set_GameOver", 3500, Null)
End Sub
Private Sub Set_GameOver (ft As X2FutureTask)
GameOverState = True
Sleep(500)
StartGame
End Sub
Public Sub StopGame
X2.SoundPool.StopMusic
X2.Stop
End Sub
Public Sub StartGame
If X2.IsRunning Then Return
X2.Reset
X2.UpdateWorldCenter(X2.CreateVec2(X2.ScreenAABB.Width / 2, X2.ScreenAABB.Height / 2))
GameOverState = False
' ──────────────────────────────────
' Preparations for Tilemap
' ──────────────────────────────────
TileMap.Initialize(X2, File.DirAssets, "TiledMapFile_proj01.json", ivBackground)
Dim TileSizeMeters As Float = X2.ScreenAABB.Height / TileMap.TilesPerColumn
TileMap.SetSingleTileDimensionsInMeters(TileSizeMeters, TileSizeMeters)
SetWorldCenter ' Update the world center based on the map size
TileMap.PrepareObjectsDef(ObjectLayer)
Border = TileMap.CreateObject2ByName(ObjectLayer, "border")
' ──────────────────────────────────
' Draw Tilemap
' ──────────────────────────────────
Dim tasks As List
tasks.Initialize
TileMap.Draw(Array("Tile Layer 1"), TileMap.MapAABB, tasks)
For Each dt As DrawTask In tasks
If dt.IsCompressedSource Then
TileMap.CurrentBC.DrawCompressedBitmap(dt.Source, dt.SrcRect, dt.TargetX, dt.TargetY)
End If
Next
' ──────────────────────────────────
' Preparations for bodies
' ──────────────────────────────────
CreateObjects
' ──────────────────────────────────
' Motors for Characters
' ──────────────────────────────────
Dim MotorDefChar1 As B2MotorJointDef
MotorDefChar1.Initialize(Border.Body, mChar1.bw.Body )
MotorDefChar1.MaxMotorTorque = 1
MotorDefChar1.MaxMotorForce = 1
MotorDefChar1.CollideConnected = True 'let the Char1 collide with the borders
MouseMotor = X2.mWorld.CreateJoint(MotorDefChar1)
MouseMotor.CorrectionFactor = 0.1
Dim MotorDefChar2 As B2MotorJointDef
MotorDefChar2.Initialize(Border.Body, mChar2.bw.Body )
MotorDefChar2.MaxMotorTorque = 1
MotorDefChar2.MaxMotorForce = 1
MotorDefChar2.CollideConnected = True 'let the Char2 collide with the borders
MouseMotor = X2.mWorld.CreateJoint(MotorDefChar2)
MouseMotor.CorrectionFactor = 0.1
' ──────────────────────────────────
' Start the Main loop
' ──────────────────────────────────
X2.Start
End Sub
Sub pnlTouch_Touch (Action As Int, X As Float, Y As Float)
If Action = pnlTouch.TOUCH_ACTION_MOVE_NOTOUCH Then Return
Dim WorldPoint As B2Vec2 = X2.ScreenPointToWorld(X, Y)
Dim MainBCPoint As B2Vec2 = X2.WorldPointToMainBC(WorldPoint.X, WorldPoint.Y)
If Action = pnlTouch.TOUCH_ACTION_DOWN Then
Dim FirstPointBC As B2Vec2 = X2.WorldPointToMainBC(mChar1.bw.Body.Position.X, mChar1.bw.Body.Position.Y)
' 'Clone the paths before modifying them.
' PathMainBCForward = PathMainBCForward.Clone
' PathMainBCForward.Reset(FirstPointBC.X, FirstPointBC.Y)
' PathMainBCBackwards = PathMainBCBackwards.Clone
' PathMainBCBackwards.Reset(FirstPointBC.X, FirstPointBC.Y)
' PathWorld.Clear
End If
' If PathWorld.Size > 0 Then
' Dim PrevPoint As B2Vec2 = PathWorld.Get(PathWorld.Size - 1)
' Dim distance As B2Vec2 = PrevPoint.CreateCopy
' distance.SubtractFromThis(WorldPoint)
' 'to improve performance we skip very close points.
' If distance.LengthSquared < 0.1 Then
' Return
' End If
' End If
' PathMainBCForward = PathMainBCForward.Clone
' PathMainBCForward.LineTo(MainBCPoint.X, MainBCPoint.Y)
' PathWorld.Add(WorldPoint)
End Sub
Since the interaction of the X2 framework components has not yet been sufficiently understood, concrete references to the required "best practices" would be needed.
In order to continue with the development, the following problem points need to be solved:
- The Bitmap of the Tilemap should have the same area of extension as the area of the form.
- The Characters should have their starting position on the sand areas on the left side.
- The characters should "drive" by motorjoint from one object-point (e.g. "p1") to a certain other object-point (e.g. "p6") on a path along the points series.
- If a character collides with a text object, an event should be triggered.