The "QBertLevelGenerator" is a procedural generation algorithm designed to create Q*Bert-inspired levels. These levels follow a pyramid-like grid structure where each block can contain items and obstacles or it could be the spawn point for the player or the goal.
The challenge for the procedural engine is ensuring a solvable path from the player's start to the goal (a black sphere) while keeping generated levels diverse and modifiable. So instead of trying to optimise random block placement such that critical paths exist, the challenge is solved by laying out the critical path first and then adding random blocks. This approach guarantees level solvability and with a couple of control variables also makes levels customisable.
In a Q*Bert-inspired game, levels are organized in a staggered, triangular grid, where each block connects diagonally with neighboring blocks. This distinct layout sets up a playable area with strategic pathways that guide player movement.
![Insert Screenshot 1: An example of the empty QBert grid layout to introduce the structure.]
The generation process is managed by three main scripts: BlockGridCreator.cs, Block.cs, and ProceduralGenerator.cs. Each plays a specific role in constructing and populating the level.
The BlockGridCreator.cs
script is responsible for creating the level’s grid structure. Using adjustable grid dimensions (gridSizeX
and gridSizeY
) and block spacing (blockWidth
and blockHypotenuse
), it arranges blocks in a staggered layout that mimics a pyramid. This layout forms the basis of the Q*Bert-style gameplay space.
- Parameters:
gridSizeX
andgridSizeY
: Define the grid’s width and height.blockWidth
andblockHypotenuse
: Control the spacing between blocks, creating the staggered layout.
Process:
- Each block is placed in rows with staggered columns to form the pyramid layout.
- Jump vectors (
LeftJumpVector
andRightJumpVector
) are calculated, guiding player movement directions.
![Insert Screenshot 2: The grid created by BlockGridCreator.cs
, showing how blocks are positioned in a pyramid structure.]*
Each block within the grid is defined by Block.cs
, which assigns properties such as state (empty or filled) and potential contents (gems, spheres). Blocks know their own position (row
and col
), any items they hold, and connections to neighbors, allowing for flexible gameplay interactions.
- Key Properties:
- row and col: Position in the grid.
- isEmpty: Indicates whether the block is empty or filled.
- hasBlackGem, hasRedGem, etc.: Tracks which items are present on the block.
Process:
- Each block’s state and contents are dynamically managed.
- Visual effects and animations are applied as necessary, enabling elements like gem animations.
![Insert Screenshot 3: Example of blocks with various properties set, showing items like gems or spheres on different blocks.]*
The ProceduralGenerator.cs
script is the core of the level’s procedural generation. Its design philosophy emphasizes ensuring solvability by laying out a critical path before filling the remaining blocks with randomized elements. This reverse approach—where the critical path is established first—was chosen to avoid the complexity of validating randomly generated layouts for solvability, a step that would have been outside the project’s scope.
- Starting with the Goal in Mind: The algorithm begins by selecting a starting block and then creates a path towards the black sphere (the goal). This ensures that a solvable route is always in place.
- Path Construction: Using the
walkerSteps
parameter, a path is generated step-by-step from the starting block to the goal, ensuring continuous movement through adjacent blocks. - Populating the Remaining Grid: After laying out the critical path, additional blocks are filled based on the
fillMapNumber
parameter, adding complexity without interfering with the critical path.
- Key Variables:
- walkerSteps: Defines the length of the critical path.
- fillMapNumber: Controls the number of additional filled blocks after the path is set.
- Item Prefabs (e.g.,
blackGemPrefab
,redGem
,redSpherePrefab
): Prefabs for items that populate the grid, adding variation to each level.
Process:
- Critical Path Creation: The algorithm lays out the critical path first, ensuring the player can always reach the goal.
- Random Block Filling: Additional blocks are filled randomly, adding challenge and interest without obstructing the main path.
- Item Placement: Items are added to various blocks, creating gameplay elements and enhancing player interaction.
![Insert Screenshot 4: The grid after generating the critical path, highlighting the direct path from the start to the goal.]*
![Insert Screenshot 5: The fully populated grid with additional blocks and items, demonstrating the final level structure.]*
Below are examples of levels generated with different values for walkerSteps
and fillMapNumber
, showing the flexibility and scalability of the algorithm.
-
Short Path, Sparse Grid
- Parameters:
walkerSteps = 3
,fillMapNumber = 2
- ![Insert Screenshot 6: A simple level with a short critical path and minimal additional blocks, suitable for beginners.]*
- Parameters:
-
Long Path, Dense Grid
- Parameters:
walkerSteps = 10
,fillMapNumber = 15
- ![Insert Screenshot 7: A complex level with a long critical path and many additional blocks, offering a challenging experience.]*
- Parameters:
- Guaranteed Solvability: By laying out the critical path first, the algorithm ensures that each level is playable without requiring additional validation checks.
- Controlled Difficulty: Parameters like
walkerSteps
andfillMapNumber
allow for fine-tuning the level’s complexity and challenge. - Enhanced Replayability: Randomized elements, such as block filling and item placement, ensure each level remains fresh and engaging.
To further improve the generation process, the following features could be considered:
- Dynamic Difficulty Adjustment: Adjusting
walkerSteps
andfillMapNumber
based on player performance. - Branching Paths: Adding optional paths for exploration beyond the critical path.
- Themed Blocks: Introducing different block types and visual themes as levels progress.
By focusing on a critical path-first approach, the "QBertLevelGenerator" achieves a balanced solution between uniqueness and playability, exemplifying a robust design for procedural level generation.