views:

792

answers:

2

[edit]sigh... the "spam protection" here isn't letting me post the links, so I guess it's URIs instead[/edit] [edit2]ok, BROKEN forms of the URI that can get past the regexp...[/edit2]

I'll preface this by saying I'm totally new to SVG, and somewhat new to Javascript, having come from a background in low-level C. On a whim, though, I decided to try my hand at writing a realtime game using all these fun new web technologies.

I'm fully prepared for the answer to be "The web can't do that yet, sorry!" This may simply be too much data throughput for a browser.

So I have written the beginnings of an Asteroids clone. It is a thin SVG document, with the entire game being dynamically generated as SVG line/polygon entities from javascript. Much to my surprise, this actually worked. I was able to get mostly smooth animation from Firefox 3.5 (not tested on anything else).

This does variations of this each time a rock it hit:

// on startup
svg_ns = 'http://www.w3.org/2000/svg';
container = svgdoc.getElementById('rocks');

// each time a rock breaks, times ~20
x = svgdoc.createElementNS(svg_ns, "polygon");
x.setAttribute(...various things...);
container.appendChild(x);

Unfortunately, it's not smooth enough. It lags all the time. There are jerks and breaks in the flow, even on my modern 3GHz box. Several friends I have shown it to also immediately complained of stuttering.

I have spent the last week or so reading up on how to optimize javascript, which helped slightly, but I suspect that the issue is all of the insertions/deletions I am doing to the DOM for pretty much every action in the game. I have considered various pre-allocation schemes, and don't really want some complicated memory manager if it's not actually going to help.

What did come up a lot in my research is discussions of "reflow" and "repaint". As I understand it, any insertion/deletion to the DOM will cause some sort of re-parse of the entire tree, which is slow. The common solution was to collect sub-nodes together first, and only do a single insert into the actual document. This model doesn't really work with a game like this, though.

As a test, I sort of combined the two ideas: pre-allocate when possible, and insert in groups when possible. So in this version, each asteroid is replaced with a SVG group, and the asteroid, it explosion effects, and its pop-up score are created all at once. This way, allocations and insertions only happen when asteroids are created (not destroyed). To keep these extra effects hidden until they are needed, I set the "display: hidden" attribute.

  • new, group-preallocated version: http - public.codenazi.fastmail.fm/asteroids_prealloc.svg
  • (javascript): http - public.codenazi.fastmail.fm/asteroids_prealloc.js

When the rocks are created, this happens instead:

g = svgdoc.createElementNS(svg_ns, "g");
// make the rock itself
rock = svgdoc.createElementNS(svg_ns, "polygon");
rock.setAttribute(...various things...);
g.appendChild(rock);
// make the destroy effect (repeated many times)
frag = svgdoc.createElementNS(svg_ns, "line");
frag.setAttribute(...various things...);
frag.style.display = 'none';
g.appendChild(frag);
// actually add it
container.appendChild(g);

// then, sometime later when a hit is detected
rock.style.display = 'none';
frag.style.display = 'block';

I think this DID make it a bit smoother! But... it also dropped the framerate significantly. I have to keep track of more elements at once, and some testing has shown that wrapping everything in another element makes the SVG render slower as well.

So my question is this: is this even possible? Can I actually get a reasonably smooth SVG animation like this out of firefox? Or is firefox inherently going to have stalls/stutters? If it is possible, is there some memory/element management technique that can allocate/insert SVG elements in a better way than I currently am?

I kind of suspect that the answer will be to just stick with the first, easier method with fewer elements and wait for the future when browsers are better... -sigh-

A: 

I haven't tried SVG animation (yet); but Canvas is surprisingly good at it; especially if you do layers.

In short, create a canvas object for each layer, and erase/redraw each one separately. You can also do quick blitting between an off-screen canvas an the displayed ones.

I know, in theory SVG should be much quicker; but as you've noticed, the DOM parsing kills you. with Canvas there's no need for that.

Javier
I'm suspecting this is the correct solution, but for now I'm going to consider using Canvas to be giving up. Half of the point of this was to try and lean SVG, so canvas kind of defeats the purpose.
pdkl95
Waaaaay late update, but I have sense re-implemented the whole game using Canvas, and it works much better as you said. The difference in smoothness is staggering. I guess I'll have to wait for SVG to catch up...
pdkl95
A: 

Okay, first things first:

Dude - that is the most awesome bit of SVG I have ever seen. Thank you for sharing this.

It ran perfectly smoothly on my system, until I destroyed a whole bunch of rocks at once. What if you replace the dynamic frag animation with a pre-made SVG animation? Does that improve performance?

sealclubber
That's a good idea. I'm not certain if it will speed it up, but it's totally worth trying.
pdkl95