Boomer & Zoomer Release & Postmortem

Hello all, I’m very excited to announce today that I have finished up the production on Boomer & Zoomer, which is a 2 person endless runner that I worked on over the summer. I worked on the project as the sole engineer and primary designer, and worked alongside 2 fantastic teammates. A Composer, Zoe Morfas. and a 3D modeler & Designer Alan Karbachinsky. I was very lucky to work with them, and the game would not be where it is now if I hadn’t had these really talented people working with me. That said..

Boomer & Zoomer is a game which I played pretty much every single role on aside from 3D models/Animation and music. I’m not saying this to sound cocky… I hate the thought of that. I’m saying it because it is pertinent to the understanding of where the rest of this blog post will be going, which is a postmortem of my experience creating the 2 player endless runner that is Boomer & Zoomer. With all that said, I’m super proud to present you all with Boomer & Zoomer!

Download Boomer & Zoomer (PC – Xbox & Keyboard controls)

Note: Video does not contain in game sound!

I encourage you to at least watch the above game play video before continuing to read the rest of this post! And… Download the game too!

Boomer & Zoomer Postmortem

It’s time to cut the bullshit. Boomer & Zoomer will from hereon be referred to as “B&Z“. Additionally, Player 1 will refer to the dog player (Zoomer), and player 2 to the boomerang player(Boomer). Sound good?

Creating fun through mechanics is perhaps, in my eyes, the most crucial pinnacle in design. If what the player is doing isn’t fun, what’s the point? If you can make what the player is mechanically doing while they play the game always fun, then that game will always be fun. And then we can get art in there to make it look good too!

B&Z started as an attempt to test the limits of the endless runner genre. Endless runners are generally associated with mobile games, single player, directional mechanics only. B&Z is a game meant to be played with Xbox controllers, by 2 players, which includes a combat system. And this is how it was designed to be, back when the project started in June! As far as features which we had planned and didn’t implement, there were none! In fact, feel free to checkout the Game Design Doc here! That is perhaps the most impressive thing about this project. In the attempt to push how an endless runner functions, I tried to rely heavily on a set of stable mechanics. These mechanics were the player’s throwing & catching interaction, the player’s abilities (& token system), and the player’s distinct movement styles.

Given that I feel so strongly about the mechanics in games, I would like to use this postmortem as an opportunity to reflect on the core mechanics which I listed above, from the perspective of both the designer & engineer.

Throwing and Catching

Designer

The interaction is simple, but core to B&Z. To throw: Player 2 uses their left stick to aim the the indicator projected in front of the players. Player 1 then taps A to start the throw towards the aimed area. Time immediately screeches to a quarter of it’s former speed. That’s right, we got SLOW MO! The camera begins to pull out from the tight focus on just Zoomer, as Boomer has now entered the playing field. Player 2 is hurled forward, moving ahead of Player 1. Boomer is moving faster than Zoomer, but is equally in slow motion. Any enemy (water balloon) or rope in the path from Player 2 to the aim indicator is obliterated as sparks fly everywhere. Player 2 has just been thrown! And time resumes normal speed.

To catch: Player 2 must navigate within 1m range of Player 1. Player 2 must hold down A and have it held while Player 1 taps A. This will complete the process of Zoomer catching Boomer! Players hear a quick click sound accompanied by an extremely quick but powerful controller vibration to confirm they’ve made the connection. The camera quickly transitions into the previous “Zoomer Only” view, and Boomer regains control of their aim indicator.

As the designer of these mechanics, looking back I think that I could’ve used stronger visual representations of the unique catching mechanic. While it’s simple in idea, testing proved that a lot of players struggled with that coordination. While the player’s “action logs” tell the players when they’re in range of pulling off a catch, it doesn’t visually represent what the boomerang player must do vs what the dog player must do. It does say it in text. But images and animated control UI really helps players understand unique mechanics such as this, and I wish that I had put something like that in. Additionally, I wish I had added more feedback for the throw process. While I like the slow mo, I feel like something is still missing from the feeling of the mechanic.

Engineer

