When I first got the idea to port the game to the browser, I looked at what kind of game libraries were available. Back in April, nothing seemed to fit what I was looking for. Lots of libraries and frameworks, both free and proprietary, have appeared or blossomed only last summer. And most of the ones I browsed focused on fast-paced shooters and platformers, with animations, collision detection, particle systems, physics simulation and other features that were clearly not needed for Desktop Dungeons. So I quickly decided that I would code everything myself. It did not seem too big too handle.
The other thing that I decided immediately at start (I don't even know if I should call it a decision) was to use only HTML and CSS for rendering: no canvas drawing. I thought that I would be able to ensure compatibility with older browsers that way. And since the game is tile-based, it was a very natural choice. I did not need real drawing capabilities. I only wanted to show images at predefined positions, rectangular buttons and areas, all things possible with <img>s, <div>s, <a>s and a bit of CSS styling. I abandoned compatibility with older browsers (yes, IE 7 and 8, I'm talking of you) along the way, but for other reasons.
The early days
On the first day, I was able to display the 20x20 board with a predefined maze, a yellow placeholder for all the status information, a hero sprite that I could control with the keyboard and move even through walls, and a mouse-following 'cursor'. And all that with one HTML file, two JavaScript (one for utilities/compatibility layer and one for the game itself) and all the bitmaps from the original game. I used git immediately to track my changes and backup my work. I made five revisions on that first day.
Web Desktop Dungeons at one day |
After three weeks of (after hours) work, the game started to look a bit better: the info panel at the right was a bit more fleshed out, moving the hero worked both with the keyboard and the mouse, thanks to the pathfinding algorithm, the maze (still fixed) was filled with lots of intersting stuff and the combat system was in place, somewhat. Except it was not yet possible to die. The JavaScript code had now expanded to four files, totaling 1.200 lines. Three refactorings had already taken place, one of them labelled as "big" in the change log.
Web Desktop Dungeons at three weeks |
Then, I reorganized the code in true MVC fashion, began to develop individual widgets (buttons, gauges, images, tooltips, dialogs), continued to improve support of game mechanics (magic spells, special states, regeneration, power ups), added the basic logic of the menu screen and mid-June, I contacted QCF Design to present my creation. Danny (Day aka Dislekcia) answered me and has always been very positive and supportive. At that time, I wanted to open-source the whole thing and publish it on Github, with permission. Danny convinced me it was not a good idea: the project was still far from finished or even playable. Had I gone public at that time, I would probably have received only negative reactions.
So I resumed work on implementing all the "content": hero classes and races with their unique abilities, monsters, bosses, gods (oh! boy!) and items. I was guided by the content of the wiki and by personal experiments with the desktop version. I contributed a few elements back to the wiki. But after three months, I began to realize that play-testing was not going to be enough to ensure correct implementation of all the details and prevent regressions. I was now at 4.000 lines of JavaScript and I needed an automated test system. Otherwise, it was impossible to manually reproduce rare situations, like finding an altar to Jehora Jeheyu while being immune to poison.
Growing up, gathering tools
I chose JsTestDriver as my unit testing library. It's mature. It can be added to an automated build. It runs the tests in real browsers, not in some kind of virtual JavaScript environment. It can drive several browsers at once. It produces reports that are compatible with JUnit. Sometimes the server gets stuck and needs a restart, but that's really bearable. I started with version 1.3.2 and later migrated to 1.3.3 when it became available. I chose to only write unit tests with it for the game mechanics. I did not invest in automated UI testing. I thought the return on investment for those kinds of tests was not worth it. Breaking something at the UI level would become obvious quickly with some play tests: that could not be said of subtle game logic regression. In hindsight, it was a good choice.
The test suite started with 75 lines of JavaScript in July. At the begginning of August, the code base was 5.600 lines and the test suite more that 600, mainly focusing on hero classes and beasts. In September, 7.800 lines of game code, 1.700 lines of test code, more than half for all the exquisitely detailed behaviour of divine entities.
Yeah! Tests ok across five browsers! |
Mid-August I got interested in JavaScript minification or compilation. I found two possible tools: Doug Crockford's jsmin minifier and Google closure compiler. closure being written in Java was a better fit for my environment and promised more than just minification, with its advanced compilation mode. However, trying to apply it to the project at that time, with several thousands lines of existing code proved too hard. The advanced compiler made wrong assumptions that broke the game and needed me to explain everything and possibly apply deep refactorings, ultimately ruining some of my JavaScript techniques. I gave up on the advanced compilation mode and kept only the basic one. It's still very useful for two reasons. The first is that it packs all the JavaScript code into one single file. It means I can structure my code any way I want, for easy development and still end up with one compact file, better for deployment. The second advantage of compilation is size reduction: closure manages to shave off a good third of the initial size with whitespace and comment elimination and local variable reduction.
On my next project, still in a very early phase, I used closure from the start, and took precautions to keep advanced compilation working. I don't know if it enhances runtime performance, but size reduction is still more impressive. My 50kB of JavaScript become 22kB with standard compilation and a mere 13kB with advanced mode. After all, maybe it's only useless optimization, when the complete JavaScript code is smaller than the image for the splash screen...
Original sources and HTML5
In early September, probably bored at answering my nitpicking questions, QCF Design agreed to send me the GameMaker project of the original game. I discovered both the tool and the game source with mixed reactions. First, GameMaker is primarily targeted at hobbyists and not at programmers or software engineers. Versioning source code is impossible up to version 8.1. Code is scattered all around the place: sometimes as real code, sometimes as graphical constructions. The design screens have somewhat inconsistent conventions of interaction. From a software engineer point of view, it's ugly.
But after doing the tutorials, I began to appreciate its philosophy. I am now convinced that it's a great tool for either hobbyists or rapid prototyping. In each case, it makes it possible to focus on the game itself and not on technical issues (How do I display an animation? How do I play a sound? How do I code collision detection?) It's also probably a great way to introduce children to programming. It's easy and quick to have something working. Afterwards, it's open to experiment.
Is that code?? |
Overall, the game source became my source of answers, and I shamelessly copied the maze generation algorithm.
During the summer, I added a few things that required HTML5 features and definitively lost any hope of supporting older browsers. The first one is anecdotal. I need to use a <canvas> element behind the scenes in Chrome and Opera to scale up bitmaps and keep the pixellated look. The second one is central to the game: saving progress using localStorage. You could argue that it would be better if progress was saved on the server side. But that was not the point. I wanted to provide saved games on the client side, to allow deploying the game anywhere, without server support over simple HTTP transfer, much like what Flash games do. The third HTML5 feature has been added later: sound support. It was a bit tricky (different browsers support different encodings, Safari requires QuickTime before it can play anything, Firefox 3.6 requires <audio> tags to be part of the DOM tree) but otherwise quiet easy to add.
Finishing touches
That's definitely the hard part. At least for me. Most of the interesting bits of code were done. Most of the tools had lost their shine. What helped me was my moral commitment towards Danny and QCF Design. Some psychological studies - yes, I read some of them - suggest that great achievers always have a mental picture of the goal they are targetting and focus on it, whereas under-achievers keep looking in the past at all the work they have already done... and generally stop their effort before finishing, when they consider they have done a lot. I am often guilty of that sort of behaviour. That time at least I finished. The fact that it was a port of an existing game, with a clearly defined goal that I could use to estimate the path left must have played a role in helping me succeed. I must keep that in mind for my future projects.
On the 2nd of October, after exactly six months of work, 9.800 lines of game code and 3.100 lines of unit tests, I told Danny that I was ready for deployment. It still took about a month to add a splash screen to monitor the loading progress over slow connections and iron out a few bugs. And on the 5th of November the game was finally uploaded to the official Desktop Dungeons web site and announced in the news. I took two weeks of vacations in the Bahamas to celebrate and write this account of the complete journey ;)
Bonus
I made a visualization of the development history, if you're curious. It represents the tree structure of the project at each commit. Each type of file has a distinct color. The size of the file is show as the width of the segment (at logarithmic scale). Enjoy! And don't forget to play the game.
The final project structure |
Nice post, was cool to see how you went about doing it!
ReplyDeleteAlthough I do think it would have probably been a lot easier to just use flash instead of js.
Nice work! While poking around the source code I couldn't help but notice your utils file. Do you have that published somewhere with documentation?
ReplyDeleteSorry, but not at this time.
ReplyDeleteThere is a possibility that the whole source could be made open source at some point, but I have an agreement with QCF Design not to do it on my own, without their consent.
That being said, the utils.js file does not contain any Dungeon-specific code, and if you feel like using it, go ahead (but don't sue me afterward). In another project, I have split that file into several and added comments for the Closure compiler, but not really for a user. I think... that file is kind of a kludge to avoid embedding external libraries (jQuery or other) but it's probably far more buggy and less compatible. Another example of the "Not Invented Here" syndrome :)
Thanks for the reply! It has many useful functions that I am trying to figure out like events and observable. The others like listing all the keycodes for keypresses is also pretty useful. I am starting to dabble in javascript games and it has helped so far. Keep up the good work!
ReplyDeleteHello Laurent, any chance to publish the sources of the game somewhere? Enough time has probably passed :)
ReplyDelete