Friday, February 17, 2012

The Gamepad API and the case of the shifting buttons

Took the opportunity to look at the Gamepad API for the first time yesterday while at the Mozilla Games Work Week. It's a simple API at it's core, and one that should be very straightforward to use. I gave a stab at it by sneaking gamepad controls into my Quake 3 demo (Have you noticed that tends to be the testbed for all my API fiddlings?) and am semi-pleased with the result. If you plug in most controllers (Xbox, PS3, etc) you should get at least be able to move with one stick and look with the other, though depending on the OS/gamepad/browser some things may not work quite as expected.

And that brings me to my grip with the Gamepad API as it exists now. It IS OS/gamepad/browser specific. My current code looks roughly like this:

for (var i = 0; i < navigator.gamepads.length; ++i) {
    var pad = navigator.gamepads[i];
    if(pad) {
        walk(pad.axes[0], pad.axes[1]);
        look(pad.axes[2], pad.axes[3]);
            
        if(pad.buttons[0]) { playerMover.jump(); }
    }
}

The basic idea here is that we loop over all the available gamepads and when we find one pull from axis 0 and 1 (hopefully left stick on your basic 360/PS3 style pad) for movement, axis 2 and 3 (right stick?) for looking around, and button 0 (???) to jump.

That's the theory, anyway. Here's what actually happens: In Chrome on my Mac the DirectInput gamepad I have returns the sticks as axis 0/1 for the left stick and 2/5 for the right. This means that looking up and down is broken. With my Gamepad-enabled Firefox build, though, the axes are actually 0/1 and 2/3. If you use the PS3 controller, the axes match up to what you would expect on both browsers, so that's nice, but the button mapping is COMPLETELY different between browsers. Firefox considers the "X" button to be button 0, while Chrome things button 0 is D-Pad up. (Firefox thinks the D-Pad is an axis, actually.) So if you're going to use gamepads on your page, um... you're kinda screwed. It's like if typing a "W" on one browser gave you a "W" and on another it gave you a "U". Madness!

