Games [BANanoPhaser] Let's create a Star Catcher Game

Mashiane

Expert
Licensed User
Ola

UPDATE: 2019-11-20 Complete Rewrite

You can also checkout BreakOut here, created with BANanoPhaser also.

With this game we will have our alien who will collect stars and dodge some bombs. We will be able to make him jump and run. We will just create a simple level here.

This was featured here, so here we will go about creating it. After we have done this tut, we will be able to create this game.


NB: The project uses a tweaked version of the BANanoPostProcessor (attached) its not compulsory, you can remove the reference to it.

Details about the Phaser Game Engine are here.

PREPARING THE GAME

1. Step 1, let's get our game space ready. As indicated in this post, we need to have our Preload, Create, and Update events ready. We initialize a scene so that we can have everything in order.

1.1 We preload our resources onPreload
1.2 onCreate we actually code our game
1.3 onUpdate is where the animations / FPS fire and we perform stuff in our game.

This game is on the pgStarCatcher module attached herein.

In Process globals we define some variables for our game..

B4X:
Private game As BANanoPhaser
    Private body As BANanoElement
    Private Scene As PhaserScene
We then create the skeleton for our game.

B4X:
Sub Init
    'set the body of the page
    body = BANano.GetElement("#body")
    body.SetStyle(BANano.ToJson(CreateMap("margin":"0")))
    body.Empty
    'add the div to set the game in
    'lets set up the game
    'initialze the game, use game element and set size
    game.Initialize
    'The Type can be Phaser.CANVAS, Phaser.WEBGL Or Phaser.AUTO. AUTO means that
    'Phaser will Try To render with WebGL, And fall back To Canvas If it fails
    game.SetTypeAuto(True)
    'element to inject the game in
    game.SetParent("body")
    game.SetWidth(800)
    game.SetHeight(640)
    'Ensure the canvas is resized to fit the parent div's dimensions
    game.SetScaleMode(game.ScaleModeRESIZE)
    'Center the game canvas both horizontally and vertically within the parent
    game.SetScaleAutoCenter(game.ScaleCenterCENTER_BOTH)
    'The physics engine determines how objects interact with the world. Phaser
    'supports three physics engines out of the box: arcade, impact and matter.
    ' Arcade is understood to be the simplest one to implement
    game.SetPhysicsDefault("arcade")
 
    'create a scene there can be multiple scenes in the same game
    Scene = game.CreateScene("starCatcher")
    'steps in the game scene to execute
    Scene.SetOnPreload(Me, "onPreLoad")
    Scene.SetOnCreate(Me, "onCreate")
    Scene.SetOnUpdate(Me, "onUpdate")
    game.SetScene(Scene.Scene)
    '
    game.Start
End Sub

Sub onPreload
 
End Sub

Sub OnCreate
 
 
End Sub

Sub onUpdate
 
End Sub
 

Attachments

Last edited:

Mashiane

Expert
Licensed User
LOADING RESOURCES

Now that our game skeleton has been set up, we will start loading our resources for the game. We need a dude, a bomb, some stars and then the platform that the dude will run in.

These we add using our Files tab of our project...

Bomb

bomb.png

Dude (spritesheet)

dude.png

Platform

platform.png

Sky (background)

sky.png

Star

star.png

In our skeleton, we created a Scene. There could be a lot of scenes in a game. So we will load our resources using key, value pairs with the exact URL names for our files. These resources are saved in the ./assets folder of our BANano App. Loading the images onPreload ensures that the resources are in the memory cache and are faster to load should they be needed elsewhere in your game.

There are different methods and properties of the Scene, thus it being prefixed by .SceneLoad etc.

Our preload method is now updated and reflects.

B4X:
Sub onPreload
    Scene.SceneLoadImage("sky", "./assets/sky.png")
    Scene.SceneLoadImage("ground", "./assets/platform.png")
    Scene.SceneLoadImage("star", "./assets/star.png")
    Scene.SceneLoadImage("bomb", "./assets/bomb.png")
    Scene.SceneLoadSpriteSheet("dude", "./assets/dude.png", 32, 48)