Throwing and catching were very fun to work out in code, as I got to work with state machines which heavily depended on one another’s state. I talk a lot about that here, but basically my main reflections on these mechanics as an engineers is that I wish I had finer tuned the sensitivity of Player 2’s aim. The game is meant to be played with an Xbox controller, and therefore a joystick, but as the developer I spent most of my time playing on the keyboard controls. I feel player 2’s aim is the mechanic that took the hardest hit from this crucial misstep in my development process. The aim runs on the typical joystick scale of (-1) – (1), but the aim function is unfortunately not properly scaled to the speed at which a joystick moves through these values, resulting in the aim being extremely sensitive and fast to snap around on aiming,

Player Abilities

Designer

Player abilities were a wildcard in the design process. I was unsure exactly how they would turn out, because I hadn’t fully thought them out until I sat down to make them. That said, the end result I’m really proud of. The abilities in this game depend upon ability tokens gathered while playing. I tried to first associate heavy feedback with collecting the ability tokens. I did this with crumbs flying everywhere when the player collects them, a satisfying click sound, & with a subtle vibration to the player who collected the token. This feedback is then meant to reach a big “payout” when player’s use their tokens. A description of all player abilities can be found in the above design doc, but for our purposes I’ll break down Zoomer’s Shock Attack. The shock attack sends a flurry of lightning across the ground and sparks up into the sky, as all nearby water balloons melt away. In addition to the visual feedback, the player receives a really nice ($3) shocking sound and controller vibration ringing so long as the ground is laced with lightning. Of all the mechanics in my game, I believe the ability mechanics to be the most fun simply because of the assortment of feedback and dynamics presented into the game through them. That said, nothing is without flaw.

If I were to have a second shot at making these mechanics I would better map out how they would interact with one another. I formed each ability independent of the others, and because of that none of them are specifically designed to interact with one another. Of course the dynamics generated by my abilities and how they do interact are really cool and fun! But the abilities were not designed with that specific intent/interaction in mind. I would like to see what abilities would have looked like if I had considered allowing them to interact / play off one another.

Engineer

The primary reflection I have on the engineering side of the ability mechanics is how I stored the particle systems & visual effects used by the ability tokens and abilities themselves. I wish that I had taken the time to build out a better system for instantiating and storing instances of these specific particle sysyems. The way it works right now, unfortunately, is each token collection instantiates a new particle system, but these are unpooled and remain assigned to the specific instance of that token. When I made this I knew it was not efficient, and I’ve written object poolers in the past, but I just got lazy on this one and I really do regret it.

Player Movement

Designer

One of the things I learned the most about in the production of B&Z is that about the importance & difficulty in pinpointing perfect player movement, From the design approach, I wanted the boomerang to feel free and fast. Meanwhile, the dog was to be easy to control and “medium” speed. One of the main design points of the game is that the boomerang player can move along the forward to back axis while the dog player is locked moving forward at a constant rate. This was the gameplay idea that started the entire project, and I’m still very happy with it.

Things I would do differently is a loaded question… mostly because the things I would do differently I wouldn’t be able to do myself, & therefore couldn’t have done for this project. But the main highlight would be stronger visual representation of the player’s movements. Both the boomerang and dog. For Zoomer, I would’ve liked to have an animation to better represent the player’s left and right movement. Right now, the dog just rotates and faces the direction they’re moving, but it doesn’t look great because the dog moves forward at a constant rate. On the reverse end, the boomerang is difficult to see. I added an indicator over the boomerang for when they are reaching questionable visual territory and it helps. But it doesn’t fix the issue that the boomerang itself is hard to see. My solution to this (If I had unlimited resources) would to be creating some sort of secondary camera overlay. Not even necessarily meant to be the main camera used by Boomer but more-so a supplementary angle from above their location, helping them identify their own surroundings. Then ideally taking that second camera output to dynamically work with the current player positions to display additional input for Player 2. However, that would be out of the scope of this already kind of out of scope plan, and would take a lot of dedication to get to work right.

Engineering

