I made this game using the Custom 2D Engine to showcase its usage and functionality. It is a downsized version of the 1986's Bubble Bobble game, only providing 3 stages. I had a lot of fun recreating such an iconic game.
The project focuses on handling the switching of scenes using the provided functionality and focuses on creating a flexible and structured codebase.
In a Finite State Machine each state is represented with a class. The Player- or EnemyComponent checks whether the current state returns another state.
Each state is provided with a OnExit and OnEnter method. Handy to e.g. (un)register certain inputs per state. (Alternatively, you could use the constructor/destructor)
I decided to let the state decide what animation should be played. I created a struct that saves certain sprite data. I let each state own an object of this sprite data to send that data over to the SpriteComponent.
I use Pushdown Automaton (stack-based state machine) for handling the scenes. The scenes are handled like states and get pushed on, popped from or set on the stack. This stack-based approach is especially handy for moments where you want to still render the previous scene in the background while processing the current scene.
E.g. pause-screen, dialogue pop-up, etc...
The scenes in the background become suspended and don't update anymore until you pop the current scene from the stack.
By design, an object can only receive one CollisionComponent. But with the provided CheckForCollision function, the user can specify different behaviour with each call.
For the player, I call the CheckForCollision function in the PlayerComponent to check if I get damaged. Additionally, I call it in a FloorCheckComponent and in a WallCheckComponent, with an additional offset, by which I can act accordingly and handle overlaps.
You can see the result on the left, one CollisionComponent gets handled in three different ways.
To showcase the flexibility of the engine, I made a Versus mode where player 2 plays as an Enemy.
Just by adding the movement component to the enemy object (like you would do for the player), the player is now able to control the enemy.
The MovementComponent creates the necessary input commands and provides public functions to (un)register those commands to the Input Manager.
This in combination with the state machines, makes it extremely easy to enable/disable certain inputs for certain states.