End Sub
Congrats you have just completed Part 2 of this tut. We have added the resources we need and a spritesheet that indicates that our dude's width and height are like 32 and 48 respectively.

If you enlarge the dude you will note that it has some frames, we will create the animations based on that.
 
Last edited:

Mashiane

Expert
Licensed User
ADDING OUR GAME BACKGROUND

Now its time to start adding some images to our game. To do that we will use the onCreate method. To add items we place them using X and Y co-ordinates. This places stuff in their centre so we can move them by anchoring them. Its important to know the dimensions of the resources that you are adding.

Again, we are adding stuff to the current scene. The order of how you add items is the order they get displayed.

You used the keys to add resources to the 'cache', so to retrieve them we use the same keys. Let's update our code to add the sky and star and see what happens.

B4X:
Sub OnCreate
    Scene.SceneAddImage(400, 300, "sky")
    Scene.SceneAddImage(400, 300, "star")
End Sub
skystar.png


The image is 800x600 and when added its placed by its center, so to place it to cover the whole screen we divide its actual width & height by 2 and then use those in the x,y position of AddImage, thus 400 and 300 above.

Cool, isnt it?
 
Last edited:

Mashiane

Expert
Licensed User
ADDING PLATFORMS

Next, we need to add some platforms in our game that the dude will step on, run on, jump on. To do this, we have already added the Arcade game system, however let's just update it a little. On it update the game code to reflect that its needed by Phaser for Physics.

B4X:
game.SetPhysicsDefault("arcade")
    game.SetPhysicsArcadeDebug(False)
    game.SetPhysicsArcadeGravity(CreateMap("y":30))
The platforms will be added as a group. With that its easy to define events and everything else that will happen in that group as a single entity. We will create a couple of playforms that will be hosted inside the group. This we will do in our onCreate method. The name of our platform is ground.

Let's update our onCreate sub to add the platforms.

B4X:
Sub onCreate
    Scene.SceneAddImage(400, 300, "sky")
    Scene.SceneAddImage(400, 300, "star")
    '
    'add some platforms
    platforms = Scene.PhysicsAddStaticGroup
    Dim g1 As BANanoObject = Scene.Create(platforms, 400, 568, "ground")
    Scene.SetScale(g1, 2)
    Scene.RefreshBody(g1)
    '
    Scene.Create(platforms, 600, 400, "ground")
    Scene.Create(platforms, 50, 250, "ground")
    Scene.Create(platforms, 750, 220, "ground")
End Sub
Should you run this, you should see something like this..

part4.png


We created a staticGroup, in this group we created 'instances' of the objects we needed and placed them at various locations withing our game scene.

As you might have guessed, Dynamic bodies can be moved by other forces, so the static body added here cannot be moved. So anything colliding with it, it doesnt budge!!
 
Last edited:

Mashiane

Expert
Licensed User
Remember:

Our ground is 400x32. When added, these are placed at center of 400x568. We want the platform to take the whole screen, we then scale it by 2. This makes it 800x64.
As we have scaled a static body i.e. stuff that does not change, we need to refresh it so that the whole physics game knows about the change.

We then continue adding other platforms at the locations we need, we dont scale these though.
 

Mashiane

Expert
Licensed User
ADDING OUR PLAYER

We have added the sky, the platforms, now its time to add the player. As this will be a dynamic body, we will use the Physics engine to add it. This again we are doing in the onCreate sub.

We have defined player as a BANanoObject.

B4X:
'add a player
    'This creates a new sprite called player, positioned at 100 x 450 pixels from the bottom of the game.
    'The sprite was created via the Physics Game Object Factory (this.physics.add) which means it has a Dynamic Physics body by default
    player = Scene.PhysicsAddSprite(100, 450, "dude")
 
    'After creating the sprite it is given a slight bounce value of 0.2.
    'This means when it lands after jumping it will bounce ever so slightly.
    Scene.setBounce(player, 0.2)
    'The sprite is then set to collide with the world bounds.
    'The bounds, by default, are on the outside of the game dimensions.
    'As we set the game To be 800 x 600 then the player won't be able to run outside of this area.
    'It will stop the player from being able to run off the edges of the screen or jump through the top.
    Scene.setCollideWorldBounds(player, True)