This game was made in Unity, and in Unity we use Rigidbodies to enact physics on our game object. One of the big turning points in the engineering of this game is when I decided to remove the rigidbody from my player game objects, thus removing them from the physics of the scene. Instead I moved the players using their transform, and directly feeding the player objects a new location each frame, for each player. Having control of the player’s location like that allowed me to build my own sort of constraints on player movement that I like to think turned out pretty well, especially in the case of the boomerang. The movement is meant to reflect the unpredictability of a boomerang… while also remaining fair and in the control of the player. It slows down and speeds up at random intervals and flies in small circular rotations within a larger space, taking up more room that its unmoving collider.

Conclusion

B&Z took a lot of effort and hard work, but I feel that it shows in the final product. No game is ever really finished, but in B&Z’s case we set out with specific intentions, clearly planned out scope & schedule, and defined personal goals, and I believe that is showcased in the final project, making this game complete to us.

My personal goals were to try and test myself creatively, to work on my ability to think up & build out fun mechanics, and to fit this all into a well thought out & efficient game architecture. While nothing is ever perfect, I am satisfied with effort toward achieving these goals. For a personal project over the summer, I’m very happy with the outcome.

I am so grateful to have worked with such a great team, who really brought this game to life artistically. Boomer & Zoomer can be downloaded above!

Summer Update: 2 Game Downloads Linked!

Hello All! As with the last few posts up here, I hope every one of you is well and staying safe amidst Covid-19. To say we are witnessing history is an understatement. We are actively participating in history, as Covid has proven to be something which permeates all facets of life, for all. That said, I have continued to use my newfound free time to work on the same 2 projects I posted about last time. That post, of course, included the download link to the “Liquidators” demo, a year long project which is planning for a release on steam in the coming month, as well as a break down of a personal project I am still currently finishing up with a friend!

This project, which we have taken to calling “Boomer and Zoomer” (It wasn’t my idea)(I know, I’m sorry), has served as a great experience for me, as I have handled all Engineering. Everything from the audio manager to the shaders in the game, I have been the one responsible! And I have really enjoyed it! I post here today not to share code, but instead the most up to date build!

A little about the game:

It is a 2 player endless runner

It requires an Xbox (one or 360) controller

In its current state it has no tutorial (I will be working on this week)

Controls can be found in the #readme

The game can be downloaded HERE!

I hope you guys are able to enjoy my personal project in it’s current state, and that you guys also give the Liquidators demo a play!

Please Excuse GIF Quality!
Again!

….Also excuse that this is me playing the part of 2 players…!

SODA and Finite State Machines

Hello All, I hope everyone is at least as well as the last time I posted on here, hopefully even better. I wanted to write here today to update on my current projects. I am happy to announce that Liquidators, a yearlong project I have been lucky enough to be the lead producer of, has released a playable and open demo, for free. I would love to have support from my site and receive feedback on this game! It is a survival horror game based on the real life events which followed the reactor meltdown at Chernobyl. The demo can be downloaded HERE.

I will of course keep my site in the loop about this game, as we plan for a steam release this summer, hopefully. However, this is a site dedicated to my personal code and projects so I will carry on to stuff that you will definitely find less interesting than a nuclear reactor survival horror game which is literally free right up there ^. You could play it for FREE but you’re still reading this? Fine, if you’ve made it this far lets talk about scriptable objects and how I’ve been using them in my Finite State Machines in a separate, yet still cool, side project.

A little about this “side” project:

Been in production for almost 3 weeks

I’m working as the only engineer, Game being built in Unity, Being built for gamepad controllers (using primitive input system (I know))

it’s a 2 player endless runner

Player 1(Dog) can choose when to throw player 2(Boomerang)

Boomerang player aims themselves before throw

Dog player only has control of left and right movement, constantly moving forward

Boomerang player can control left, right, and forward back, still constantly moving forward, just at adjusted rate

Players try to survive as long as possible

Each have abilities they can use to help each other

“Tokens” used when using ability

Cylinder is enemy only boomeang can kill, kills dog. Wall blockades kill both

Here’s a clip:

So here, you could imagine there are a few player states. Specifically, a state for each player when the boomerang is with the dog, for when the dog and boomerang are separate, and for when the boomerang dies and the dog persists. A Finite State Machine helps us out here because none of these states will be coexisting. So, each player will have their own instance of a StateMachine, which takes an iState, and they must communicate with one another, to ensure they are in proper states at all times, since one of their states being out of sync would inherently break the other’s state (since their controls are dependent upon one another).

