Home       About Me       Contact       Downloads       Part 77    Part 79   

Part 78: Another WebGL Demo

April 1, 2013

I've converted the DontHitMe demo to Javascript and WebGL. Click below to run it. Use WASD or the cursor keys to move. There's no sound, no collision detection and no scoring, but you should get the idea of what a completed game would look like. The goal is to not hit anything!


Don't Hit Me Demo

I'm encouraged by the number of times the demos were run last week. Hit counts are always problematic due to RSS readers and multiple attempts by the same person, but here are the numbers I have:

In the last 8 days, the page images were hit 1051 times. The StarrySky script (used by both demos, so probably cached) was hit 308 times. Once WebGL is found and runs, it loads the sky texture images, which were hit 255 times. So about 25% of you managed to run the demos.

Since I started my statistics logging server in Part 59, only 1285 logs have been sent, so this is a substantial number of runs for the demos. We'll see how this part turns out, and what kind of counts I get for a finished game.

Javascript issues

This demo was about 3,000 lines of code. Add the milligram library and the other two demos, and I've converted 11,000 lines of C++ to WebGL. Here are the issues I still have:

  • I continue to have debugging issues due to misspelled method calls or variable names and wrong numbers/types of arguments. I really miss static checking of code by the compiler! I also miss method/variable name completion from the Visual C++ IDE.

  • To get decent performance, I can't just translate the C++ code blindly. In C++, I might have something like this in an inner loop:
    for (int z = 0; z <= m_samples; z++)
    {
      pz = z/(double) m_samples;
    
      mgPoint3 pt(px-0.5, py-0.5, pz-0.5);
      pt.normalize();
      ...
    }
    

    In Javascript, I would translate this into

    for (var z = 0; z <= this.samples; z++)
    {
      pz = z/this.samples;
    
      var pt = new mgPoint3(px-0.5, py-0.5, pz-0.5);
      pt.normalize();
      ...
    }
    

    The use of "new" in an inner loop is going to cause more garbage collection and waste time. I should move it outside the loop and just set "pt.x", etc. to new values in the loop. In fact, if the method is called many times (such as during rendering), I should make "pt" an instance variable so that that "new" is only called once, not once per render.

    This is all doable, and I have done a bit of it as I've translated the code, but it kind of messes up the source, from a C++ perspective. It can also lead to subtle errors, if you reuse the "pt" variable in two methods which call one another. This also makes it hard to keep the Javascript version in sync with the C++ version.

  • I'm not sure if it's the time to parse the source scripts or some other issue, but reloading the page is starting to be a bit noticeable in Chrome. It's only a couple of seconds, but I wonder how bad it would get if I had 100,000 lines of source code.

Once I had a fair amount of C++ code translated, I decided to check performance. The Simplex Noise routines are about as simple as you can get -- just array references and arithmetic. I called them a million times to see what numbers I would get. The test script is here. Reload several times to get an idea of the range. It seems to vary a lot!

C debug236 ms
C release118 ms
Chrome170 ms
Firefox183 ms
Android Chrome  580 ms

This a just a preliminary test and doesn't mean much. Since there's so little code inside each Simplex Noise call, I have a big component of the time spent just on the function calls. Still, it's encouraging that Chrome and Firefox did so well on this code. The Android number is a bit depressing though. To keep this code running well on tablets with WebGL, I'm going to have to be really careful with CPU.

It would be interesting to know how well this code would run in Java as an Android app. I'm not sure it's worth the time to figure the IDE out at this point. If someone out there wants to try it, email me.

Several comments last week pointed me to asm.js Using this, I could compile my C++ code to Javascript. I'll take a look at that, but it's not as if the code can be used without change. I still have libraries to interface to. I'm not sure what the performance of a demo translated by hand vs. compiled to Javascript will look like. I do know the resulting Javascript will be useless to other JS programmers.

Rendering Issues

Rendering performance on my desktop is still at 60fps, which is the fastest the Javascript "requestAnimationFrame" feature supports. I haven't tried to instrument it more carefully and compare to the C++ implementation. On the Nexus 10 tablet, it manages around 40-50 fps.

There's some bug in my texture atlas shader which leads to a bad edge on the side of the saucer. I don't remember seeing this when running OpenGL 2.1 on the desktop, so I'm not sure what's causing it.

On the other hand, this demo has had weird problems under desktop OpenGL 3.2 on some of my machines. I've seen the splotchy ship texture go completely wrong, and the walls of the towers disappear. I don't see any of this under WebGL.

UI Issues

To finish this off, I need some kind of menu/options screens. I will try to do this with overlaid HTML elements, but I'm not sure how exactly I can control the look across browsers. On my own machine, I have fonts set large, which breaks a lot of pages on the net. I'm not sure if you can really do this right.

If I don't use HTML, I have to draw menus and so on with WebGL. That means either having a font texture and copying characters out of it, or using the Canvas support to create textures on the fly. I haven't researched that at all, and don't know how hard or slow it is.

Finally, the touch interface for the game is a problem. Javascript gets touch events with multiple points (one for each finger.) The user needs to be able to control this with two fingers -- one for turning and one for speed. Right now, the buttons at bottom-left just respond to the first touch, and don't work correctly even for that.

I'm not sure if I can just attach touch event handlers to buttons and get what I want. I think I need to attach them to the canvas and check each touch point against all the controls. That means knowing exactly where they are on the screen. I'm not sure how to do that.

Finishing the Game

I need to add collision detection, since the whole point of the game is to avoid hitting things. Then there needs to be lap timers and a scoreboard for high scores. The track needs to be smoother and more interesting, and there needs to be sound. I'll probably play with the textures and world a bit more.

My inspiration for this was the game Proun, but I doubt I'll go to the trouble of multiple tracks, an internet high score board, etc.

I hope to have this finished soon and move on to some kind of simple game server with both WebGL and C++ clients.

Home       About Me       Contact       Downloads       Part 77    Part 79   

blog comments powered by Disqus