The previous screen seemed odd a little, one of the platforms was outside the body, to fix that let's style the body element.

Update the body element style by adding this code.

B4X:
body.SetStyle(BANano.ToJson(CreateMap("margin": "10px", "padding": "0", "width": "800px", "height": "600px")))
If you run the code now, this is what will happen...

mygame.gif
 
Last edited:

Mashiane

Expert
Licensed User
ADDING ANIMATIONS

sprites.png


We loaded a dude sprite that has frames inside. Each frame is 32 by 48 px. This has 4 frames to run to the left, 1 facing the camera and 4 also to run to the right. 9 frames in total. This is what we will use to create our animations.

Let's add the animation to run left,

B4X:
'The 'left' animation uses frames 0, 1, 2 and 3 and runs at 10 frames per second. The '-1' value tells the animation to loop i.e. repeat
    Scene.CreateAnime("left", "dude", 10, 0, 3, -1)
We add the rest of the animations, called turn and right, this completes our code to be:

B4X:
Scene.CreateAnime("left", "dude", 10, 0, 3, -1)
    Scene.CreateAnime("turn", "dude", 20, 4, 4, Null)
    Scene.CreateAnime("right", "dude", 10, 5, 8, -1)
In its complete format, our onCreate sub will now look like this.

B4X:
Sub onCreate
    Scene.SceneAddImage(400, 300, "sky")
    Scene.SceneAddImage(400, 300, "star")
    '
    'add some platforms
    platforms = Scene.PhysicsAddStaticGroup
    Dim g1 As BANanoObject = Scene.Create(platforms, 400, 568, "ground")
    Scene.SetScale(g1, 2)
    Scene.RefreshBody(g1)
    '
    Scene.Create(platforms, 600, 400, "ground")
    Scene.Create(platforms, 50, 250, "ground")
    Scene.Create(platforms, 750, 220, "ground")
    
    'add a player
    'This creates a new sprite called player, positioned at 100 x 450 pixels from the bottom of the game.
    'The sprite was created via the Physics Game Object Factory (this.physics.add) which means it has a Dynamic Physics body by default
    player = Scene.PhysicsAddSprite(100, 450, "dude")
    
    'After creating the sprite it is given a slight bounce value of 0.2.
    'This means when it lands after jumping it will bounce ever so slightly.
    Scene.setBounce(player, 0.2)
    'The sprite is then set to collide with the world bounds.
    'The bounds, by default, are on the outside of the game dimensions.
    'As we set the game To be 800 x 600 then the player won't be able to run outside of this area.
    'It will stop the player from being able to run off the edges of the screen or jump through the top.
    Scene.setCollideWorldBounds(player, True)
    
    'Our player animations, turning, walking left and walking right.
    'The 'left' animation uses frames 0, 1, 2 and 3 and runs at 10 frames per second. The 'repeat -1' value tells the animation to loop.
    Scene.CreateAnime("left", "dude", 10, 0, 3, -1)
    Scene.CreateAnime("turn", "dude", 20, 4, 4, Null)
    Scene.CreateAnime("right", "dude", 10, 5, 8, -1)
    
End Sub
When the player presses a keyboard, we need a way to trap the player and move items left and right and even turn the player.

Congrats. You have just finished part 5 of the tut.
 

Mashiane

Expert
Licensed User
COLLISIONS: PART 1

The player will collide with the platforms, the stars that will be added and also with the bombs that will be added to the scene. We need a way to trap these. We do these with the collider methods. We added platforms, we also added a player as a BANanoObject. To trap the collision, lets tell phaser that we need to trap these. We can also add a callback method to the collision. e.g. when a player collides we a star, collect the star or when a player collides with a bomb, kill the user etc etc.