If you have read my other posts here , or here, or here, or here or- okay you get it.. If you’ve seen those posts or the title of this one, you know that I love SODA. So given our state machine above, SODA fits into this very well. The first issue presented above is that our state machines must know the state of one another. SODA Events really help us with this here. When the dog throws the boomerang, it’s an event. The dog script doesn’t even communicate with the boomerang script directly. It simply invokes that event, and the GameEventListener on the Boomerang is quick to respond, switching to its “free” state, after a throwing thread, of course. This exists for all my state transitions here, except for the boomerang being caught by the Dog for that requires a synchronous tap of a button by both players. But you can see how that would ensure our State machine stay in our intended state.

The next BIG help from SODA in these FSMs is that my IntVariable type, the Scriptable Object int I’ve made, can be passed into my states, where they have free access to the value they need, and receive all live updates to that number via that reference. Confused? Imagine this: My dog player location is stored in my Vector3Variable Scriptable Object. In my state constructor, I take a type Vector3Variable _playerLocation. Now _playerLocation.value will be a reference to the exact spot in memory where my Player Location value is stored. Whether I just want to access it, or even adjust it, that value is live, and feeding into any other script that may need that live number. This is huge because normally I would have to make a reference to my player in every script that needs that number, and store it again in that script. Furthermore, by taking it in the state constructor, I don’t have to pester my player script to retrieve it every frame, because in the case of a value that is changed elsewhere, my state script will receive that update instantly through the magic of SODA!

I hope this makes sense, but in case it doesn’t, here’s some code:


//This is a state for player 1's movement, while the boomerang is in its backpack
//Here you have my declaration of the Variables I will need in this State
//Protecting these variables calmed down an empty value warning I was gettig
    protected Vector3Variable _dogLocation;
    
//Player speed can be changed at any time
    protected FloatVariable _playerSpeed;

//Player can move, or not
    protected BoolVariable _playerCanMove;

//Reference to Dog script
    protected DogPlayerMovement _player;

//Constructor
public ISDogRunning (Vector3Variable dogLocation, FloatVariable playerSpeed, BoolVariable playerCanMove, DogPlayerMovement player)
    {
//Simply taking the references I have, and setting equal to the Variable I need
        _dogLocation = dogLocation;
        _playerSpeed = playerSpeed;
        _playerCanMove = playerCanMove;
        _player = player;
    }

//.............................

//This is my state Tick, run every frame
public void OnStateTick ()
    {
        if (_playerCanMove.value)
        {
            //Moves player forwards
            _player.transform.Translate (Vector3.forward * Time.deltaTime * _playerSpeed);

            //Allows player to move LR
            _player.transform.Translate (Vector3.right * Time.deltaTime * Input.GetAxis ("P1Left Stick Horizontal") * horizontalMovementMod);

            //Throw boomerang when A pressed
            //Keyboard controls for debug
            if (Input.GetButtonDown ("P1A Button") || Input.GetKeyDown (KeyCode.E))
            {
                //Tells player to throw
                _player.BoomerangThrown (aimLocation);
            }

            //Reads for player using ability
            if (Input.GetButtonDown ("P1B Button") || Input.GetKeyDown (KeyCode.R))
            {
                _player.UseSelectedDogAbility ();
            }

            //Allows player to switch through abilities
            if (Input.GetButtonDown ("P1X Button") || Input.GetKeyDown (KeyCode.F))
            {
                _player.SwitchDogAbility ();
            }
        }

        //Gets player's aim
        //This state actually reads input from player 2 as well, as aiming component was initially part of dog
        //Axis is between -1 and 1, adding 1 and then dividing by 2 to get complete input
        aimLocation = Vector3.Lerp (_player.GetLeftAimLimit (), _player.GetRightAimLimit (), ((Input.GetAxis ("P2Left Stick Horizontal")) + 1) / 2);

        //Will rotate arrow assigned to plauer
        arrowGO.transform.LookAt (aimLocation);

        //Puts aim point at aim locations
        aimPointGO.transform.position = aimLocation;

        //Updates player's location
        _dogLocation.value = _player.transform.position;
    }