How I made Jingle Smash
This is part 1 of my series on how I built Jingle Smash, a block smashing WebVR game
When advocating a new technology I always try to use it in the way that real world developers will, and for WebVR (the VR-only precursor to WebXR), building a game is currently one of the best ways to do that. So for the winter holidays I built a game, Jingle Smash, a classic block tumbling game. If you haven't played it yet, put on your headset and give it a try. Now an overview of how I built it.
Jingle Smash is written in ThreeJS using WebVR and some common boilerplate that I use in all of my demos. I chose to use ThreeJS directly instead of A-Frame because I knew I would be adding custom textures, custom geometry, and a custom control scheme. While it is possible to do this with A-Frame, I'd be writing so much code at the ThreeJS level that it was easier to cut out the middle man.
Jingle Smash is an Angry Birds style game where you lob an object at blocks to knock them over and destroy targets. Once you have destroyed the required targets you get to the next level. Seems simple enough. And for an 2D side view game like Angry Birds it is. I remember enough of my particle physics from school to write a simple 2D physics simulator, but 3D collisions are way beyond me. I needed a physics engine.
In previous games I have used 3D models created by an artist. For this Jingle Smash I created everything in code. The background, blocks, and ornaments all use either standard or generated geometry. All of the textures except for the sky background are also generated on the fly using 2D HTML Canvas, then converted into textures.
I went with a purely generated approach because it let me easily mess with UV values to create different effects and use exactly the colors I wanted. In a future blog I'll dive deep into how they work. Here is a quick example of generating an ornament texture:
const canvas = document.createElement('canvas') canvas.width = 64 canvas.height = 16 const c = canvas.getContext('2d') c.fillStyle = 'black' c.fillRect(0, 0, canvas.width, canvas.height) c.fillStyle = 'red' c.fillRect(0, 0, 30, canvas.height) c.fillStyle = 'white' c.fillRect(30, 0, 4, canvas.height) c.fillStyle = 'green' c.fillRect(34, 0, 30, canvas.height) this.textures.ornament1 = new THREE.CanvasTexture(canvas) this.textures.ornament1.wrapS = THREE.RepeatWrapping this.textures.ornament1.repeat.set(8, 1)
Most block games are 2D. The player has a view of the entire game board. Once you enter 3D, however, the blocks obscure the ones behind them. This means level design is completely different. The only way to see what a level looks like is to actually jump into VR and see it. That meant I really needed a way to edit the level from within VR, just as the player would see it.
To make this work I built a simple (and ugly) level editor inside of VR. This required building a small 2D UI toolkit for the editor controls. Thanks to using HTML canvas this turned out not to be too difficult.
I'm pretty happy with how Jingle Smash turned out. Lots of people played it at the Mozilla All-hands and said they had fun. I did some performance optimization and was able to get the game up to about 50fps, but there is still more work to do (which I'll cover soon in another post).
Jingle Smash proved that we can make fun games that run in WebVR, and that load very quickly (on a good connection the entire game should load in less than 2 seconds). You can see the full (but messy) code of Jingle Smash in my WebXR Experiments repo.
While you wait for the future updates on Jingle Smash, you might want to watch my new Youtube Series on How to make VR with the Web