To add collision, we pass the two variables we want to trap collision for. In this case, we are not running a callback for the collision between the player and the platforms.

B4X:
'Checks to see if the player overlaps with any of the stars, if he does call the collectStar function
    Scene.PhysicsAddCollider(player, platforms)
With this collision, the player will be placed on top of the platform and not at the bottom of the scene as depicted in the previous gif.

collisions.gif


As the grounds are static objects, they do not move during the collision.

Enjoying this??

;)
 

Mashiane

Expert
Licensed User
MOVING THE PLAYER AROUND...

Velocity - the speed of something in a given direction.

In the previous post we ensured that the player was placed in a platform so that our game is proper. Now we need to move the player left and right and the player should also jump. The player has been created and thus has a body. This body has gravity and velocity.

To trap the movement, we need to trap the keyboard keys, we call this method.

B4X:
'input events
    Scene.CreateCursorKeys
The movement of the player should happen during the execution of the game. This means when the FPS (frames per second is running), we need to detect the key presses and then act accordingly. We now move over to the onUpdate method of the game.

  • When the left key is presses, the player should run left.
  • When the right key is pressed, the player should run to the right
  • When the up key is pressed, the player should jump
  • If no key is being held, the player should stop.

Let's update our onUpdate method to reflect this code.

B4X:
'run the game loop code
    If Scene.LeftIsDown Then
        Scene.SetVelocityX(player, -160)
        Scene.PlayAnime(player, "left", True)
    else if gameScene.RightIsDown Then
        Scene.SetVelocityX(player, 160)
        Scene.PlayAnime(player, "right", True)
    Else
        Scene.SetVelocityX(player, 0)
        Scene.PlayAnime(player, "turn", Null)
    End If
    'jump
    If Scene.UpIsDown And Scene.IsBodyTouchingDown(player) Then
        Scene.SetVelocityY(player, -330)
    End If
Automatically when the user jumps, they will be drawn by gravity. You can change the gravity body values also.

readyplayerone.gif
 
Last edited:

Mashiane

Expert
Licensed User
TWINKLE, TWINKLE LITTLE STAR, HOW I WONDER WHAT YOU ARE...

The purpose of the game is to catch stars, so lets add a purpose for the game. To do this let's sprinkle some stars. We will add a group of stars to the game and then allow the player to collect them through, you guessed it, collision.

We want the stars to move and bounce and be spread on the game. We define a stars bananoobject and then call the method to add the stars. We do this in onCreate.

B4X:
'Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
    stars = Scene.PhysicsAddGroup("star", 11, 12, 0, 70, False)
What will happen if we run the code after adding this line. First let's remove the line where we added a single star to the game.

You guessed it right, the stars will be added to the game however due to no collision detection added, they will come into play and fall out of the bounds as depicted below.


Remember:

Things are added to the scene in the sequence you add them, so adding stars should be done after you have done everything else.

stars.gif


So to fix this problem, let's add code to ensure that the stars collide with the platforms. We also do this in onCreate.

B4X:
gameScene.PhysicsAddCollider(stars, platforms)
By setting the collision, we allow the stars to fall into the platforms. They are being drawn down to earth by gravity. At the end you have this game running..

fallingstars.gif
 

Mashiane

Expert
Licensed User
BOUNCE, BOUNCE, BOUNCE, BOUNCE, BOUNCE, BOUNCE, BOUNCE...


This is not as interesting now (above gif). Lets add some bounce to the stars. To do that we will use some iteration of the children in the stars group and change the bounceY value. This is done via a callback function.

The next piece of code iterates all children in the stars Group and gives them a random Y bounce value between 0.4 and 0.8. The bounce range is between 0, no bounce at all, and 1, a full bounce. Because the stars are all spawned at y 0 gravity is going to pull them down until they collide with the platforms or ground. The bounce value means they'll randomly bounce back up again until finally settling to rest.

