Sunday was the final day of the inaugural Training Jam event. A not-quite-two-day game-creating jam hosted by OpenSesame, a curator/marketplace for electronic learning solutions. It took place at DeskHub, an office-location-for-rent that serves small businesses and startups with a place to share, and stake out some desks for your team, with conference rooms and teleconferencing equipment, and a decent break area with plenty of supplies for coffee, food, etc. The staff from OpenSesame kept us fed with excellent catered meals, and lots of soda, water bottles, and energy drinks. They were also extremely helpful and encouraging to the jammers. The overall energy was very positive and uplifting.
The challenge was to use the time to create games that teach—training that’s “not training”. Not simply “gamified” training solutions, but games that are fun to play, and while you’re playing you just happen to come out of it with solid, usable knowledge. Check-in was Friday at 6, but the event kicked off with a keynote at 7, so actual coding (well… conceptualizing) probably started at 7:30-ish, at which point the five themes to choose from were revealed. Pencils-down happened at 2pm Sunday, so we were really about 5½ hours short of 2 days available to write a game-that-trains from start to finish.
The themes available were chosen based on the top-selling elearning courses at OpenSesame, and had to do with things like work safety, time management, and customer support. I teamed up with my buddy Randy, who created the artwork for the game while I did the code, and we chose Fire Extinguisher Safety as our theme.
Rather than cover the entire range of information that comprises a complete training in that topic, which among other things would require that we essentially take the training and slap “game-like” things on it—contrary to the spirit and intention of the program—we chose to concentrate on matching the correct type of fire extinguisher to various classes of fire (from sources such as wood, grease/oil, or combustible metals).
We decided to create an arcade shoot-em-up game that caricatures reality with moving/reacting blazing fire “baddies”, against which you would use different extinguishers as appropriate. Grease fires and a magnesium fire would react very poorly to being extinguished with water-based solutions, and choosing the right tool for the job would become crucial. Up-front instruction would be minimal, relying on the gameplay itself to teach which tools work best. Of course, some amount of textual instruction is unavoidable: using an inappropriate device would produce dire consequences (or at least be less effective than another choice), but also the game would pause momentarily as the game advises the user about the reason for the poor reaction, and advice on making better choices in extinguishing devices.
Alas, pretty much none of that made into the product we wound up with at the end of ~42 hours. You can try it out here (note, the initial loading time will take several seconds). There’s not much to it. You can move your extinguisher guy around using AWSD (or the arrow keys), and swap between devices with the Tab key. You can’t actually activate the extinguisher in any way, and the baddies fire off at you and then just bounce around the screen slowly, never hurting you. If you hit Backspace, you’ll instantly kil the fire (wherever it may be, and regardless of what device you’re using. There simply wasn’t time to get more into the game.
I think Randy and I are a bit disappointed with the lack of features we wanted to get in, but are nonetheless happy that we learned a lot in the process, and are happy that we at least managed to have something coherent to show to the judges. Randy got all the basic art that we needed done (and did a good job of it!), though he definitely needed all the time leading up to the end, but I was a bit slow with the code, and couldn’t do everything we hoped to with the assets we had (though we did use all the assets he created).
Both of us struggled a lot with making progress on Saturday, the only full day available for working, and the day we actually started coding (as Friday night was spent finalizing our plans). For my part, I was using an existing code base I’d created for some other games I’ve been working on, but one basic piece of functionality needed to be added to it (panning a camera over a larger playing field—so far I’d only been writing single-screen games), and struggles with other pieces of the code slowed me down considerably (basically, it turns out my experiment with enforced unit-measurement types in JavaScript ultimately causes more problems than it solves, at least where timeliness is concerned—it needs to be something more ingrained into the language—switching back and forth between simple numbers and the unit typing was the source of too many points of failure).
On Randy’s part, he was using graphical tools (GIMP and Blender3D) that weren’t terribly familiar to him, and was struggling with their many idiosyncrasies. Blender3D can be particularly frustrating to deal with, as they change their UI quite often, so any documentation you find is guaranteed not to map quite right with the version you happen to be using. By the end of Saturday he had become much more comfortable with his tools, and I with mine, but by then there was precious little time remaining to get the bulk of the work done.
It was an excellent learning experience for me, though. Clearly, it was unwise to put myself in a situation with extreme time constraints, and then be building not only the game itself, but also still much of the underlying engine as well. In the future I’d really do well to use only a preexisting engine that has all the features I’ll need it to have , and with which I already have the familiarity I’ll need to make it do what I need it to, without scouring the docs to solve puzzles (or, perhaps, to work around by not using such features in the first place—we’d have been much more productive if I’d just gone with another single-screen game like my others, since avoiding that spent way too many precious hours on Saturday).
OTOH, the time pressure of game jams, and the challenge it presents, is an excellent way to motivate me to learn new tools and technologies, and get familiar with things I’d been meaning to learn about. That’s certainly what happened with my first game jam, which was my first venture into using HTML5 to create a game. And unlike some of the other contestants in jams like these, I’m not really in it to win anything or gain notoriety; I just enjoy the challenge, and find it a fun time (stressful, but fun).
I think I’ve also finally found my breaking point with using pure JavaScript for the logic. I think I may well focus my future game-making efforts on using Elm to code with. Having a proper, strict type system prevents so many accidental goofs in code, and going without them in JavaScript can get to be a real pain (in particular, the existence of null
, or passing an object of the wrong type, or an object that lacks properties it’s meant to have had—such things are impossible in Elm). The purely functional nature of it also greatly aids debugging—which is all the more enhanced by Elm features such as the Time-Traveling Debugger feature, which allows you to step back and replay the state as it changed over time, to see what was going wrong. You can also modify the code on the fly as it’s running (and not just modifying variables as in JS), to see how things change. It’s also more concise and readable than JavaScript, so it’s basically win-win-win-win.
There are some things about Elm I’m decidedly not a fan of… it lends itself to a style of web development that I rather detest, which is when all of the actual substantive content of a page is not to be found in the HTML, but is generated/produced by JavaScript. (The Elm site itself is an excellent example of this.) This is decidedly unfriendly to a host of useful web tools, and the spirit of the web as seen by many of those who were instrumental in bringing it about. But for game programming? I believe it’ll be great. The only thing I’m not entirely certain of is how easy it’ll be to control image preloading… but I’m certain that can be hacked in if absolutely necessary, and meanwhile the other features aiding more rapid productivity are sure to be a boon.
Update (the next morning): Or maybe PureScript instead. I really like the elegance of Elm’s FRP approach; but maybe not so much as I’m likely to miss Haskell features like typeclasses or equivalent tools for ad-hoc polymorphism (that is, using a single function definition to work with many different types that share certain characteristics). PureScript has the same strong typing guarantees that make Elm attractive, though it lacks the awesome debugger. Maybe I’ll just try making or rewriting some of my games in each language before I settle on one.
Elm totally plans on adding typeclasses or something that solves the same problems. But it’s not there at the moment, and I expect that’s going to be hard. It certainly makes it much harder for me to visualize how I’m going to treat, for example, a wide selection of various sprites with different behaviors, as variants of the same thing. Perhaps has-a instead of is-a relationships may save me, but I think I’d have to play around with it more to know for sure.
One thing I really, really like about my current pure-JavaScript engine, is that my sprites are defined in a simple, declarative style:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
sprites.Player = G.makeSpriteClass({ size: U.pixels( 12 ) , hitPoints: 3 , behavior: [ Bh.momentum , Bh.rotateKeys( { clock: [ 'd', 'ArrowRight' ] , counter: [ 'a', 'ArrowLeft' ] } , U.radians( Math.PI ).per.second ) , Bh.thrustKeys( { forward: [ 'w', 'ArrowUp' ] , back: [ 's', 'ArrowDown' ] , left: 'q' , right: 'e' } , U.pixels( 300 ).per.second.per.second ) , Bh.friction( U.pixels( 100 ).per.second.per.second ) , Bh.speedLimited( U.pixels( 240 ).per.second ) , Bh.bouncingBounds( U.pixels( 0 ), U.pixels( 0 ), GA.game.width, GA.game.height, // Play "clink" when we bounce off a wall GA.maybeTeleportGate ) ] , draw: GA.art.drawPlayer }); |
I can just plop in new behaviors in there to change the movement and features, and AI, of sprites. It works using closures, and is really powerful – but of course it works by mutating underlying data in the object that calls them (particularly, coordinates and velocity values). If I can find a way to do things in a similarly convenient way in Elm or PureScript, I will. I obviously can’t do it this way, but Pure functional languages are often this succinct as a natural product of their purity, so I may gain as much as I lose.