Skip to content

Instantly share code, notes, and snippets.

@timoxley
Last active December 30, 2019 07:56
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timoxley/4697136 to your computer and use it in GitHub Desktop.
Save timoxley/4697136 to your computer and use it in GitHub Desktop.
Things I learned about Voxel.JS today

Terminology

  • block/voxel/cube -> mostly interchangeable. The minecrafty blocks you see on the screen.
  • chunk: is a piece of the world that contains voxels
  • AABB: bounding volume
  • voxeljs: not 100% consistent yet, 'voxel.js' also acceptable, but definitely not 'VoxelJS'.
  • dims: short for 'dimensions'. Perhaps obvious to some.

Positions

  • x, z: horizontal plane
  • y: vertical plane

voxeljs uses various forms to represent positions:

  • {x: Number, y: Number, z: Number}
  • [x, y, z] and
  • Three.js's Vector3. Vector3 is rad and has a bunch of operations that allow you to combine vectors in various ways. Wish this was the only way they represented positions, but perhaps it's not a good idea for performance reasons. ??

Check if there's a block at a position

// returns 0 or 1 depending whether block at that position exists and is 'on'
game.getVoxel(pos)

Toggle a block on/off

game.setVoxel(pos, 0) // off
game.setVoxel(pos, 1) // on
game.setVoxel(pos, 2) // on, with another material

Get player position

game.controls.yawObject.position

Generate a flat world 1 block high

Flat world is a nicer way to start (at least you can't fall off the edge). This places the player just above the ground.

var game = createGame({
  generate: function(i,j,k) {
    return j < 1 ? 1 : 0;
  },
  startingPosition: [0, 75, 0],
  texturePath: texturePath // etc
})

Defaults

Block Size

Default block size is 25, accessible via game.cubeSize:

game.cubeSize // => 25

Player Size

Default 'player size' is a 1/2 block long/wide and 1.5 blocks high:

game.playerAABB().width() // => 12.5
game.playerAABB().height() // => 37.5

See implementation of Game.prototype.playerAABB for more details.

Events

Game instance can fire the following, non-exhaustive, list of events:

game.on('mouseup', fn), game.on('mousedown', fn)

Captures mouse activity

game.on('tick', fn)

Called every game tick. Callback is passed delta.

game.on('collision', fn)

Called every tick when an item is colliding with the player. Callback is passed the item that is colliding.

Collisions

Check for collisions between an item and other 'things'

Detects collisions between an item and other items, or voxels.

game.getCollisions(item.mesh.position, item)

This will give you back a 'collisions object' whose keys are positions on the object and values are arrays of the positions of faces that are colliding.

For example, here we have 4 faces colliding with the bottom of our object:

{
  back: Array[0]
  bottom: Array[4]
  down: Array[1]
  forward: Array[0]
  left: Array[0]
  middle: Array[0]
  right: Array[0]
  top: Array[0]
  up: Array[0]
}

Textures

Loading textures creates multiple "materials".

var material = game.loadTextures([ 'obsidian', 'dirt' ]);

Both of these textures come with 6 materials, one for each side of a cube, giving a total of 12 materials. By default, faces 1 to 6 are assigned materials 1 to 6. You can assign materials to faces in however you want. For example, we could load materials 7 to 12 (e.g. the dirt materials) like so:

mesh.geometry.faces.forEach(function (face, index) {
    face.materialIndex = index + 6; // obsidian texture indices 0 - 5, dirt 6 - 11.
});

Items

  • Items are non-voxel objects you can add to your game. e.g. Monsters/Players, Powerups, etc.
  • Items currently implement their own physics, which is calculated every 'tick' by running an items' item.tick function. It's not very sophisticated.
  • Items .mesh property is the thing that's actually processed by the THREE.js engine. Other properties of item are used in voxel.js to update the mesh, e.g. item.velocity.y is used every tick to calculate the next position the mesh should be in.
  • Using the current item physics system, setting item.resting to false will force an item to recalculate it's position.

Example: Creating an Item

// texture for item
var material = game.loadTextures([ 'obsidian' ]);
var mesh = new game.THREE.Mesh(
    new game.THREE.CubeGeometry(10, 30, 10), // width, height, depth
    material
);

// move item to some location
mesh.translateX(87.5);
mesh.translateY(420);
mesh.translateZ(12.5);

// if these item dimensions don't match the mesh's dimensions,
// the object's physics will not operate correctly.
var item = {
    mesh: mesh,
    width: 10,
    height: 100,
    depth: 10,
    collisionRadius: 20, // padding around object dimensions box for collisions
    velocity: { x: 0, y: 0, z: 0 } // initial velocity
};

game.items.length // => 0
game.addItem(item)
// use `game.removeItem(item)` to remove
game.items.length // => 1
@timoxley
Copy link
Author

timoxley commented Feb 2, 2013

on second look, most of this stuff is in the readme haha. TLDR.

@christopherdebeer
Copy link

still useful, I'm still trying to wrap my head around the implementation, such that i can add a 3rd person orthographic camera ;)

@martindale
Copy link

still useful, I'm still trying to wrap my head around the implementation, such that i can add a 3rd person orthographic camera ;)

@christopherdebeer did you ever get a 3rd person camera working?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment