We'll begin this tutorial with an empty project. Mine will be called BreakingBlocksProject.
The New Project Wizard can help us get a quick project set up. We will leave most of the options to their default, but we will change the following:
Change What kind of control will the player have? to Platformer
Uncheck Add Sprite to Player Entity unless you intend to add graphics to your Player entity. This tutorial will not cover how to add graphics to the Player entity.
Change Number of levels to create to 1. We will only have one level in this game
Now our project is ready to go and we can start adding our level.
This set of tutorials covers how to add breakable blocks to your platformer game. Breakable blocks have been popularized in the Super Mario Bros franchise, but are also common in other games such as Castlevania, Metroid, and Mega Man.
Despite being introduced in an early Nintendo Entertainment System (and arcade) game, the feature of being able to break blocks requires advanced FlatRedBall collision functionality. We will be covering the following FlatRedBall topics:
Creating entities in Tiled
Using TileShapeCollections to adjust Entity RepositionDirections
Dynamic destruction of entities
Combining manual collision calls with CollisionRelationships
Manually calling CollisionRelationship
Now that we have a simple platformer game with a Player moving around in a level with solid collision, we can add blocks to our game. We will be creating a new Block entity, instantiate blocks through our TileMap, and adjusting collision RepositionDirections.
Our Block entity will contain an AxisAlignedRectangle for collision and a Sprite for visual. Since the player will be able to walk on Block instances, we want to make sure the AxisAlignedRectangle size matches the tile size of our map (16x16). To create a Block entity:
Select the Quick Actions tab and click the Add Entity button
Enter the name Block
Check the AxisAlignedRectangle option
Check the Sprite option
Click OK
Select the newly-created AxisAlignedRectangle inside of Block
Click the Variables tab
Change the Width and Height to 16
FlatRedBall automatically creates a BlockList in our GameScreen, so we are finished creating our Block entity and can move on to instantiating it in our Tiled map.
To create Block instances in Tiled, we must decide which tile we want to use to represent the Block. When creating entities, we recommend using tiles in the standard tileset. This is the same tileset we used in the previous tutorial to add solid collision. To mark a tile as a Block tile:
Open Level1Map.tmx in Tiled
Select the TiledIcons tileset
Click the Edit button to make changes to the Tileset
Select the desired tile to mark as Block. I'll use the tile that looks like the solid collision with cracks
Change the Type to match the name of your Entity. In this case, it should be Block
Save the TSX file
Place blocks as desired in the GameplayLayer in Level1Map. Be sure to do this in the GameplayLayer, since mixing tilesets in a single layer will prevent your game from running.
Save the map file so the game can use the changes
Your game should now be creating Block instances for each Block tile placed in Level1Map.tmx. Since we haven't yet added graphics to the blocks, the blocks currently display only the white collision rectangles. Notice that each instance also has a small black square - this is the Sprite which we will be modifying next.
Our Block objects are currently displaying a black dot in the center of the collision rectangle for its graphics. This Sprite is not currently assigned a Texture yet. To fix this:
Expand the Block entity
Right-click on Files
Select Add File -> Existing File. We will be using the same file which is used for the graphics in our Level1Map.tmx - it has a graphic for a breakable block.
Search for FRBPlatformer.png and click OK
Drag+drop the FRBPlatformer file onto the SpriteInstance to set its Texture
Change the pixel coordinates to the following:
Left Texture Pixel = 0
Right Texture Pixel = 16
Top Texture Pixel = 144
Bottom Texture Pixel = 160
For more information on texture pixels, see the Sprite.Texture Coordinates page. Now our entity is using the breakable block graphic.
Currently our Player can jump through blocks rather than being able to hit and stand on them. We can fix this by creating a CollisionRelationship:
Expand the GameScreen Objects folder in Glue
Drag+drop the PlayerList onto the BlockList to create a new CollisionRelationship
Select the newly-created PlayerListVsBlockList and change the Collision Physics to Platformer Solid Collision
The Player can now collide with the blocks.
So far our game seems fairly functional, but it has a collision bug which can result in snagging - the unexpected stopping of the Player's velocity when moving across flat surfaces. This bug can be difficult to reproduce, so you may not have noticed it in the game yet, but it can definitely happen given our current setup. This problem is caused by the Block instances currently having AxisAlignedRectangles with RepositionDirections in all four directions. The following image provides a visualization of this problem:
The purple lines indicate possible RepositionDirections which can occur, and if these occur the Player will experience snagging. The topic of RepositionDirections is fairly extensive, and interested readers can see the following pages for more information:
We will be applying some of the concepts and code discussed in the tutorials above, but for the sake of keeping the tutorial shorter we will not take a deep dive into every topic here. To adjust the RepositionDirections of our Block instances, we will use a new TileShapeCollection which is created purely for this purpose. In other words, we'll make a new TileShapeCollection, but we won't create any collision relationships or fill it in Glue the way we normally do with other TileShapeCollections. First we'll create a TileShapeCollection to be used for adjusting the Block RepositionDirections:
Select the GameScreen in Glue
Select the Quick Actions tab and click Add Object to GameScreen
Select TileShapeCollection
Enter the name CombinedShapeCollection
Click OK
Now we can add the rectangles from our BlockList to the CombinedShapeCollection. To do this:
Open GameScreen.cs in Visual Studio
Modify CustomInitialize as shown in the following code snippet
Now our BlockList will have proper RepositionDirections. Remember the CombinedShapeCollection - we will be returning to this when we work on creating/destroying our Blocks in the next tutorial.
Our blocks are now fully functional as platforming collision. The next tutorial will add the ability to break the blocks when the player hits the block from below.
Now that we have a basic project (with a Player instance falling off the screen) we can create our map. Our map will have:
Solid collision to keep the player on screen
Graphics so our tutorial project looks like a real game
Tiles defining where to place our breakable Block instances
The Glue Wizard created a Map in our Level1 screen, which is useful if we were going to make our levels from scratch. Instead, we'll use a TMX file to speed things up. Download the following three files to the same folder (such as your downloads folder):
FRBPlatformer.png - the PNG containing the art for our game
FRBPlatformerTileset.tsx - the tileset for the visuals in our game
Level1Map.tmx - the map for our game which has the pre-made visuals
Our game already contains a file named Level1Map.tmx - this is the default name of the TMX added to our Level1 Screen. We can replace this file on disk with the downloaded file. We need to remember to also copy over the other two files. To do this:
In Glue, expand Level1 Files
Right-click on Level1Map.tmx
Select View in explorer to open the containing folder
Once open, drag+drop the three downloaded files into the Level1 content folder. If asked, replace the existing file.
Now our game will run and display the level, but our character still falls through the screen. We'll fix this next.
Our map already has visuals for a platformer game, but no tiles are marked as solid collision. We will add the standard tileset to our map and create a new layer which defines solid collision. To do this:
Double-click the new Level1Map.tmx - either in the file explorer or in Glue
Open the Content folder
Drag+drop the StandardTileset.tsx in the Content folder onto Tiled to access this tileset in Level1Map.tmx\
Add a new layer to the map called GameplayLayer\
Outline the solid collision areas in the level using the top-left brick tile to mark the SolidCollision tiles. Be sure to place these tiles on the GameplayLayer
Don't forget to save the TMX file after adding the collision. Since we used the StandardTileset.tmx file, our game automatically uses these tiles for the SolidCollision TileShapeCollection, and our player can walk around the level.
The GameplayLayer visibility can be toggled in Tiled. You may want this off at times to see the art of the game without the solid collision tiles blocking the visuals, or you may want it on to help diagnose issues.
Now that we have a functional level, we will create the Block entity.
Currently our game is fully playable as a platformer, but it is missing the feature we've been working towards on this whole tutorial - the removal of blocks on collision. Games which remove blocks based on collision are common. Some games, such as Mega Man and Metroid, remove blocks in response t being shot by a weapon. Super Mario Bros removes blocks in response to collision with the player. Specifically, if Mario has collected a power-up which makes him grow, then hitting a block from below destroys it.
This tutorial will implement this type of destruction since it will give us the opportunity to perform more advanced collision logic.
Before we add logic to destroy Blocks, we will make some minor adjustments to the game to make it easier to control, and easier to see our player. First, we'll adjust the collision on the player.
Expand the Player Objects folder
Select the AxisAlignedRectangle
Change the Width to 16
Change the Height to 24
We'll also adjust the movement of the player so it's a little easier to control.
Click on the Player entity
Select the Entity Input Movement tab
Change the variables in this tab to the following values
Ground
Max Speed = 130
Jump Speed = 270
Air
Max Speed = 130
Next we'll zoom in the camera to make it easier to see the player.
Click the Camera icon in Glue
Change Resolution to 360x240
Change Scale to 300%
Now our character is easier to control and see.
To help us understand the code that we will be writing, let's first look at block breaking implementation concepts. For the player to break the blocks, a number of things must happen:
The player must collide with the blocks. This may seem like an obvious requirement, but it's worth noting since it we will be writing code in the PlayerListVsBlockList collision relationship.
The player must hit the block from below.
Only one block can be destroyed at a time. Modern Super Mario Bros. games do support destroying multiple blocks at the same time, but the older games only support one at a time. We will follow the old approach.
Once a block is destroyed, the surrounding block RepositionDirections must be adjusted to prevent snagging.
Of the four concepts listed above, the one which requires the most attention is the third.
To understand why breaking one block at a time adds complexity to our implementation, let's consider a few possible collision scenarios. The first scenario is the simplest - where the player collides with a single block as shown in the following image:
The player is represented by a blue rectangle and the block is represented by a red rectangle. In this case, the two overlap, and the player is hitting the block from below so the block should break. Keep in mind that the player may not be exactly below the block. The block should break even if the player is slightly offset, as shown in the following image:
In the image above, the block would still break. Of course things get a little more complicated when we add multiple blocks. For example, consider the following image:
Which block should break in this situation? The player overlaps more of the block on the right than the block on the left, so breaking the block on the right seems best. Unfortunately, when we perform collision between entities and players, the block on the right may not ever collide with the player. CollisionRelationships perform collisions between one player/block pair at a time, and which pair is checked first depends on the order of blocks in the BlockList, which can change depending on partitioning. In other words, when using CollisionRelationships, we cannot depend on collision being performed in a particular order. With this in mind, it's possible that the left-most tile collides with the player first, which would result in the player being shifted down, as shown in the following image:
If the left block collides first and the player is pushed down as a result of the collision, the right block will not perform collision with the player. The events raised for collision will only be raised for the left block. As explained above, the block which actually collides with the player is arbitrary - it could be the left or it could be the right. This means that we cannot rely purely on the Player vs Block relationship to decide which block to destroy - at least, not if we plan on controlling which block is destroyed in such a situation.
We can solve this problem by adding a second collision object to the Player to help us decide which block to destroy. Glue supports objects with multiple collision objects. Consider the following image, where the Player object has two collision shapes - a blue rectangle for the body and a green rectangle for determining which rectangle to destroy.
Of course, it is possible that a block collision may occur even without the green rectangle colliding, as is the case in a single-block collision where the player is offset from the block.
This means that we can not rely completely on either the body (blue) or sub collision (green) to decide which block to destroy. Instead, we need to use both to decide which to destroy. The logic should be as follows:
If the player collides with a block from below, then the player will destroy a block.
First, check all blocks to see if any block collides with the sub collision (green rectangle). If so, destroy that block.
Otherwise, if the sub collision (green rectangle) does not collide with any blocks, destroy the block that collided with the player body (blue)
This means that the sub collision is an optional collision. It should only be performed if the player body collides with the blocks from below. Therefore, we will be performing this collision manually in code rather than creating a collision relationship.
As mentioned above, Glue supports objects with multiple collision shapes. To add a new shape to the Player:
Select the Player in Glue
Select the Quick Actions tab and click the Add Object to Player button
Select the AxisAlignedRectangle object type
Enter the name BlockCollision
Click OK
Select the newly-created BlockCollision
Set the following values:
Y = 12
Width = 1
Height = 5
Our player now has a small rectangle at the top. However, this rectangle is currently considered as part of the entire player. This is most evident when hitting blocks from below. Notice that the new sub-collision performs solid collision.
We don't want this rectangle to perform solid collision - it should only be used in our code to check which block to break. To fix this, we can mark the BlockRectangle as being excluded from the default list of shapes used in collision. To do this:
Select the BlockCollision object under Player
Click the Properties tab
Set the IncludeInICollidable to false
Now BlockCollision will still be part of the Player object but will not be considered in any CollisionRelationships (by default).
Now that we have a BlockCollision rectangle, we can perform the logic explained above. We'll treat each of the four steps separately.
We can handle Player vs Block collision by adding an event to this collision relationship:
Expand GameScreen Objects folder in Glue
Expand the Collision Relationships item
Select PlayerListVsBlockList
Select the Collision tab
Click the Add Event button
Click OK to accept the defaults
Glue has now added an event to the GameScreen.Events.cs file which is called whenever the Player collides with a Block.
The OnPlayerListVsBlockListCollisionOccurred method is called whenever the player collides with a block from any side. This means that if the player is standing on a block, this function will be called every frame. We only want to destroy blocks if the player hits a block from below. Whenever a solid collision occurs, the objects being collided must be separated to prevent overlap. Since blocks cannot be moved when a collision occurs, the player must be moved. When hitting a block from below, the player must be moved downward (negative Y). We can check the player's AxisAlignedRectangleInstance.LastMoveCollisionReposition.Y value to see if the player was moved down due to the collision. If so, then the player hit the Block from below. Modify the OnPlayerListVsBlockListCollisionOccurred method as shown in the following code snippet:
Now we can perform our destroy logic. As mentioned above, we will first check if the BlockCollision collides with any blocks. If so, we will destroy the block. Otherwise we will destroy the block that the body collided with. To do this, modify the OnPlayerListVsBlockListCollisionOccurred method as shown in the following code snippet:
Now if we run the game, the Player can only destroy one Block at a time. If the Player collides with multiple Blocks, then the one which is directly above the Player will be destroyed. Of course, we there are some collision problems caused by the RepositionDirections not being adjusted properly. This is evident when trying to destroy the top row of blocks.
The RepositionDirections are initially set when all of the Blocks are created, and will work properly until one of the Blocks is removed. Once a Block is removed, surrounding Blocks must have their RepositionDirections updated. To do this, we will remove the Block's collision from the CombinedShapeCollection before destroying the Block. Doing so will result in all adjacent collisions updating their RepositionDirections. To do this, modify the OnPlayerListVsBlockListCollisionOccurred method as shown in the following code snippet:
Notice that the rectangle is removed before the Block is destroyed. Also, notice that the RemoveRectangle method is called in two spots since we destroy blocks in two spots in the code above. Now the RepositionDirections are updated properly and the player will be able to collide with the blocks properly even after blocks are destroyed.
If you've made it this far, congratulations! You have worked through a complex collision example in FlatRedBall. Now you should be able to add blocks freely in Tiled and the game will collide properly.
Return to Glue and click the Folder icon to open the project folder