B4X:
'Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
    stars = Scene.PhysicsAddGroup("star", 11, 12, 0, 70, False)
    'bounce the stars
    Dim child As BANanoObject
    Dim cbIterate As BANanoObject = BANano.CallBack(Me, "iterateChildren", Array(child))
    Scene.ChildrenIterate(stars, cbIterate)
B4X:
'create a bounce
Sub iterateChildren(child As BANanoObject)
    'Give each star a slightly different bounce
    Dim by As Double = game.FloatBetween(0.4, 0.8)
    Scene.SetBounceY(child, by)
End Sub
bounceit.gif


Scene.ChildrenIterate

When this happens for each child, the bounceY value of each child is changed via a callback. If we run the game now, as much as it will be doing most of what we need, you will realize we are not there yet. We need to collect the stars. As usual, we detect collision between the player and the stars group. However in this collision, when a star is overlapped, it needs to be 'collected'. That has to make it disappear on the screen.

TRY TO COLLECT STARS

trycollect.gif


Nothing happened on collection!!!
 

Mashiane

Expert
Licensed User
STAR CATCHER

When the player runs in the game, they have to collect the stars. To do this we need to trap an overlap between the player and the stars. This we define in create and it calls a callback method to collect the stars.

Add the player star collision.

B4X:
'when a player overlaps with a star, collect it
    Dim star As BANanoObject
    Dim plyr As BANanoObject
    Dim cbCollectStar As BANanoObject = BANano.CallBack(Me, "collectStar", Array(plyr, star))
    Scene.PhysicsAddOverlap(player, stars, cbCollectStar, Null, Scene.Physics)
Create an effect that the star is really collected.

B4X:
'quite simply the star has its physics body disabled and its parent game Object is made inactive and invisible
', which removes it from display.
Sub collectStar(playerObj As BANanoObject, starObj As BANanoObject)
    Scene.DisableBody(starObj, True, True)
End Sub
starcatcher.gif
 

Mashiane

Expert
Licensed User
SCORE-BOARD

It would be nice if there would be a score-board of how many stars are collected by the player isnt it? Yeah, lets add it.

We define a score variable and a score text variable. Each time a star is collected, we need to increment the score.

Let's add the score board in the onCreate method

B4X:
'add the score board
    scoreText = Scene.AddText(16, 16, "Score: 0", CreateMap("fontSize": "32px", "fill": "#000"))
Now, when the player collect stars, lets increment the scoreboard. For that we need to update our collectStar code a little to reflect the collected stars

Scoreboard.gif


The updated collect star code..

B4X:
'quite simply the star has its physics body disabled and its parent game Object is made inactive and invisible
', which removes it from display.
Sub collectStar(playerObj As BANanoObject, starObj As BANanoObject)
    Scene.DisableBody(starObj, True, True)
    '
    score = score + 10
    Scene.SetText(scoreText, $"Score: ${score}"$)
    
End Sub
 

Mashiane

Expert
Licensed User
Pre-Closing Summary

In less than 150 lines of code, we have demonstrated how you can create a game easily with BANano. If you managed to get everything working, congratulations. What did we do.

1. We created our Phaser skeleton game
2. We loaded resources to use in the game.
3. We then added the resources to our created scene
4. We added spritesheets and added animations
5. We trapped the keyboard events to move left, right, jump
6. We collected objects by detecting overlaps and placement through collision.
7. We created a scoreboard and updated this during game execution.

In this next section, we will add 'enemies'. This will be bombs that the player needs to avoid else they die.
 

Mashiane

Expert
Licensed User
BOMBS AWAY!

The issue with the bombs follow the same principle we followed with the stars

1. Add a group called bombs
2. Set the collision detection between bombs and platforms.
3. Add an overlap between a player and a bomb just like we did for the stars and have a callback method of what should happen when the player overlaps a bomb!

For this scenario we will add a single bomb that will bounce around the screen as soon as the game stars. Well that wont be challenging that much isnt it?

I have delected some scenes here, you get the drift isnt it?


baddies.gif


B4X:
'add a group for bombs
    bombs = Scene.PhysicsAddGroup1
    'add bomb
    AddBomb
