
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