This is a short guide that will teach you the workflows that have been figured out by the voxel.js community for writing node modules + sharing them on NPM and Github. It is assumed that you have a basic understanding of JavaScript, github and the command line (if not you can check out an introduction to git and the command line or learn JS basics from JavaScript for Cats)
The voxel-tower repository on github contains all the example code from this guide.
- Installing Node
- Making a new project folder
- Writing the module code
- Make an example/demo
- Development workflow
- Making a good README
- Publishing to NPM
All of the components in the voxel.js ecosystem are open source + available on NPM and github. NPM is the Node Package Manager and is automatically installed when you install Node, which can be downloaded from nodejs.org.
You can use the command npm search voxel
to see all of the modules in one big list. We use node modules because they are the simplest module format available in JS and we use NPM because everyone who shares node code uses NPM because it makes it super easy to share and publish modules, and generally gives you less headaches than other package managers.
We use browserify for packaging node modules together into game bundles that will run in your browser. Not every module on NPM will work with browserify (for example, browsers cannot yet make UDP connections), but a lot of them will!
voxel.js 'Add-ons' are actually just node modules. If this guide doesn't explain it well enough you can google for blog posts and tutorials on how to write node modules to find out more.
Let's pretend we are making a new module called voxel-tower
, which will let you place a big stack of voxels. Starting from scratch here is how you can start hacking on this new module:
- make a new folder called
voxel-tower
- inside that folder, you can either type
npm init
or manually make a file calledpackage.json
with the following contents:
{
"name": "voxel-tower",
"version": "0.0.1"
}
- make another file called
index.js
with the following contents:
module.exports = function() {
// TODO add some code :D
}
To recap: all you need to get started is a package.json
with a name
and version
, a folder that shared the same name as your name
in package.json
, and a file called index.js
that exports something (module.exports
is the thing that gets loaded when you type require('voxel-tower')
The best place to learn about how to use voxel.js is the voxel-engine readme as well as the source code for the examples and modules on voxeljs.com.
Let's add some code to our index.js
:
// a convenience function, usage:
// var tower = require('voxel-tower')(game)
// if we did module.exports = Tower then the usage would
// have to be:
// var Tower = require('voxel-tower')
// var tower = new Tower()
module.exports = function(game, opts) {
return new Tower(game, opts)
}
// expose the Tower constructor so that it is available
// in case someone wants to access the .prototype methods, etc
module.exports.Tower = Tower
function Tower(game, opts) {
// protect against people who forget 'new'
if (!(this instanceof Tower)) return new Tower(game, opts)
// we need to store the passed in variables on 'this'
// so that they are available to the .prototype methods
this.game = game
this.opts = opts || {}
this.height = this.opts.height || 5
this.material = this.opts.material || 'brick'
}
// creates a new stack of voxels
// usage:
// var tower = require('voxel-tower')(game, { height: 5 })
// tower.place([5, 30, 0])
Tower.prototype.place = function(position) {
for (var i = 1; i < this.height + 1; i++) {
var pos = [position.x, position.y + i, position.z]
this.game.setBlock(pos, this.material)
}
}
A common pattern is to pass in the game
instance (from voxel-engine
) so that you can modify the game state from within your module. If you need to access three.js methods you can use game.THREE
. Don't require('three')
in your modules as it will cause weird rendering bugs!
OK, lets get our module loaded into a game in the browser so we can test it out! The easiest way to do this is to add voxel-hello-world
and the painterly-textures
texture pack to your devDependencies
section of package.json
and then install them:
npm install --save-dev voxel-hello-world painterly-textures
Now make a file called demo.js
with the following contents:
// __dirname is the current working directory, we pass it in to
// the textures module and receive back the path from here to where
// the textures are located
var textures = require('painterly-textures')(__dirname)
var game = require('voxel-hello-world')({texturePath: textures})
var tower = require('./')(game)
// make a tower appear after 5 seconds at the players position
setTimeout(function() {
tower.place(game.controls.target().avatar.position)
}, 5000)
Then make a new file called index.html
and put this line into it:
<!doctype html>
<html>
<body>
<script src="bundle.js"></script>
</body>
</html>
To get your game running you have to build a browserify bundle. Here is how to do it manually:
# install browserify
npm install browserify -g
browserify demo.js -o bundle.js
# install and start a http server in the current directory
npm install http-server -g
http-server
Now you should be able to open http://localhost:8080 in your browser to play around with the game.
The above manual process of bundling the game from demo.js
into bundle.js
works okay but you have to go the command line every time. It also assumes that people who clone your repo will have already done npm install browserify -g
ahead of time. Setup should be as easy as possible -- this way more people will send you pull requests!
Firstly, lets install a new devDependency
called beefy which will replace the need for http-server
by automatically hosting our game for us and -- this is the best part -- automatically bundling our game every time we refresh the page. We should also add browserify
as well so that they both get installed automatically into the node_modules
folder by npm install
:
npm install --save-dev browserify beefy
Open up package.json
and add a start
script:
"scripts": {
"start": "beefy demo.js:bundle.js 8080"
}
This essentially means every time a <script> tag requests test.js browserify bundle.js and send it back as test.js
. Now you can just type npm start
to start a local dev server. Go ahead and try it out!
The other huge benefit of this approach is that the steps to get your repository to run locally are just git clone
, npm install
and npm start
.
There is more information available on the beefy website.
If your compile times are slow you can try this alternate version which is optimized for speed: beefy index.js:bundle.js --debug=false -- --fast
, which
READMEs are important for users of your code as well as developers. The main outline of most of the voxel.js readmes is:
- module title
- short description
- what is the module's name on npm
- list of api methods and how to use them
- optionally: license, changelog, misc notes
Most people use markdown formatting in their readmes. Make a file called README.md
and start writing! You can also copy-paste an existing readme and change the relevant parts.
Your readme will show up on github as well as npmjs.org. A great example is the readme for voxel-physical, check it out on github and npmjs
When your module works + is ready to share you just have to type npm publish
to put it on the NPM registry. Whenever you change your projects version you should do npm publish
again.
To decide when you should update your version number please read SEMVER FTW!.
A healthy package.json
is also a sign of a good maintainer. Here is a great one to learn from: voxel-texture package.json. At a bare minimum you should have the name
, description
, author
, and repository
fields filled out.
Most authors choose a "do whatever you want, just don't sue me" license, such as BSD/MIT/Apache. The other kind of license is a "you can't use this commercially" or "you can't use this without putting my name on your website" but it's up to you if you want to be prescriptive about usage of your code: the more barriers you add the less you will receive contributions.
Finally, please send a pull request to https://github.com/maxogden/voxeljs.com and add your module to the list of add-ons so that it can be discovered and used by other developers.
If you have questions/comments you can view this article on github or you can also ask a question in the #voxel.js IRC room on freenode.
I really like this approach and workflow. Thanks!