
It has been a while since I’ve written a new article on this blog because it have been busy months both at work and in my personal life. One of the things I wanted to do was writing an easy to use Game Engine for B4A. I have written the ABPhysics engine in the past and recently Informatix pointed out I had started another engine (ABGameEngine) before that. Development on ABGameEngine was stopped early for several reasons: time was one of them, but also because several other developers were working on an engine themselves. It seemed a little bit pointless to continue.
However, the other engines were not further developed either. Until recently Erel came up with the GameView. This is an excellent View that will cover the needs of a lot of beginning programmers. I definitely would like to urge starting game developers to take a look at this. Registered users of B4A can download the GameView lib from here.
But still, I wanted something more. I looked at ABgameEngine again, as it had some great ideas like layers, animated sprites, gamepads etc. But it was also very old code and not written very well. And it crashed all the time 🙂
I decided to restart from scratch a couple of weeks ago. Some weekends and evenings later ABPlay was born! And with a lot of goodies!
Here is a small demo video of what I got so far. it demonstrates the following:
1. Layers
(Layer 1) the moving background with the Odies as animated sprites
(Layer 2) the black foreground layer with a weird dancing creature that passes now and then
2. Animated sprites, not bound to a layer
(our hero Garfield is back!)
3. A sprite can have different animations.
(like one for standing, one for walking, one for fighting, etc)
4. A sprite can have different predefined 'Walks'
(A walk can be build like you would build a path. It's a sequence of lines, bezier curves, wait periods etc. It's like a simple flash movie)
5. Gamepad controls
(the 'joystick' pad on the left)
(the 'action' pad on the right)
(the 'direction' pad as an alternative to the joystick, not shown in this video)
6. In the demo I cannot demonstrate it with a mouse, but it is completely multi touch
(You can control Garfield AND press the Action button X AND do a swipe anywhere at the same time)
But let’s have a look (beware this is running on the Emulator. On a real device it is much smoother):
I’m actually very pleased with the result. The graphics and handling is very smooth.
Above all, it’s still very easy to program. Here is the whole code for the demo app:
#Region Project Attributes
#ApplicationLabel: ABPlayTest
#VersionCode: 1
#VersionName:
'SupportedOrientations possible values: unspecified, landscape or portrait.
#SupportedOrientations: landscape
#CanInstallToExternalStorage: False
#End Region
#Region Activity Attributes
#FullScreen: true
#IncludeTitle: false
#End Region
Sub Process_Globals
End Sub
Sub Globals
Dim myPlay As ABPlay
' the panel that will hold the ABPlay
Dim myPanel As Panel
' a background and foregroud layer
Dim bgLayer As ABLayer
Dim fgLayer As ABLayer
' some colors to show the multi touch points in the demo
Dim myColors() As Int = Array As Int(Colors.Red, Colors.Green, Colors.Blue, Colors.Cyan, Colors.Yellow, Colors.Gray, Colors.White, Colors.Magenta, Colors.LightGray, Colors.DarkGray)
' our hero
Dim Hero As ABSprite
' our enemies
Dim Enemies As List
' param to set the set the speed
Dim Speed As Float = 0.2
' some variables to hold the current state of the hero
Dim currentAction As String
Dim currentDirection As String
Dim currentIsStanding As Boolean
End Sub
Sub Activity_Create(FirstTime As Boolean)
' initialize ABPlay with myPanel
myPanel.Initialize("")
Activity.AddView(myPanel, 0,0,100%x,100%y)
myPlay.Initialize(myPanel, "myPlay")
'////////////// BEGIN loading sprite sequences
' Load Sprite Sequences
myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
myPlay.LoadSpriteSequence("GarStandingRight",LoadBitmap(File.DirAssets, "garrightstill.png"),6, 1, 1000)
myPlay.LoadSpriteSequence("GarWalkingLeft",LoadBitmap(File.DirAssets, "garleft.png"),8, 1, 1000)
myPlay.LoadSpriteSequence("GarWalkingRight",LoadBitmap(File.DirAssets, "garright.png"),8, 1, 1000)
myPlay.LoadSpriteSequence("GarStandingLeftFight",LoadBitmap(File.DirAssets, "garleftstillfight.png"),2, 1, 250)
myPlay.LoadSpriteSequence("GarStandingRightFight",LoadBitmap(File.DirAssets, "garrightstillfight.png"),2, 1, 250)
myPlay.LoadSpriteSequence("GarWalkingLeftFight",LoadBitmap(File.DirAssets, "garleftfight.png"),7, 1, 1000)
myPlay.LoadSpriteSequence("GarWalkingRightFight",LoadBitmap(File.DirAssets, "garrightfight.png"),7, 1, 1000)
' and the ones for Odie
myPlay.LoadSpriteSequence("OdieStandingLeft", LoadBitmap(File.DirAssets, "odieleftstill.png"),7,1,Rnd(800,1200))
myPlay.LoadSpriteSequence("OdieStandingRight", LoadBitmap(File.DirAssets, "odierightstill.png"),7,1,Rnd(800,1200))
myPlay.LoadSpriteSequence("OdieWalkingLeft", LoadBitmap(File.DirAssets, "odieleft.png"),5,1,Rnd(800,1200))
myPlay.LoadSpriteSequence("OdieWalkingRight", LoadBitmap(File.DirAssets, "odieright.png"),5,1,Rnd(800,1200))
' and the one for the creature
myPlay.LoadSpriteSequence("CreaturePassToRight", LoadBitmap(File.DirAssets, "creature.png"), 3, 4, 1200)
'////////////// END loading sprite sequences
'////////////// BEGIN building the Hero Garfield
' initialize the hero
Hero.Initialize("Hero", 50%x, 50%y)
' add sprite sequences
Hero.AddSpriteSequence("GarStandingLeft")
Hero.AddSpriteSequence("GarStandingRight")
Hero.AddSpriteSequence("GarWalkingLeft")
Hero.AddSpriteSequence("GarWalkingRight")
Hero.AddSpriteSequence("GarStandingLeftFight")
Hero.AddSpriteSequence("GarStandingRightFight")
Hero.AddSpriteSequence("GarWalkingLeftFight")
Hero.AddSpriteSequence("GarWalkingRightFight")
Hero.StartSpriteSequence("GarStandingLeft", True)
currentDirection="LEFT"
'////////////// END building the Hero Garfield
'////////////// BEGIN building the background layer with Odies
' initalize a background layer
bgLayer.Initialize("background", 0,0)
bgLayer.SetBackground(LoadBitmap(File.DirAssets, "bga.jpg"), 1.0)
myPlay.AddLayer(bgLayer)
' initialize some enemies
Enemies.Initialize
Dim a As Int
For a = 1 To 10
Dim Odie As ABSprite
' initialize an Odie with some animation sequences
Odie.Initialize("Odie" & a, Rnd(10%x,90%x), Rnd(10%y, 90%y))
' add multiple sprite sequences
Odie.AddSpriteSequences(Array As String("OdieStandingLeft","OdieStandingRight","OdieWalkingLeft","OdieWalkingRight"))
' create a random walk to the right
Dim WalkRight As ABSpriteWalk
WalkRight.Initialize("ToTheRight", True)
Dim newX As Int = Odie.x+Rnd(20%x, 80%x)
WalkRight.AddLine("OdieWalkingRight",Odie.x, Odie.y, newX, Odie.y, Rnd(90,100))
WalkRight.AddWait("OdieStandingRight",newX, Odie.y, Rnd(10,50))
WalkRight.AddLine("OdieWalkingLeft",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
WalkRight.AddWait("OdieStandingLeft",Odie.x, Odie.y, Rnd(10,50))
Odie.AddWalk(WalkRight)
' create a random walk to the left
Dim WalkLeft As ABSpriteWalk
WalkLeft.Initialize("ToTheLeft", True)
Dim newX As Int = Odie.x-Rnd(20%x, 80%x)
WalkLeft.AddLine("OdieWalkingLeft",Odie.x, Odie.y, newX, Odie.y , Rnd(90,100))
WalkLeft.AddWait("OdieStandingLeft",newX, Odie.y, Rnd(10,50))
WalkLeft.AddLine("OdieWalkingRight",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
WalkLeft.AddWait("OdieStandingRight",Odie.x, Odie.y, Rnd(10,50))
Odie.AddWalk(WalkLeft)
' pick random a walk, left or right
Dim GoLeft As Int = Rnd(0,2)
If GoLeft = 0 Then
Odie.StartWalk("ToTheRight")
Else
Odie.StartWalk("ToTheLeft")
End If
bgLayer.AddSprite(Odie)
Next
'////////////// END building the background layer with Odies
'////////////// BEGIN building the foreground layer with creature
' initalize a foreground layer
Dim fgLayer As ABLayer
fgLayer.Initialize("foreground", 0,0)
fgLayer.SetBackground(LoadBitmap(File.DirAssets, "fga.png"), 100%y/400)
myPlay.AddLayer(fgLayer)
' and a weird creature...
Dim creature As ABSprite
creature.Initialize("creature", -100%x, 100%y-240)
creature.AddSpriteSequence("CreaturePassToRight")
' with a walk
Dim pass As ABSpriteWalk
pass.Initialize("DanseToTheRight", True)
pass.AddLine("DansingRight",creature.x, creature.y, 200%x, creature.y, 300)
creature.AddWalk(pass)
' and start the walk
creature.StartWalk("DanseToTheRight")
' add the creature to the foreground layer
fgLayer.AddSprite(creature)
'////////////// END building the foreground layer with creature
'////////////// BEGIN Add game pads
' initialize and start a Joystick Gamepad
myPlay.InitializeJoystickPad(18, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "joystick_bg.png"),LoadBitmap(File.DirAssets, "joystick.png"))
myPlay.ShowJoystickPad(True)
' initialize and start a Action Gamepad
myPlay.InitializeActionPad(100%x-210, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "action_active.png"),LoadBitmap(File.DirAssets, "action_inactive.png"),LoadBitmap(File.DirAssets, "action_mask.png"),True, False, True, False)
myPlay.ShowActionPad(True)
'////////////// END Add game pads
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
' NEEDED FOR THE MOMENT TO CATCH THE HOME KEY, RESUME/PAUSE NOT YET SUPPORTED!
myPlay.StopAndRecycle
Activity.Finish
End Sub
Sub Activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
Select Case KeyCode
Case KeyCodes.KEYCODE_BACK
' NEEDED: stop the drawing thread and recycle stuff
myPlay.StopAndRecycle
Activity.Finish
Return True
Case KeyCodes.KEYCODE_HOME
Return True
End Select
End Sub
Sub myPlay_Draw(c As Canvas, State As ABState)
' the actual drawing, do NOT set a debug stop in here!
'////////////// BEGIN Calculation stuff
' calculate hero stuff depending on what buttons we pressed on the gamepad
currentAction = ""
If State.UsingActionPad Then
' go into fight state
If State.ACTION_X Then
currentAction = "X"
End If
' go back to center
If State.ACTION_Y Then
Hero.SetPostition(50%x,50%y)
End If
End If
Dim XMovement As Int
Dim YMovement As Int
If State.UsingJoystickPad Then
XMovement = State.JOYSTICK_X*Speed
YMovement = State.JOYSTICK_Y*Speed
' update the hero's position and direction
If XMovement<0 Then
currentDirection = "LEFT"
Else
currentDirection = "RIGHT"
End If
Hero.SetPostition(Hero.x+XMovement, Hero.y+YMovement)
currentIsStanding = False
Else
currentIsStanding = True
End If
' set the animation type
Select Case currentDirection
Case "LEFT"
Select Case currentAction
Case "X"
If currentIsStanding Then
Hero.StartSpriteSequence("GarStandingLeftFight", False)
Else
Hero.StartSpriteSequence("GarWalkingLeftFight", False)
End If
Case Else
If currentIsStanding Then
Hero.StartSpriteSequence("GarStandingLeft", False)
Else
Hero.StartSpriteSequence("GarWalkingLeft", False)
End If
End Select
Case "RIGHT"
Select Case currentAction
Case "X"
If currentIsStanding Then
Hero.StartSpriteSequence("GarStandingRightFight", False)
Else
Hero.StartSpriteSequence("GarWalkingRightFight", False)
End If
Case Else
If currentIsStanding Then
Hero.StartSpriteSequence("GarStandingRight", False)
Else
Hero.StartSpriteSequence("GarWalkingRight", False)
End If
End Select
End Select
' update the hero animation
Hero.Update
' move our backgrounds, does not make sense but shows the possibilities
Dim newX, newY As Int
Dim newMovementX, newMovementY As Int
newMovementX=Min(Abs(XMovement),1)
newMovementY=Min(Abs(YMovement),1)
If currentIsStanding = False Then
If currentDirection = "LEFT" Then
newX = bgLayer.ViewX
newY = bgLayer.ViewY
If newX-newMovementX >= 0 Then
newX = newX-newMovementX
End If
If newY-newMovementY >= 0 Then
newY = newY-newMovementY
End If
bgLayer.SetLayerPostion(newX, newY)
Else
newX = bgLayer.ViewX
newY = bgLayer.ViewY
If newX+newMovementX <= bgLayer.OuterWidth - myPlay.Width Then
newX = newX+newMovementX
End If
If newY+newMovementY <= bgLayer.OuterHeight - myPlay.Height Then
newY = newY+newMovementY
End If
bgLayer.SetLayerPostion(newX, newY)
End If
End If
'////////////// END Calculation stuff
'////////////// BEGIN Drawing stuff
'ok, all the calulations are done, let's draw!
' draw the background layer with all its sprites on it and advance all sprite animations and walks
myPlay.DrawLayer("background", c)
Dim a As Int
' draw the multitouch points that are not on the gamepad, no action here but just to show the possibilities
For a = 0 To State.touchPoints.Size - 1
Dim tmpP As ABTouchPoint
tmpP = State.touchPoints.GetValueAt(a)
c.DrawCircle(tmpP.X, tmpP.Y, 50dip, myColors(tmpP.id), True, 1dip)
Next
' draw the hero
Hero.Draw(c)
' draw the foreground layer with all its sprites on it and advance all sprite animations and walks
myPlay.DrawLayer("foreground", c)
'////////////// END Drawing stuff
' NEEDED: very last line of the Draw event. Let ABPlay know it may process touches again!
myPlay.DrawDone()
End Sub
I’m not there yet, but it’s a good start. It needs a lot more testing and a lot of new features.
Until next time!
Alwaysbusy
if you like my work