There are projects out there already that try to normalize this situation. Gamepad.js and input.js are the big ones right now. Both do some checking against the browser/os/gamepad model and map them into a more sensible gamepad structure that looks a lot like XInput. (That's a good thing, by the way!) The biggest problem with these libraries is that if the library author hasn't gotten their hands on your exact controller you're out of luck. Also if you have a controller that happens to not look at home on a modern console, like an actual joystick, these libraries will do nothing for you.

I'm not trying to be down on the gamepad abstraction libs, because I actually think they're great and would encourage people to use them. What I feel is problematic is that the browsers aren't returning consistent data for the same device. If we could remove the question of OS and Browser from the equation and just focus on the device that's connected developers would be able to cope with the vast array of input devices out there with the minimal amount of pain required by dealing with such a varied field. If things stay the way they are now, we can still get everything to work, eventually, but we'll be working three times as hard for it.

So please, browser peoples, get everyone together on a conference call and all agree that you'll poll the hardware the same way. It will make our lives as developers much easier!

Thursday, February 9, 2012

...in which I give updates on everything

Also known as: Why I've been so lazy lately.

So I know that theres more than a couple of you out there that have been very patiently awaiting the next post in my Building the Game series for the last couple of months. It's probably been a little frustrating to have the first 6 posts come out fairly quickly and then just abruptly cut off. For that I apologize.

The reality is that I've been buys with plenty of other, non-blog-related things lately and simply haven't been able to spend the time working on it. During the holidays I made a conscious decision to focus on family rather than code, and immediately after that I've been pretty swamped with a project from work (you know, the guys that actually pay me!) that has left me with little desire to do even more code when I get home. On top of that, I quickly realized that the where I wanted to take the bog series next would require some research into areas that I've never really poked into before (generating visible sets and such) and it's been difficult to find the time and resources to really dig into it.

In other words, I've been feeling a little burnt out. I think all programmers go through this from time to time, and I'm simply allowing myself to recover and gain back some of that all important enthusiasm.

Don't fret, this isn't my "well that was a failed experiment" post where I leave things forever unfinished. I'm going to continue the series soon, but I think I'll be back tracking a little bit and hitting some other aspects of it first before getting back into the whole level loading thing. Better animation management and player input feels like a good place to hit next, along with some retrospective tweaks to the model formats. I don't think I can give dates on when we'll see the next post, but rest assured it will be sooner than later.

Beyond the blog, however, there's been a couple of other developments worth mentioning lately. Since the contract is now signed I think it's safe to announce that I'm currently co-authoring a WebGL book! I was asked to help Diego Cantor complete a WebGL Beginners Guide for Packt Publishing, which has proven to be no small feat. I don't know anything really about potential release dates or anything like that, but rest assured that I'll be tweeting and blogging about it once I know more.

Also, I've been invited to attend Mozilla's HTML5 Games Work Week this February. Essentially, Mozilla is reaching out to people who have been active in game-related HTML5 tech (like WebGL) and is bringing them together for a week to ask "How can we make the web an awesome game platform?" This is a goal I think we can all support!

For my part, I want to try and address several concerns from the development community that I've run into either through this blog, via email, or that I've seen talked about on various other online resources. These include:
  • Synchronizing game logic and rendering
  • Considerations for reducing power consumption of games on mobile devices
  • Content protection
  • Content delivery
  • Robust input handling
  • High-performance audio
But I'd also love to hear what the rest of the HTML5 game dev community is concerned about! I can't promise that everything that I hear about will be addressed but if I'm hearing similar concerns from multiple people you can be sure I'll do my best to bring it up.

That's about it for now. There's a couple of conferences on the horizon that I may have a chance to speak at but nothing is confirmed right now. I'll let you know if things solidify. There's also some exciting new and upcoming bits of web tech that I'm itching to play with and do some little demos for, so expect to see some quickie proof-of-concept stuff in the coming months as well. In the meantime, I'm always available for questions by email, on twitter, or through this blog, and I've made a habit of trolling StackOverflow for WebGL questions

WebGL JS1K entry

As a sort of follow up to last years Itty Bitty WebGL post I decided to take a crack at actually entering the js1k competition this time around. Since the theme of the competition is "Love" I thought it would be fitting to do a minimalistic version of this awesome little "beating heart" shader.

You can see my under 1k version here.

And the not-quite-so-compressed-but-still-really-hard-to-read source code here:

c=document.getElementById('c');
c.height=300;
g=c.getContext('experimental-webgl');

b=g.createBuffer();
a=34962;
g.bindBuffer(a,b); 
g.bufferData(a,new Float32Array([0,0,2,0,0,2,0,2,2,0,2,2]),35044); 
g.vertexAttribPointer(0,2,5126,false,0,0);

p=g.createProgram();
function l(x,y){
    s=g.createShader(35633-x);
    g.shaderSource(s,y.replace(/#/g,'float ').replace(/\^/g,'vec'));
    g.compileShader(s);
    g.attachShader(p,s);
}

// See the uncompressed shader code at http://glsl.heroku.com/e#1599.2

l(0,'attribute ^2 p;void main(){gl_Position=^4(p.x-1.0,p.y-1.0,1,1);}');
l(1,'precision highp #; uniform #t;^2 r=^2(300,300);^3 h(#x,#y){#s=mod(t,1.0);s=1.0+(1.0-exp(-5.0*s)*sin(9.0*s));x*=s;y*=s;#h=abs(atan(x,y)/3.14);#d=(13.0*h-22.0*h*h+10.0*h*h*h)/(6.0-5.0*h);return mix(vec3(1.0,0,0),vec3(1.0),smoothstep(d-0.05,d,sqrt(x*x+y*y)));}void main(){^2 p=^2(-1.0+2.0*gl_FragCoord.xy/r);gl_FragColor=^4(h(p.x,p.y),1.0);}');

g.linkProgram(p);
g.useProgram(p);
g.enableVertexAttribArray(0);

u=0;
setInterval(function(){
    g.uniform1f(g.getUniformLocation(p,'t'),u+=0.02);
    g.drawArrays(4,0,6);
},16);

To be perfectly honest, I really don't care to explain the details of what's going on here. The basic setup is the same as my previous post on the subject, with the core difference being that we're now rendering a fullscreen quad. (That effectively makes this a tiny version of mr doobs GLSL Sandbox) The shader code is really just a minimal implementation of the original shader, and the only really interesting trick there is that I'm doing a string replace to simplify commonly repeated symbols like "float" and "vec".

I'm not certain that this even qualifies for the contest (since WebGL isn't enabled by default on some of the required browsers) but I had fun doing it and it was a good break from everything else going on in my life right now. (I'll be posting more on THAT shortly.)

OVERNIGHT UPDATE: So apparently I missed the line on the rules page that actually excludes WebGL from the competition (Second to last bullet under "Other things you should know"). That's a shame, but hopefully WebGL will be allowed for the next round! Doesn't matter much to me, I was just doing it for kicks anyway.

In other news, @p01 managed to cull 303 bytes out of my demo while I was sleeping which I think is deserving of a "I'm not worthy!" bow. There's some seriously clever little tricks in his version that I'm most certainly going to have to utilize the next time I try one of these! :)