Hello world (it's my first post here)!
I'm responsible for Butcher's programming (and for a few other things, but that's unrelated now). We (THD) have received an email asking for explanation of how the game works, so I hope I'll provide you enough info about it.
- I haven't used any external engine, it's a custom JavaScript (it started as a simple experiment/exploration of canvas capabilities). It has its good and bad sides. The bad side is that some parts definitely could be coded in a more elegant way, but overall it was a cool experience.
- I haven't used WebGL (besides pixelation/stretching the screen, which seems to be faster if done with WebGL shader - the rest is fully software/canvas and you can play without WebGL support)
- The burning logo, the rain of blood in the intro and the lava effects (including lava particles - they repeat after a while) are just color-cycled images, this part was partly inspired by Canvas Cycle demo, although the implementation and functionality is a little different (simpler I think):
- The palette is a strip of colors that will appear in the output image, including transparent colors.
- The input image is completely grayscale, each pixel represents a different place on the palette, shifted by a time variable (and also looped, so it can cycle)
- I obviously read the input image once, then just put pixels repeatedly at the runtime.
I must admit I did that thing mainly because of my nostalgia (some games used palette cycle technique in the past), I don't know yet whether that approach is useful or not (in comparison to normal animations), but it has some advantages (for example, creating organic animations like fluids or burning seems to be easier that way). R0J0hound was right that the canvas implementation requires low-res for good performance, but a pixel shader doing the same thing on much bigger textures could be very fast (it'd require only 2 texture reads after all).
- The collision is based on internal array to avoid reading pixels. The level was even initially destructible in some areas, but we got rid of it as it didn't work out well for this type of gameplay. The collision masks are also pre-processed to avoid too awkward logic at the runtime (and also to make the movement smoother). For example, I detect slopes to simplify the platformer collision code.
- I do use drawImage() a lot like other games, because that's the fastest method of drawing bigger sprites, also the game supports rotation and scale (saws, bullets, some particles), it's just not used that much because of the style we were aiming for. However, the level is indeed painted per-pixel because of the internally stored collision mask. I need to paint both the mask (i.e., the array) and the image, so the easiest way is just to paint pixels (one by one) of both at the same time (because I don't have any high level function to paint arrays ). It's definitely slower than using drawImage(), but the sprites painted on the level are very small (4x4 pixels max. I think), so it shouldn't hurt that much. The fastest and most flexible method of painting blood (and in general pixels that were potentially spread everywhere) was to draw filled 1x1px rectangles (because you don't want to draw 400x400 pixels just to draw 5 blood pixels and you don't have to create image data each blood pixel), at least that was what jsPerf test told me back then.
- I use my own matrix stack and matrix operations, because it seems to be faster than save()/restore() on modern PCs/browsers. I think it squeezed out a 0.5 ms or so, not that much, but it was just kind of a mental masturbation to fiddle with it.
- I use an extremely simple space division algorithm (I just divide the whole level to a "buckets" and put things in proper buckets according to their positions), it could support 1000 of self colliding squares spread throughout the whole level at 60fps on my computer, afair the drawing of the rectangles was a bottleneck here, so I considered it good enough for my case.
- The Doom effect is just a bunch od drawImage() calls (each strip is a different call).
- The game could run pretty smoothly on computers from 2007+, but the bottleneck on the older graphic cards was, surprisingly, stretching the whole canvas aka pixelation. The game was playable if played in its native resolution (320x180, yes, it's 16:9 ), it started to lose frames if you tried to stretch the screen to enlarge in-game pixels. Also, the modern browsers didn't want to support the WebGL pixelation on older cards, Chrome even blacklisted some chipsets. Oh well. That's one of the reasons I now develop the game on a different platform (I don't develop my own engine this time, I decided to use Unity - now I regret a little, because it's still not a perfect engine for that kind of game, but at least the new Butcher runs smoothly on older PCs).
- Standalone versions are built with node-webkit.
I think that's all for now, ask me if you want to know further details. I've originally tried to post some links, but I can't do it (the posts just didn't appear, no messages, no anything, I think it's a bug in the forums script - even if newcomers can't post links, I'd expect some validation messages).