Personal Project
A fish fight with tail blades.
Swim your fish forward; swing your tail blade from behind.
The project developed entirely alone, yet learned a lot.
The project was my attempt in creating a game alone, based on constraint of using only joystick movement. That constraint encouraged me to better focus on other aspects that resulted in the project. Also added in local multiplayer to understand the nuances of single versus multiplayer game sessions. I eventually moved on to other projects, but will always remember this as a valuable learning experience.
Attack enemies with your tail
Eat any small fry fish that swim away
Don't let enemies attack or else swim away as a small fry
As small fry, avoid enemy attacks until recovered
Survive and clear waves from eliminating your aquatic adversaries
Play alone or with your friends, with friendly fire option available
Opponent Controller Finite State Machine
This project's defining feature is the Opponent AI that play the same rules as the player. With the controls being only movement, that made defining behaviors easy. With the opponent having the tail blade like the player, its means of attacking is simply spinning around. To make the opponent engaging, a finite state machine (FSM) was implemented to allow a variety of behaviors, and can be tweaked through inheritance. The state machine can support four actions: Patrol, Seek, Attack, and Flee, with subclasses free to use all or few states as necessary, like a small fry only capable of fleeing or even the aggressive blue sea scythe constantly seeking and attacking nearby players with intense speed.
Opponent Fininte State Machine
A Star Pathfinding with screen wrap
The opponent AI also utilizes A Star Pathfinding to target any player on screen. Each path is made by computing waypoints from opponent as starting point to player as end point; the pathfinding updates in real time based on changing player position. If there are no solid obstacles, the opponent can directly head towards target. With solid obstacles, the opponent has to navigate around the level to reach to end points, and opens up strategies for players to exploit the opponent's movement patterns.
With the game taking place on a single screen with any entities capable of wrapping around the screen, it was necessary to modify pathfinding to allow entities to chase nearby players that are closer from performing s screen wrap. The amazing side effect of that change is that enemy movement feels more intuitive, unpredictable, and suspense for moment to moment gameplay.
Move Directly to Target
Follow Path
Compute target point distance with screen wrap
Wave Information and Select Screen
With the game being made as inspiration towards old school video games with short play sessions, the game plays in waves, with preset levels and enemy queue. There must be a level select screen that allows players to select waves that show the appropriate information, including the level screen itself, the three star rating system, and the enemy queue preview display. Those all require clever use of Unity's tools, including UI canvas and camera render.
For all loaded waves, they are placed on the bottom half of the screen, and is a layout grid of buttons. After the last row of buttons, the player uses the gamepad to navigate the page with a highlighted cursor. When the cursor selects a wave number, as a button, the level and enemy preview data loads. When the player changes page, a next set of waves are loaded, and the available waves are highlighted in white while the non-existent waves are greyed out.
For the level preview, I used an additional camera and the render view sprite to show the level that is viewed from another camera. In 2D games, the camera views are compressed together, so each camera has to be used cleverly to not overlap each other. For the level preview, the level data form the wave is placed far away from the menu screen so the additional camera can view the level. The camera render sprite, which stays at the menu screen, uses camera render shader for the other camera to view the level.
Both the three star rating and the enemy queue preview uses Unity Canvas system by aligning them in a layout grid. The three star system is a single row of three stars that are dark or light, depending on player's survival skills with three stars from taking no hits. The enemy queue preview is a rectangular grid of icon images that take enemy data from each wave, and shows the appropriate headshot. The order of enemies go from top left to bottom right, so the player knows what challenge to expect with the level being shown.
Load Wave from Unity UI Button Cursor (OnSelect)
Showing Wave Data
Loading Wave Data onto page
Wave Select Grid Page - Initialize and Load page
Level Editor
To make more content faster, I used the Unity Editor scripting to create a custom inspector that allows designers to easily add in any object to the scene, with the ability to toggle them on and off, and save layouts to reuse them for waves combined with enemy queue. Having a single scene with a collection of obstacles that can be tweaked to make multiple levels is more economical compared to making every level per scene, due to the single screen arena constraints.
A custom inspector is necessary since manually making levels results in wasted time, and want to have a tool to allow multiple variations of a level theme. To achieve the level editor, I made a list of objects that can be adjusted through changing the size of the list, accepting any game object, and a toggle to turn the objects on and off. With those elements, I added buttons to allow those actions to occur so making levels become easier and faster.
With the level editor made, saving variations is required since the objects all lie within a single scene and the level editor contains the list of objects that are visible by toggle. To allow only toggled objects to be saved, I implemented the binary string that scans through the objects stored under the level data parent in the scene, with visible being 1 and invisible being 0. By converting the list of objects' visibility into a binary string, they can be saved into a list of variations and loaded from picking the index value from the stored list of variants. The buttons to save and load level variants were implemented so making and tweaking levels can be done entirely on the inspector without having to write more code nor make excess amount of scenes.
Level Data - The inner workings
Level Data Editor - Inspector controls for editing level data
Level Design
To exercise my game design abilities, creating levels is fundamental, especially with my level editor implemented. Fintail Gladiator's single screen arena with emphasis on tail blade means that the levels need to have enough open space to have the player rotate freely to turn the blade or tail flip to attack at the opposite side. Level design also considers density of the entities on screen, even with up to 4 active players being together.
I set up some rules when making levels:
There must be at least 6 spawn points (clam shells)
4 players and at least 2 more to guarantee enemy spawning
any entity can prevent spawning by overlap, thus reducing viable spawn points
There must be space to wrap around the screen
Enemy AI can reach to targets with or without screen wrapping
Pathfinding is a big part to allow AI and players to navigate anywhere
There must be enough space in several areas to freely rotate player fish and flip to opposite side
I designed a variety of levels within such rules and learned from the following observation:
Open Area Levels
Easier to make
AI opponents can directly target players
Players are more vulnerable with more enemies on screen
Players can bait certain enemies that chase players, but requires alertness and timing with tail blade
Maze Levels
Challenging to make, especially with balance of allowing open space
The more narrow pathways, the more pathfinding opponents must use to reach targets
Certain enemy behaviors are less predictable with solod obstacles in the way
Enemy movement patterns can be exploited by players since some enemies cannot rotate fully or are too big in narrow paths to fully turn. Opponent fish combatants deliberately cannot tail flip while players can.
Play Testing
I conducted multiple play tests on Fintail Gladiator through attending physical play test events since they often have good crowd sizes, which the local multiplayer game benefits from. As I gathered feedback, I used several online tools to collect such feedback:
Google Forms and QR codes
Discord
Itch.io with web builds
Recording gameplay footage
With the itch.io builds, they are good for remote play testing, but can only see one computer user at the time. Most of those play tests are single player, which helps with some difficulty balancing, but I am really after multiplayer, and the game does not have netcode implemented. Therefore online multiplayer is limited to Parser that require effort for remote play, whereas the game was built around local multiplayer.
I made my own discord server as a central hub for obtaining feedback, yet require players to go through the itch.io page of the game project since I put the discord link there. However, the number of people who joined in my discord server was rather small, and seldom leave feedback. Also, I had to remember the details of the feedback people say when it comes to discovering bugs, suggestions for improvements, etc., since not everyone goes to discord to leave written feedback. Discord is great for garnering communities, but not for small scale projects that remain in prototype or for less dedicated audiences.
Google Forms with QR code scans has been the most successful method of getting player feedback when showing up at physical play test events, especially when printing out paper for QR codes. How that worked is that I attended the table with my Fintial Gladiator game, have people play, and inform them to scan the QR code after playing to give off feedback about my game. As for the Google Forms, I focused on general questionnaire such as solo or multiplayer, rating the gameplay controls, difficulty, feature requests, and the likes. From hearing from people and such, more want to have a separate versus mode since the main game is more of co-op game versus enemies with friendly fire thrown in there, which can cause confusion to those who don't know about friendly fire.
Making Art and Music from Scratch
While programming is my main strength, My attempts in art and music are decent enough for functional gameplay prototype.
For my art, I went for as few colors possible to focus on distinguishable forms. Even more so, I used a Unity Asset plugin called All in 1 Sprite Shaders, which has the feature of swapping the Red Green Blue colors for three other colors, utilizing a technique of color palettes to save time on generating quick prototype ready art. Even with the limited colors, having clearly identifiable visuals is important for the gameplay loop. The fish combatants must have tail blades that are easy to visually read, even with a small screen, with the fish having a round body, and the tail being sharp with shapes being more like a sword. Took some time to refine the graphics from being more like a tail, to having a sword like shape attachment to the tail, as nailing the look of a sword bladed tail was a challenging task in respect to maintaining the collision size of both the body and the tail. If I spent more time and effort getting the game beyond being a good vertical slice prototype, I would gladly get an artist to nail the look of fish with blade-handled tails.
For the music, I wanted an underwater ambient instrumental that focused on the percussion instrument with emphasis on fast and tense combat. With Garage Band on my iPhone, I picked out both the taiko drum, a Japanese drum instrument, and the bongo for complimentary beats with the taiko drum. Both the Koi and Taiko drum came from Japan as I liked some of those cultural aspects, and fits fine within Fintail Gladiator. I recorded the sessions to make a series of taps, and then edited the music beats to fine tune the timing to make short and reliable music loops. The same two instruments are heavily used for some one off sound effects. Whenever the player is in small fry form, I added in the maraca sound for the shaking rattle to indicate the player is temporarily little and vulnerable. I put great emphasis on making modular music events to make the game feel more interactive, such as normal size and small fry size, last enemy, etc. While I enjoyed making the music to fit in with the Fintail Gladiator, I would be better off selecting a musician to further expand the palette of musical instruments that fit with tense underwater combat while still adhering to the Taiko drum as primary music instrument.