The method AddBomb adds a single bomb just when the game starts away from the player. We have also updated the bomb hit to stop the game and the game becomes over when the player is hit.

B4X:
Sub AddBomb
    Dim x As Int
    'get the player position, ensure that the bombs displays away from the user
    Dim playerX As Int = Scene.GetX(player)
    'get a range we can add to
    If playerX < 400 Then
        x = game.Between(400, 800)
    Else
        x = game.Between(0, 400)
    End If
    'create the bomb
    Dim bomb As BANanoObject = Scene.Create(bombs, x, 16, "bomb")
    'velocity
    Dim vx As Int = game.Between(-200, 200)
    Scene.SetBounce(bomb, 1)
    Scene.SetCollideWorldBounds(bomb, True)
    Scene.SetVelocityXY(bomb, vx, 20)
    Scene.AllowGravity(bomb, False)
End Sub
Here we get the player position. We then get an x value (horizontal) between 0 and 400. We create a bomb away from the player. The speed of the bomb can be between -200 and 200 and it should bounce. We ensure that it does not go beyond the game area and its not affected by gravity.

There we go!

However we need to make our game more challenging...
 

Mashiane

Expert
Licensed User
MAKING THE GAME MORE CHALLENGING - MORE BADDIES AS YOU LEVEL UP

Instead of adding a bomb when the game stars, we will add a new bomb every time the player collects all the stars. This creates one thing. A challenging game. Your score increases as a 'new level' is created with more and more bombs!

We will update a few things to have this going.

1. Change the location of AddBomb to be inside collectStar.
2. Update collectStar so that it detects if there are no stars left, if there are no stars, create new stars and also add a new bomb.

Let's look at the updated collectStar methods

B4X:
'quite simply the star has its physics body disabled and its parent game Object is made inactive and invisible
', which removes it from display.
Sub collectStar(playerObj As BANanoObject, starObj As BANanoObject)
    Scene.DisableBody(starObj, True, True)
    '
    score = score + 10
    Scene.SetText(scoreText, $"Score: ${score}"$)
    'detect how many stars we have, if all are collected, create new stars and
    'add a new bomb
    'create a new batch of stars
    If Scene.CountActive(stars, True) = 0 Then
        Dim child As BANanoObject
        Dim cbIterate As BANanoObject = BANano.CallBack(Me, "newStars", Array(child))
        Scene.ChildrenIterate(stars, cbIterate)
        '
        AddBomb
    End If
End Sub
  
'child.enableBody(true, child.x, 0, true, true);
Sub newStars(child As BANanoObject)
    Dim x As Int = Scene.GetX(child)
    Scene.EnableBody(child, True, x, 0, True, True)
End Sub
1. When we collected the stars, we used .DisableBody, so we use the opposite of that, being .EnableBody when iterating new stars. Each time a star was collected, we disabled it and made it inactive, so we need a way to count how many stars are left each time we collect. For that we use .CountActive

You have noted that we are refering the stars group we created isnt it?

So now instead of having a bomb when the game stars, a new bomb will appear each time we finish collection stars in that 'level'.

That's all for now. Enjoy!
 
Last edited:

Mashiane

Expert
Licensed User
STARTING OFF WITH SOME LIVES

After playing the game for a while, you realize that the more bombs there are, your life expectancy decreases and you only have 1 chance to play. Why not start off with 3 lives and the more you finish a level, you get given another live? After all, in the end you wont win! :) Lol

The version of this game with lives and levels is in pgIndex. Just off-comment that code line and on-comment the rest in BANano_Ready.

Lives.gif


As you can see, the player stars off at LEVEL 1 with 3 lives. The more they collect stars and finish the level, the level is incremented by 1 and the lives are increment by 1.

The tricky part here is when the bomb hits the player. The player re-activates in another location and thus as a player you need to be fast and move before a bomb hits you because that decrements your lives and the game ends and restarts when lives are 0.

Study the code to see the logic for more details.

Based on the BreakOut game, you can extend the game to have a start and game over notification etc.

Ta!
 
Top