This is my implentation of the tile generation system.
How it works
The tilemap consists of two chunks. Each chunk moves along the x-axis of the world. When a chunk moves off the screen, it is moved to the back of the other chunk, and is regenerated.
Each tile is represented by a scriptable object that contains a reference to the tile's prefab, as well as settings to determine the tiles behaviour in game.
The grid can be made larger or smaller as required.
NOTE: All of the coordinates in the scripts are in x and z, as this corresponds to the tile position in actual world space.
I also threw in a simple animation for the water tiles, just to see what it would look like. But this can be removed if it is not wanted.
To create a new tile:
Drag the DefaultTile prefab into the scene.
Add a model to the DefaultTile prefab.
In Assets/ScriptableObjects/Tiles, right click to create a new Tile scriptable object from the context menu.
Drag the DefaultTile prefab into the Tile scriptable object.
Set the settings for the tile in the Tile scriptable object.
In the inspector, add the Tile scriptable object to the TileManager's DifficultyProfile.
Optimisation
Currently tiles that are offscreen are being turned off by the HandleViewport method in the TileManager. This does improve performance.
A further optimisation could be to pool the tiles, so that they are not being destroyed and recreated every time the grid chunks are regenerated. This would require balancing the memory needs of the pool with the compute cost of instantiating and destroying the tiles.
Ideas for future development
When the water tiles are generated, a check could be made to see if there are adjacent water rows. If there are, bottom/middle/top water tiles could be used as well to create the effect of a larger body of water.
Currently there is no system in place to check if the player is trapped. Whether or not this really needs to be implented depends on the obstacle density, but if required its totally doable. I wasn't sure if the main difficulty mechanic was increasing speed, obstacle density or both.
Implementing other features using this system
Camera
Since I have decided to move the grid itself rather than the camera, the need for vertical camera movement has been removed. However, lateral camera movement could be added if it was decided to make the grid wider than the viewport.
Player Movement
To move the player, the player game object could be made a child of the tile it is currently on. Then the player's movement will be relative to the grid. When the player moves, it would only need to be moved a distance of one tile in the x or z direction, at which point it could be made a child of the new tile.
In regards to limiting the player to an area within the viewport, this can also be handled by the HandleViewport method in the TileManager. There is more info about this in the script itself.
Powerups
Positioning powerups could be done in a similar way to the player. The powerup could be made a child of the tile it is on, and then would move relative to the grid.
The powerups should be added in the GenerateTileGrid method in the TileGridChunk script. If the tile is checked to be passable and not a hiding place, then a powerup could be spawned on that tile.
New scripts in this PR
TileGrid/TileManager.cs: Manages the positioning of the grid chunks and maintains a logical representation of the overall grid. Also handles optimisation.
TileGrid/TileGridChunk.cs: Manages the generation of the each grid chunk.
TileGrid/TileData.cs: Scriptable object that contains the settings for each tile and a reference to its prefab.
TileGrid/TileConroller.cs: Controls the behaviour of each tile.
TileGrid/DifficultyProfile.cs: Defines structs for managing tile probabilities.
Debug/FPSCounter.cs: A simple script to display the current FPS in the game.
Effects/MaterialOffsetSineController.cs: A script to control the material offset of the water tiles.
Tile Generation
This is my implentation of the tile generation system.
How it works
The tilemap consists of two chunks. Each chunk moves along the x-axis of the world. When a chunk moves off the screen, it is moved to the back of the other chunk, and is regenerated.
Each tile is represented by a scriptable object that contains a reference to the tile's prefab, as well as settings to determine the tiles behaviour in game.
The grid can be made larger or smaller as required.
NOTE: All of the coordinates in the scripts are in x and z, as this corresponds to the tile position in actual world space.
I also threw in a simple animation for the water tiles, just to see what it would look like. But this can be removed if it is not wanted.
To create a new tile:
Optimisation
Currently tiles that are offscreen are being turned off by the HandleViewport method in the TileManager. This does improve performance.
A further optimisation could be to pool the tiles, so that they are not being destroyed and recreated every time the grid chunks are regenerated. This would require balancing the memory needs of the pool with the compute cost of instantiating and destroying the tiles.
Ideas for future development
When the water tiles are generated, a check could be made to see if there are adjacent water rows. If there are, bottom/middle/top water tiles could be used as well to create the effect of a larger body of water.
Currently there is no system in place to check if the player is trapped. Whether or not this really needs to be implented depends on the obstacle density, but if required its totally doable. I wasn't sure if the main difficulty mechanic was increasing speed, obstacle density or both.
Implementing other features using this system
Camera
Since I have decided to move the grid itself rather than the camera, the need for vertical camera movement has been removed. However, lateral camera movement could be added if it was decided to make the grid wider than the viewport.
Player Movement
To move the player, the player game object could be made a child of the tile it is currently on. Then the player's movement will be relative to the grid. When the player moves, it would only need to be moved a distance of one tile in the x or z direction, at which point it could be made a child of the new tile. In regards to limiting the player to an area within the viewport, this can also be handled by the HandleViewport method in the TileManager. There is more info about this in the script itself.
Powerups
Positioning powerups could be done in a similar way to the player. The powerup could be made a child of the tile it is on, and then would move relative to the grid. The powerups should be added in the GenerateTileGrid method in the TileGridChunk script. If the tile is checked to be passable and not a hiding place, then a powerup could be spawned on that tile.
New scripts in this PR