Escape from Krypton

gif of kryptonite game gameplay

This JavaScript based browser game is a reboot of one of my final capstone projects that I built for demo day at App Academy back in 2015. As you can imagine, it holds some sentimental value for me, but it's been over seven years since this project has been live for all the world to enjoy.

That's why I thought it was about time that I should dust it off, upgrade it, and give it a new place to call home. You can play it now by clicking the button below, or continue reading to learn more about the development process 😄

Table of Contents

Background

Inspired by the mobile game FlappyBird, I wrote the first version of this game in JavaScript / jQuery and used HTML5's Canvas to render it all in the browser.

I took it a step further than the original assignment of creating a JavaScript based browser game by building a Ruby / Rails LeaderboardAPI. I used this to track high scores and displayed the top 10 high score scores which were visi to all visitors of the site. It was a big hit on demo day, but my backend leaderboard API was quickly "hacked" and overrun with fake scores by my classmates who were all too eager to wield their newly obtained web developer skills; Something I improved this time around.

Game Design

I used HTML5 / Canvas for the rendering which was pretty fun to work with. It allows you to create your own sprites as an image source to paint onto the canvas. I actually drew my own Kryptonite in Photoshop for the obstacles:

kryptonite source images

The game randomly selects the height and calculates how much it needs for the top and bottom piece. It takes into account the Kryptonite.GAP constant and any Kryptonite.PADDING that I've allowed to make the hit boxes a bit more friendly, then it takes the appropriate slice from the source image and paints it onto the canvas. COOL, RIGHT?!

Collision Detection

During development I had the "flying object" (which I didn't know would be Superman at the time) represented as a circle, but clearly this did not work too well once I switched to using the Superman sprite:

superman with circle hit box
Hey, that's not a collision!

The white paths show the true boundaries used for determining collisions. Even with padding, you can see that the circle wasn't going to be very accurate anymore. So, I changed Superman's hit box shape to a triangle instead. After I defined the hit box boundaries I moved on to the fun part of defining how to detect collisions. The switch to a triangle hit box required some special handling.

sideCollision()

Here I detect when the right vertical edge of Superman's triangular hit box collides with the left edge of the Kryptonite. An extra qualifying condition to check is whether the top of Superman is above the lowest edge of the upper Kryptonite or the bottom of Superman is below the top edge of the lower Kryptonite.

gapCollision()

Here I detect when the top point or bottom edge of Superman collides with the top/bottom edges of the Kryptonite within the gap.

trigCollision()

Finally, I detect when the rear hypotenuse edge of Superman's hit box collides with the bottom edge of the top Kryptonite. Due to the shape of Superman with his cape, a circle or quadrilateral hit box would trigger a collision where visually it did not appear that one occurred. A trianglur hit box proved to allow more accurate collision detection.

This required a slight change in how the collision would be detected as Superman rises past this particular edge. Using an Isosceles Right Triangle made this slightly easier:

Simply put, while k is between points a and b, a collision is triggered if the length of kx is less than the length of ax. A nice and easy solution after I initially thought I would need to whip out some trig functions for this one.

Updates for 2022

To bring it into the modern era, I chose maximum pain by deciding to migrate it to TypeScript as a Next.js project using the t3 stack. With it I'll be leveraging tRPC/Prisma to replace the Ruby/Rails LeaderboardAPI for a completely fullstack Next.js application. New for 2022, I'll be adding more elements to the background of the game, and I'll also be adding SMS notifications for leaderboard rank updates.

As a predominantly backend engineer, this was also an excellent exercise in both TypeScript and React. I've used a bit of both at TravelPerk, but not with a very strong understanding of all the differences between useEffect, useState, and useContext. Luckily, I had to become acutely aware of the nuances of each as I React-ified my vanilla JavaScript game. A single unintended re-render or firing of a useEffect side-effect would cause the game to glitch out.

For context, a lot of the decisions I made regarding which language, framework, and/or tools to use were not because it was the easiest or fastest way to do it, but because I wanted to intentionally force myself to learn new things along the way. It's been slower and more painful this way, and sometimes a complete overengineering of the project, but it's been a great way to test myself and have fun while learning new skills. I mean, who doesn't want to play with all the new shiny things anyways?

Going Forward

As I resurrected this game I found myself finding all sorts of improvements I wanted to work on. Overall, most of the actual game mechanics will remain unchanged. I have to give myself credit for creating an actual playable game, but there are plenty of new features, nice-to-haves, and of course refactorings that would go a long way to keep iterating on it.

Coming Soon:

  • Leaderboard that is less hackable with server-side game validation
  • Mute sound toggle
  • Scrolling background art
  • SMS notifications
  • Game counter