Boats ImplementationI thought it might be interesting to go a little more in-depth on the boats mechanic shown in the level I finished last week, and see how it works:
Boat entitiesFirstly we need some boats. These are basically just moving platforms and use the same setup as my other moving platforms do. I can draw the boats in the level using tiles, and then cut that set of tiles out of the level to turn it into a moving platform. (I call this a Tile Chunk in the code).
I placed the boat above the level here - it doesn't really matter where it goes as these tiles don't exist as part of the level.
The properties contain "Type = ChunkBoat". This makes the game create the Tile Chunk as a specific boat entity rather than a generic moving platform. This entity knows to move to the left at a consistent speed without having to be attached to a path, and handles other little bits like the wake effect at the back of the boat.
The "Duplicate = 4" property makes 4 copies of the boat, which is the maximum that can spawn in the level at any one time.
The other properties don't matter too much but I'll cover them for the curious. "Active = false" and "Content = true" ensure the boats entities start as inactive, and puts the boat entities into a special entity pool, so they won't be present when the level starts and won't appear until told when and where to spawn. "Tag = Boat" allows the boat spawner to find the boat entities.
Boat spawnerThis is an invisible entity that's responsible for spawning boats into the level.
It has a few properties so it knows how wide the boats are, how far apart they should be, the speed they move at, etc. The boats begin at the position of the boat spawner, and are placed at regular intervals to the right, and essentially go on for infinity.
If we know how long the level has been played for then we can calculate the position of any boat at that time. The X position of the spawner is 44, and boats move at -1.5 units a second, and there's a boat every 18 units. So:
- At 0 seconds, the first boat is at x = 44
- At 0 seconds, the second boat is 18 units further right, x = (44 + 18) = 62
- At 10 seconds, the first boat is at x = (44 - 1.5 * 10) = 29
- At 10 seconds, the second boat is 18 units further right, x = (29 + 18) = 47
Each frame the boat spawner despawns any boats that have moved far enough off the screen. Then, using the above calculations it decides whether any boats should be present in the on-screen portion of the level, and spawn a boat there if one is missing.
Deciding what's on each boatThe boat spawner entity has a property that tells it an XML file to load. This contains a set of boat layouts:
<BoatLayouts>
<Layout name="Empty" tag="Boat">
</Layout>
<Layout name="Drills" tag="Boat">
<Entity type="drill" x="2.0" y="-3.5"/>
<Entity type="drill" x="9.0" y="-4.5" left="false"/>
</Layout>
The "tag" refers to the same "Tag = Boat" property that's on the boat entity. So this determines which boat entity will be spawned.
Then there's a list of entities that will be spawned on the boat. At the start of the level the boat spawner creates its own pool of these entities to draw from, for example it makes a pool of 10 drill bots that are purely used for being spawned on boats.
When spawning a boat, how do we know which layout to use? This is based on where the boat is when it's spawned into the level.
These objects are placed along the bottom of the level which determine which boat layout will be used for boats spawned in that part of the level. So any boats spawned within that first range will use the "Empty" layout, with no enemies. Boats spawned within the second range will use the "Drills" layout. You can see this in action if you go back to the gif at the top of this post.
An interesting point here is that the individual boats don't have a consistent layout. For example if you move quickly and reach the second boat early then it'll have the "Drills" layout. If you take your time and the second boat is further to the left by the time you reach it, it'll have the "Empty" layout. Once a boat has gone off screen and despawned, if the player travels back to get it back on screen, it may contain a different layout of enemies as it spawns the boat from scratch and creates a layout appropriate to that part of the level.
I decided this inconsistency / lack of persistence in the boat contents was a good choice as it allows the boat contents to progress throughout the level regardless of how fast the player travels through the level. From a certain point in the level, the boats will contain drills, later on they will contain spinners, etc. If the player travels back through the level the boats back in the earlier parts of the level will still contain those appropriate contents.