This walkthrough covers concepts related to creating an end-of-level entity which moves the player to the next level and creating a checkpoint which lets the player start at a midpoint in a level after dying.
The sample project can be downloaded from GitHub: https://github.com/vchelaru/FlatRedBall/tree/NetStandard/Samples/Platformer/CheckpointAndLevelEndDemo
This walkthrough refers to CheckpointAndEndLevelDemo as the demo and this demo.
This walkthrough covers a number of concepts for checkpoints and end of level:
Creating instances of Checkpoint and EndOfLevel entities in Tiled on object layers to allow different values on each instance
Storing the current checkpoint name in a static variable so it persists across Screen instances
Spawning and re-spawning the player at checkpoint locations
Collision between the Player and objects controlling spawning (Checkpoint, EndOfLevel, and PitCollision)
This demo includes two levels: Level1 and Level2. Each level has its own TMX file: Level1Map.tmx and Level2Map.TMX. If your project used the platformer plugin then it should have these by default. If your game already has existing levels, you can follow along but you will work in your existing levels rather than Level1 and Level2.
The checkpoints and doors will be added to a Tiled object layer. You must have at least one object layer on each level which should include a checkpoint. For this video we'll use the name GameplayObjectLayer so that it is similar to the standard GameplayLayer.
We will create two entities: Checkpoint and EndOfLevel.
To add a Checkpoint entity:
Right-click on the Entities folder
Select Add Entity
Enter the name Checkpoint
Check the AxisAlignedRectangle option
Click OK
Repeat the process above to create an EndOfLevel entity
Next we'll add a new variable to EndOfLevel:
Expand EndOfLevel
Right-click on Variables
Select Add variable
Select the type string
Enter the name NextLevel
Click OK
Next we need to associate a particular tile with the entity. By doing this, FlatRedBall automatically instantiates the entities whenever it encounters a tile.
To do this:
Open your level in Tiled. Make sure you do not have other levels open as to avoid mixing tilesets
Select the TiledIcons tileset and click on the edit button
Select the tile that you would like to use as a checkpoint, such as the checkered flag
Set the Class to Checkpoint - be sure to match the name of your entity exactly\
Save your tileset
Now you can add instances of the Checkpoint tile to your level in the GameplayObjectLayer. You should provide a descriptive name of the Checkpoint, such as LevelStart. Note that checkpoints can exist at the beginning of a level - this is where the player may spawn when going from one level to another.
To do this:
Select the Checkpoint tile
Select the GameplayObjectLayer
Clck the Add Tile icon to go into tile placement mode
Click on the map to add the Checkpoint tile
All checkpoints must have names so that they can be referenced in code. For this project we assume that every level has at least one checkpoint with the name LevelStart. You can set this name on the newly-created Checkpoint by selecting it and setting its name in Tiled:
Next we'll declare which tile in our tileset should create an EndOfLevel instance. To do this, open up the TiledIcons tileset in edit mode again. Select the icon that looks like a door. You may notice that it already has a Class set, so you can change it from "Door" to "EndOfLevel". As mentioned above, the name must match your entity exactly.
Next, we can add a new property to the tile. This should match the name of our variable in the FRB Editor exactly. To do this:
Select the tile
Click the + icon at the bottom of the properties
Keep the type as string
Enter a property name of NextLevel
Click OK
The variable should appear on the tile.
Repeat the process above to add an EndOfLevel instance to your GameplayObjectLayer.
Once this instance has been placed, its variable can be changed. For example, we can set the variable to Level2.
We want our player to spawn at a checkpoint. We'll create a static variable called LastCheckpointName which indicates the starting Checkpoint.
Initially when the game starts the LastCheckpointName should be set to LevelStart so that the checkpoint that was previously created is used:
The following code shows the logic that does this:
The spawning checkpoint is used to set the player's position. Notice that the player is moved down 8 units after spawning - this accounts for the position of the spawn point being the center of the object, while the player's position is at the bottom (at the player's feet).
Collision between the Player and various objects controls the spawning behavior. As mentioned earlier, the LastCheckpointName variable controls which checkpoint is used to position the Player instance. The CustomInitialize method is called whenever a level is created (or recreated).
We will use the EndOfLevel instances to move the player from one level to the next, and to set the LastCheckpointName.
To do this:
Create a collision relationship between the PlayerList and EndOfLevelList in GameScreen
Add an event to the newly-created collision relationship
Open GameScreen.Event.cs
Add the following code to move to the next level and set the LastCheckpointName:
Notice that the code above assumes that the EndOfLevel instance has a valid NextLevel value. If the EndOfLevel NextLevel property is not set to a valid screen then this code will throw an exception. The code above resets the LastCheckpointName to LevelStart so that the Player spawns at the beginning of the level.
We can set the LastCheckpointName whenever the player collides with a checkpoint - it doesn't have to be only when the player collides with a door. To do this:
Create a collision relationship between PlayerList and CheckpointList
Add an event to the newly-created collision relationship
Open GameScreen.Event.cs
Add the following code:
The OnPlayerVsEndOfLevelCollided resets the LastCheckpointName whenever colliding with a door, so this checkpoint will apply whenever the screen changes. The OnPlayerListVsCheckpointListCollided sets the LastCheckpointName to the name of the collided checkpoint, but this will only apply when the screen is restarted. Typically, this would happen when the player dies.
Player death can be handled in a variety of ways, such as by collision with a TileShapeCollection, or even with a hotkey to test death. Regardless, the way to restart the screen is by calling this.RestartScreen().
For example, if you have a TileShapeCollection named PitCollision, a collision relationship can be created between the PlayerList and PitCollision to restart the screen. This relationship would have an event with the following code in GameScreen.Event.cs:
This code restarts the screen, which results in the entire screen being completely destroyed and recreated. Since CustomInitialize runs again, the Player will be re-positioned according to the LastCheckpointName.
The demo includes two types of checkpoints:
Visible checkpoints - when the player collides with these checkpoints, the checkpoint changes appearance and sets the LastCheckpointName property.
Invisible checkpoints - are used only to spawn the player. In the case of the demo, only at the beginning of the level.
Whether a checkpoint is visible or not is controlled by an exposed Visible property.
Please note that if you are adding the checkpoints to your own custom project, to have the Visible property available you will need to set the ImplementsIVisible in Checkpoint Properties to true and then create a variable via the Expose an existing variable and select Visible. Also, since FlatRedBall purely converts the Tiled objects in the objects layer to instances of a FlatRedBall Entity with the same class, to actually see the flag and the door in your game you will need to add a Sprite object to the Checkpoint and EndOfLevel entities and set them to appropriate images or animation chain files. This subject is explained in detail in following tutorials.
Only Visible Checkpoint instances are considered in the Player vs Checkpoint relationship event.
The Checkpoint is visually composed of two Sprites:
PoleSprite
FlagSprite
By default, the FlagSprite is invisible, but is turned on in the MarkAsChecked function. This provides visual confirmation to the user that the checkpoint has been triggered.
This walkthrough showed how the demo project creates checkpoints which can be used to restart the player mid-level and end of level objects (doors) which can move to the next level.