Sunday, July 15, 2012

Walking Avatar

‹prev | My Chain | next›

This whole walking Gladius avatar think has taken a bit longer than expected. Still, I think I made some good progress last night. I have the right leg of the avatar moving back-and-forth:


I wound up with a poor algorithm for implementing the back and forth—effectively hard coding when to swing the leg in the opposite direction. I had originally planned on leaving it as-is, preferring working to elegant. But the ugly started to wear on me.

And it also occurred to me that, if I need to oscillate between 1 and -1, that is what sines and cosines are for. So I switch my game task function to use this instead:
        var task = new engine.FunctionTask( function() {
          var entity = space.findNamed("right-leg-frame")
            , transform = entity.findComponent("Transform")
            , amplitude = Math.sin(space.clock.time/500);

          transform.setRotation([0,0, amplitude*(Math.PI/8)]);
        });
        task.start();
The problem with this approach is that the leg swings back and forth like a pendulum (slowing at the extremes), not so much like a leg.

I eventually stumble on an algorithm for more of a triangle wave. which seems to do the trick:
        var task = new engine.FunctionTask( function() {
          var entity = space.findNamed("right-leg-frame")
            , transform = entity.findComponent("Transform")
            , t = space.clock.time
            , w = 500
            , amplitude = (w/2 - Math.abs((t % (2*w)) - w))/w;

          transform.setRotation([0,0, amplitude*(Math.PI/8)]);
        });
The arms are a little more difficult because of the angles. To handle these, I add two frames of reference: one for the initial shift and rotation relative to the body and a second that I can use to implement simpler swinging:
        space.add(new engine.Entity("right-arm-frame1",
          [
            new engine.core.Transform([0.5, 0, 0], [0, -Math.PI/8, Math.PI/3])
          ],
          ["avatar"],
          space.findNamed("body")
        ));

        space.add(new engine.Entity("right-arm-frame",
          [
            new engine.core.Transform()
          ],
          ["avatar"],
          space.findNamed("right-arm-frame1")
        ));

        space.add(new engine.Entity("right-arm",
          [
            new engine.core.Transform([0, -0.5, 0]),
            new cubicvr.Model(resources.cylinder_mesh, resources.blue_material)
          ],
          ["avatar"],
          space.findNamed("right-arm-frame")
        ));
At this point, my game task() is doing more than tranforming one limb. It has to coordinate four extremities.

So I move the arm and leg transforms out of the game task(), leaving task() to just swing the arms:
        var w = 500
          , arm1 = space.findNamed("left-arm-frame").findComponent("Transform")
          , arm2 = space.findNamed("right-arm-frame").findComponent("Transform")
          , leg1 = space.findNamed("left-leg-frame").findComponent("Transform")
          , leg2 = space.findNamed("right-leg-frame").findComponent("Transform");

        var task = new engine.FunctionTask( function() {
          var t = space.clock.time
            , amplitude = (w/2 - Math.abs((t % (2*w)) - w))/w;

          leg1.setRotation([    amplitude*(Math.PI/8), 0, 0]);
          leg2.setRotation([ -1*amplitude*(Math.PI/8), 0, 0]);

          arm1.setRotation([ 0, 0, -1*amplitude*(Math.PI/8)]);
          arm2.setRotation([ -1*amplitude*(Math.PI/8), 0, 0]);
        });
After rotating the camera a bit for a better view, I see the arms and legs move:


And do so in unison:


That will do as a nice stopping point for tonight. The chain of reference frames is both handy (for calculations) and a pain (for added code). I will have to mull that over. That aside, I'm pretty happy with the implementation as well as the result.

Day #448

No comments:

Post a Comment