Shell Shot
Design Process
Idea
Game Overview
Player is an oyster with the ability to rotate left or right and thrust forward. I wanted the player to feel like they were underwater and could only move the direction they faced, adding to the learning difficulty curve. They can also launch pearls at crabs to earn points. I wanted the pearls to feel precious to the player, so they only have 1 at a time on cooldown. They must avoid and destroy crabs while earning upgrades to earn the highest score possible.
* Created in 4 days for game jam themed "Shell". The music was created and played by Jarrett Holbrook.
In response to my general feedback from the jam: I adjusted the linear damping and speed variable of the oyster to allow for a more controlled movement feeling. I reduced the exponential scaling factor of the required XP to make allow for a more balanced leveling feel.
Originally, I only read the value of the players input for movement. After a few testers I realized that they had no need to thrust forward and just rotated to shoot. To alleviate that, I add a clamp on fixed update to ensure the player was always moving slightly forward. This created more drift and the need to move.
To further improve this, I changed the crabs to have a random speed variable on start to avoid pattern shooting boredom.
Game Mechanics and Implementation
Player Movement
Player can only rotate left or right and thrust forward. The player must manipulate their angle and momentum to navigate. The linear damping of the rigid body 2D was increased to allow for a drifty effect when releasing the thrust input.
I added player move input and a function to read input value and create a Vector2.
Rotation: On fixed update I read the -x value of the current input and multiply it by my rotation rate float and Time.fixedDeltaTime to get my current turn amount float. This amount is then added directly to the current rotation of the player using RigidBody.MoveRotation.
Thrust: The moveInput.y is used to create the thrust variable. *Alongside the Mathf.Max(.1f, moveInput.y) function to make the player gradually move forward constantly. A force amount is created by multiplying the gameObjects.transform.up, oysterSpeed and thrust. This force amount is then added directly to the rigigdbody2D of the player using AddForce and ForceMode2D.
*This was an adjustment made after a few of my first testers remained still and rotated for their entire playthrough. I forced them to have to move by doing this and added to the underwater feel.
Pearl Shooting
A game object with a rigidbody2D and sprite was created for the pearl and turned into a prefab. Shoot input was added and enabled and a function was added to respond to the input. An image set to fill was added to the active UI to represent the cooldown of the pearl. Once the player uses the ability it set the canShoot Boolean to false and start the recharge process.
In script: To smoothly fill both the pearl value and the UI I used a Coroutine, IEnumerator and lerp function. I had a pearlCooldown float set to start at 4. And at the beginning of the coroutine I create and set a variable for elapsed time to 0. While the elapsed time is less than the pearlCooldown, elapsed time += Time.deltaTime. The fill amount = Mathf.Lerp(0f, 1f, elapsed time/ pearlCooldown). Once the elapsed time equals the cooldown, the coroutine is yielded and the canShoot variable is set to true.
To further signal the player that the pearl is ready, I created a “ting” sound effect to play when ready. When they try to shoot on cooldown, an empty air noise is made as well as a puff of sand animation.
Crabs and Crab Spawner
Crabs are game objects with a 2D collision box meant to detect the player and pearls. They remain still and non-threating for 2 seconds after spawning, then pursue the player. If they come into contact with the player, they will stop moving and their walking animation. After 3 seconds they will resume pursuit and animating.
In script: Using void fixedupdate and a shouldFollow Boolean, I manipulate the crab’s location, using Vector2.MoveTowards the player. The function uses the crabs speed variable and multiplies it by Time.fixedDeltaTime.
The crab spawner instantiates the crab Prefab on an Invoked function. The function is called at begin play and each time it is called the time between (15) is multiplied by .9875 and clamped at a minimum of 3, resulting in about 18 minutes of gameplay before max crab spawn is reached. They still have a 1 second spawn time between at this point. There were 4 spawners placed various distances away from the starting point. This exponential growth forces the player to learn controls and buy upgrades quickly to compete with he crabs.
Score System
The player has a current score variable that is reset to 0 at the end of every game. Text to represent this value is centered and at the top of the screen. There is also a high score variable that can be viewed from the main menu, pause screen or death screen. The score is increased by destroying crabs and collecting shells.
In script: The function takes the current score and increases by an incoming integer amount; it then checks to see if the current score is great than the highest score variable. If it is higher, it then sets the highest score variable correctly and set player prefs to save the amount within the file. There is also a delete or reset score button on the high score screen.
Shells “Lives” (Lose Condition)
The player is given 3 lives represent by shells that match the players. They will lose a life every time they collide with a crab. The player will become invulnerable for a few seconds and blink. If the player has none of the 3 shells when they get attacked the game is over and the score is stopped. There is a “shell” collectable to restore one life that has a *7% drop rate from the crabs. This collectable stays for 15 seconds and then begins to blink for about 7.5 seconds before disappearing. This prevents players from leaving these behind and collecting them later or cluttering the game space. To prevent them from being useless to a player when lives are full, they also increase the current score.
*This rate drops by 1% after 100 seconds and then another 1% every 90 seconds until clamping at 2%. This drastically reduces the lives dropped by the crabs when defeating hordes late game- preventing players from just sailing through crabs expecting multiple lives to spawn.
In script: The active screen canvas has three shell images added to it and each one it public reference for the player script. The player script has a life variable of 3 and a function to check the amount when losing or adding a life. The “add life” function checks for less than full lives to trigger and increments the life value by 1. The “remove life” function checks for greater than 0 lives and decreases the int by 1. This function properly displays the amount of shell images and will load the game over UI if no chances are left.
Score System
The player is given 3 lives represent by shells that match the players. They will lose a life every time they collide with a crab. The player will become invulnerable for a few seconds and blink. If the player has none of the 3 shells when they get attacked the game is over and the score is stopped. There is a “shell” collectable to restore one life that has a *7% drop rate from the crabs. This collectable stays for 15 seconds and then begins to blink for about 7.5 seconds before disappearing. This prevents players from leaving these behind and collecting them later or cluttering the game space. To prevent them from being useless to a player when lives are full, they also increase the current score.
Level Up and Upgrade System
The player has a visible experience bar that is filled by destroying crabs. Once the player levels up, they get to select an upgrade. The upgrade system is dynamic in that it reads the value of the players’ lives before opening. If the player is missing a life, the first option will be a shell. If they are full, a speed upgrade will replace the shell option. The other 2 options remain the same: decrease pearl cooldown and kill more crabs.
In script: A function for every upgrade was created. The shell button would simply call the same reward life function of the collectable; it will however not reward any score this way. The speed upgrade takes the current speed variable and multiplies it by 1.25 and the linear damping by 1.05. This seemed to be the steadiest increase of speed and control. The pearl cooldown is multiplied by .99 to reduce by a percent so that it may never reach 0 but steadily grow closer. The pearl has a variable for number of crabs to hit before breaking and this is incremented for the level up.