Switch Hitter Development Log

Behold as I pretend that I am a game developer.

Sprint #8: "Christmas in July."

July 29th, 2018

I dubbed this sprint "Christmas in July" in reference to me working on a handful of things that had been on my wishlist for some time (and, you know, it's July). An inconsistent working schedule motivated me to just extend the arbitrarily defined sprint length from one week to two (which could very well be the case moving forward). Just to get it out there, I didn't end up very happy with the amount of work I was able to get done but there are still a few things worth talking about and showing off.

Recoil

One of the things that's bothered me about Ensue up to this point is that, when you throw or hit an enemy, they just come to a dead stop against a wall. To someone influenced heavily by Yoshi's Island, where eggs carom off surfaces at every which angle, it's a very unexpected and, frankly, boring look.

Adding a recoil/rebound/bounce/whatever effect was something I'd long had in my mind's eye for this game, so I finally took a little time to implement it. This is what the same thing looks like now.

As is common in programming, a pretty simple implementation gets you a long way. In my case, all I really wanted to do was negate an enemy's x-velocity whenever it would otherwise be stopped by a wall. Specifically, if an enemy was traveling with an x-velocity of 4 when it hit a wall, I wanted to instantly flip that to be -4 instead, so the physics engine when then send the enemy in the opposite direction on the x-axis.

As is also common in programming, that initial simple implementation is rarely good enough to satisfy you. In tweaking little magic numbers, I decided that multiplying x-velocity by -0.8 (instead of -1) ended up feeling a little better, especially when combined with, strangely, adding just a little bit of upward y-velocity (in my implementation, this actually means leaving y-velocity alone and, instead, setting y-acceleration to -5). By no means do I think this is the final set of magic numbers associated with recoil but it's good enough for now. It proves out the ability to make things like this.

This addition is one of those touches I've talked about that just makes Ensue's universe feel so much more dynamic and alive to me. It opens up a lot of new possibilities going forward.

Switchable spikes

Another thing I've wanted for a while is the ability to retract spikes based on a game action (instead of on a set timer). In the current Ensue world, this means allowing them to subscribe to switches. Implementation-wise, there's not much to talk about here--they do exactly the same thing that the platforms discussed in Sprint #7 do--but it's still fun to see in action.

Obviously, the main thing that switchable spikes afford me here, as a level designer, are the ability to really require the player to use the projectile system, whether that means the glove or the bat. I'm not sure how many spike switches I really want to use but, like everything else, it's a fun toy to have in my toy box, especially when you combine it with the scripted motions of enemies, creating ever my dynamicism in the world.

Build automation

Since moving to something that smells vaguely like a build system, I've introduced a new step into the Ensue development process: I have to, you know, actually run the build command when I want to, for instance, build the game and see a change I've made. It's not like the worst thing in the world to have to type npm run build every time I want that process to run but it gets old pretty quickly. A better solution, especially living in the Node.js ecosystem like I am, is to rig up an automated builder.

The way this works is that you tell a process to watch a certain set of files and execute a particular command whenever the contents of any one of them changes. In Ensue's case, I watch all of my JavaScript files that contain the actual game code, all of my JSON files that contain level data, and all of my PNG files that contain sprites. Any time any one of those files is changed, the process automatically calls npm run build for me.

This type of thing is very, very commonly needed in software development. In the realm of the web, it's most often used for things like compiling Sass files to browser-usable CSS files. It's also used extensively by React, which invents its very own extended syntax for JavaScript that, of course, must be compiled down to something that a browser actually knows how to deal with. It's so common that there are several utilities out there designed specifically to solve this problem. In Node-land, the two most prominent are gulp and Grunt.

I've used both of them professionally but have never actually been in charge of setting them up; I've only ever had to type gulp to launch the watcher process that had already been set up for me. As such, it was an interesting exercise to look into the differences between them and try to find the right tool for my job. Unfortunately, it ended up being neither.

You see, both gulp and Grunt really want to manage your entire build process. They both natively implement or have plugins for the basic features you're likely to want from a build system: concatenation, minification, translation, etc. This is fine and well but my reality is that I already have a functioning build system and really don't need to iterate on it yet. All I'm interested in is a thing that I can configure to watch a certain set of files and then run an existing command. (To be clear, I'm not saying that gulp and Grunt can't do this, just that it quickly became obvious that they wanted me to solve my problem in a much more gulpier or Gruntier way.)

Enter nodemon, a much simpler tool. It sounds like nodemon was intended to solve the annoyance of having to restart your Node application any time you made a change to it (which is something I have to do with this very website, as well as Storybook). It can just as easily be used to accomplish my build task, though. Indeed, getting nodemon set up was as simple as installing it globally with...

sudo npm install -g nodemon

...and adding a line to package.json to define a watch script that ran...

nodemon -e js,json,png,pug --watch src --watch assets/images/export --watch assets/levels -x "npm run build"

Compared to what gulp and Grunt seemed to be asking me to do, this was far easier and I've been very satisfied with it.

Closing thoughts

I glossed over a lot of self-doubt and feeling lost and wondering if I even really know how to program things at all that I experienced these past couple of weeks. I'm unfortunately in the dreaded software development cycle of just knowing that I need to rewrite or refactor a huge chunk of my code. The thing is that I don't know the best way to do that, or even necessarily what I want from it other than that I want it to be "better". I end up just skimming Game Programming Patterns and Design Patterns hoping I'll find some magic bullet of inspiration. It's not great, to tell you the truth.

Next sprint

But, having said that, it still feels like continuing to indulge the refactorization path is what's going to improve my mood about the game the most. I freely admit that Ensue would almost surely be better off if I were to spend the next sprint making levels that continue to show off the game's possibilities but, for now, refactoring is what I'm more interested in (and it's not like it's a total waste of time). I'm going to spend the next two weeks improving existing systems and flows so that, hopefully, iteration is easier going forward. I think it sounds great to not have much to show everybody in two weeks but plenty to write about.