In this post I'm going to do some housekeeping on the game and add four small things that each make the development process a little bit nicer:
Note that the fatal severity level will also open an alert dialog in the browser, effectively stopping execution of the page.
- A constants file to keep all the configuration magic numbers in one place
- A script to serve the game files up to your browser from your machine, so you can build and test the game locally and very quickly
- A better way to see the debug console output without having to open up the browser debug console
- A favicon to stop the browser complaining that it can't find one
Constants
We all know that using magic numbers in code is bad, right? Well, I've used them in a few places in the game so far. Now that the code has grown beyond a few dozen lines it's time to move those numbers to a better place. I've added a configuration file where all of these numbers are stored together in one place. This makes some of the other code easier to read (using names instead of magic numbers) but it also makes it easier to go in and tweak those values if and when I need to.
Here's the code to the config file. It uses the enumerated types class I created earlier to prevent the values from being changed at runtime:
Here's the code to the config file. It uses the enumerated types class I created earlier to prevent the values from being changed at runtime:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
config = makeEnum({ | |
// Size of the main game canvas | |
canvasSize: 640, | |
// Timing constants | |
framesPerSecond: 30, | |
msPerFrame: 1000 / 30, | |
// Player config | |
playerSpeed: 4, | |
// Bullet config | |
bulletSpeed: 5, | |
}); |
Local Server
To run the game you need to use a webserver. The way to do this is to install a webserver (or get an online webspace provider), copy the contents of the source directory there and then open the page in your browser. That's a bit long-winded. I want to keep the development-test-iterate cycle as short as possible. I could install a webserver locally on my machine to do this, but there's an even easier solution: use Python. Python comes with a built-in simple webserver. In fact, it's as simple as opening a console in the source directory, typing "python -m http.server" and then browsing to http://localhost:8000.
To make it a little easier to manage, I've added a small Python script that you can just double-click (or execute in whatever way you like) to start serving the game on http://localhost:8000. Here's the code:
Once the server is running, you can edit the code, save it, then just refresh the page in your browser to see the changes. Fast iteration time is a cornerstone of good development.To make it a little easier to manage, I've added a small Python script that you can just double-click (or execute in whatever way you like) to start serving the game on http://localhost:8000. Here's the code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Simple script to serve up the source directory on localhost. This lets you test the game on your | |
local machine without having to copy it to a webserver first. When the script is running you can | |
browse to http://localhost:8000 to run the game | |
You will need to have Python3 installed on your computer | |
The script should run without any trouble from Linux. In Windows you may be able to just double-click | |
on it to get it to work. If it doesn't you will first need to associate Python files with Python. | |
To do this, right-click on the file in Explorer, select "Open with" and choose Python. | |
""" | |
import http.server | |
import os | |
import socketserver | |
PORT = 8000 | |
DIRECTORY = 'source' | |
os.chdir(DIRECTORY) | |
with socketserver.TCPServer(('', PORT), http.server.SimpleHTTPRequestHandler) as httpd: | |
print('Bullet Hell Survive Alpha\n\nServing from %r at http://localhost:%s' % (DIRECTORY, PORT)) | |
httpd.serve_forever() |
Debug Console
Logging is super important in debugging any application, and just using console.log() doesn't really cut it. I've previously written about a simple method for debugging JavaScript by writing the messages to the webpage so that you can see them more easily. I'll be using that same method here, but I'll be adding a couple of severity levels to the logging system. Severity levels make it easier to differentiate the trivial (things that are normal behaviour) from the serious (things that are actually errors).
Here's the code for the log manager:
There are four different severity levels defined, and here are some examples of how I might use them:Here's the code for the log manager:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class LogManager { | |
constructor() { | |
this.logLines = []; | |
this.outputElement = document.getElementById('debugText'); | |
this.info('LogManager started'); | |
if (!this.outputElement) { | |
this.warning('Couldn\'t find debugText element on the page. Log messages will go to console only'); | |
} | |
} | |
info(message) { | |
// Informational messages about how the system is operating | |
this._log('INFO', message); | |
} | |
warning(message) { | |
// Something went wrong, but the system handled it | |
this._log('WARNING', message); | |
} | |
error(message) { | |
// An entire operation failed, but was handled | |
this._log('ERROR', message); | |
} | |
fatal(message) { | |
// An error that means that we cannot continue | |
this._log('FATAL', message); | |
alert(message); | |
} | |
_log(severity, message) { | |
// Add the severity to the start of the message | |
const fullMessage = '[' + severity + '] ' + message; | |
// Append to list and limit to 20 lines | |
this.logLines.push(fullMessage); | |
if (this.logLines.length > config.numOutputLinesFromLoggingManager) { | |
this.logLines.shift(); | |
} | |
// Output to the HTML element | |
if (this.outputElement) { | |
this.outputElement.innerHTML = ''; | |
this.logLines.forEach(line => { | |
this.outputElement.innerHTML += line + "\n"; | |
}); | |
} | |
// Echo to console too | |
console.log(fullMessage); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
logManager.info('Player started a new level'); | |
logManager.warning('Resource failed to load, re-trying'); | |
logManager.error('Could not load resource after re-trying, game may not work correctly'); | |
logManager.fatal('Could not find main canvas element, game cannot start'); |
Note that the fatal severity level will also open an alert dialog in the browser, effectively stopping execution of the page.
Favicon
When you load the game up in a browser, you'll notice that there's no favicon and no page title. If you look in the browser's debug console, you can see that the browser is missing the favicon too:
Both of these are trivial to fix. For the favicon I uploaded the player sprite image to https://www.favicon.cc/, downloaded the favicon.ico file that it generated and saved it in the source directory. To add the page title I just added a Title element to index.html.
Both of these are trivial to fix. For the favicon I uploaded the player sprite image to https://www.favicon.cc/, downloaded the favicon.ico file that it generated and saved it in the source directory. To add the page title I just added a Title element to index.html.
Wrapping Up
In this post I tidied up and fixed a number of small issues in the game. All of them make the game easier to work on, but none of them were really big enough for their own post.
You can see the new code here and the playable online version is here.
You can see the new code here and the playable online version is here.
Next
So, what's up next? I've been concentrating on tech so far, and I think it's time to switch over to working on the gameplay a little. I'm going to add some way to score points, and then I'll use that as a way to start tuning the gameplay - it needs to be fun to score big.
Feel free to leave comments to let me know where you think I should go next.
Feel free to leave comments to let me know where you think I should go next.
Comments
Post a Comment