Kronos Rush
This game is a bullet hell game based on the greek mythology. We used c# and monogame for this project. We also had to use some sql and design a database.
Since we weren’t able to make a webGL build with monogame I have uploaded a video of the gameplay below. There were quite a few mistakes left at the end of the project. To name the obvious: the cursor isn’t visible, some of the UI text is practically illegible, the background is very busy and the shield doesn’t clearly show what it does. Furthermore the game is way too difficult. Sadly the person who was responsible for these fixes had some personal things going on near the end of the project and we were never able to fix them.
In this project I created the different types of movement for the enemies using a state machine. I also made the invincibility ability, allowing the player to be invincible when the bar on the right is filled. Furthermore I made the spawning of enemies and the collision. Last, but not least I set up a connection to the database.
This project we worked with a team of 5 people including me. I had the most experience of everyone in the team so I spent a lot of time setting things up that were important and easy to work with for people who weren’t completely used to coding yet. I used code inspired by the state pattern to allow other people working on the project to easily add new enemy types.
This pattern allowed us to simply call an enemy’s ChangeBehaviour method to change it’s behaviour. As can be seen in the image below.
Next we’ll take a look at the Update method. Here we set newPosition which is a Vector2 that is used as the destination of the enemy. We also call the MoveToPosition method.
The MoveToPosition method handles moving the enemies towards the Vector2 newPosition. The character gets animated and there is a check to see if the enemy is already at the newPosition. After this we extract the position of the enemy from the newPosition to create a direction and we normalize this value. We then move the enemy towards the new position using Vector2.Lerp. Lastly we call the ClampPosition method which clamps the position of enemies to the screen.
The newPosition is set because in the Update method we call the currentBehaviour.NewPosition(). To make sure we always get a Vector2 I have made an interface that the behaviours use.
The image below shows an example of one of the behaviours added to the game. This behaviour will make the enemy move in a circle around the player and switch direction every few seconds. I had already made code to make an enemy rotate around the player with a boolean stating whether it should move clockwise or counterclockwise. In the behaviour below I have simply added a timer that changes whether the enemy should move clockwise or counterclockwise and I then return the result of the CircleObject behaviour.
Review
In hindsight I find quite a few mistakes in my code and decision making.
- Some of the names can be very confusing. the newPosition variable should probably be called targetPosition, or something similar.
- The state pattern is best used when an object often has to switch states which wasn’t the case in our project. Enemies usually got assigned a state when they were instantiated and never switched. So the state pattern was a bit of an overkill.
- As we had just learned about interfaces I was overly eager to use them. In this case an abstract class would’ve been better suited as used in the state pattern, because the behaviours are all the same type of object, a behaviour. They don’t just have the same capability.
- Last but not least, my approach was all wrong. I added all sorts of behaviours. Most haven’t been used in the game. I created a lot of possibility with the idea that you could create hundreds of different enemies with all sorts of unique movements, but I never consider whether it would ever come to that or whether we even wanted that to be part of the game.