12 December 2007

Exobotics!

We at Exopolis just launched this today:



This is a hybrid Flash/Flex app that allows you to select robot parts (including a heart, which represents a musical track) and assemble them into a dancing robot which you control with the mouse and keystrokes. I have to say—I spent hours making this and it's still fun to me.

I'd actually started something similar back in Flash 4, if you can believe that. Yes, before true ActionScript. I had to manually create a sine lookup table. It was fun.

After a couple of false starts in intervening versions of Flash, I finally had my greatest success with a Flex version. Dynamic binding makes this sort of thing so much easier.

Basically, the way the robots work is that each joint is associated with a type of object called a Locator. Minimally, a Locator stores a point, dispatches updates whenever that point moves, and allows that point to be constrained. A Locator can be created as simply as:

<Locator id="myLocator" x="100" y="100"/>

Of course, that's not a very interesting Locator, since it doesn't move. Moving a Locator can be done in one of two ways. One way is to extend the basic Locator class, creating a specialized type of Locator. As an example, one of these is StageMouseLocator:

<StageMouseLocator id="mouseLocator"/>

The x and y properties of this Locator always correspond to the position of the mouse. If I wanted to bind another Locator to this, I could do it like so:

<StageMouseLocator id="mouseLocator"/>
<Locator id="myLocator" x="{mouseLocator.x}" y="{mouseLocator.y}"/>

Voila, now myLocator follows the mouse. Why would I want to do this when mouseLocator already does that? Good question. I wouldn't. But now I can do stuff like this:

<StageMouseLocator id="mouseLocator"/>
<Locator id="myLocator" x="{mouseLocator.x + 100}" y="{mouseLocator.y}">
    <constraint>
        <constrain:VerticalConstraint minY="100" maxY="500"/>
    </constraint>
</Locator>

Now it will always be 100 pixels to the right of the mouse, but it will constrain the vertical position to between 100 and 500 pixels in the display's coordinate system. You can see how this sort of binding with modification can lead to more complex behavior.

Another way to control a Locator object is by using an (aptly-named) Controller object. One such object is a SineController object:

<control:SineController id="beatController" speed="0.25" context="{this}"/>

This keeps an angle value, updates it on every frame (incrementing by 0.25 radians) and makes the sine value available through a property called sine. So if I wanted a controller that followed the mouse but oscillated up and down, I could do this:

<StageMouseLocator id="mouseLocator"/>
<control:SineController id="beatController" speed="0.25" context="{this}"/>
<Locator id="myLocator" x="{mouseLocator.x}" y="{mouseLocator.y + beatController.sine * 50}"/>

Of course, I have other types of Controller (StepController, which extends SineController; RandomController; ClickController; etc.), other types of Locator (JumpLocator, JointLocator, InterpolatedLocator, ModeLocator, etc.), and other types of constraints (DistanceContraint, RectangularConstraint, etc.). All of these I blend to create a basic skeleton of points.

Once I have the skeleton, I can attach various visible objects call Connector objects, each of which connect to a number of points and have rules for "squash & stretch". The robots all use MovieClipConnector objects, which each link a Flash movie clip to one or two connectors. I also have DrawnConnector, ImageConnector, etc., and hopefully I can use those on future projects.

I had actually started to make this as an application where you would sketch your own character and it would animate according to a skeleton, but this turned out to be much too processor-intensive, at least for now. Fortunately, one of our senior designers, Chuck Chugumlung, had this idea for a dancing robot factory and he and some other designers (Dan Duran, Jancy Liu), went to town.

Anyway, enough geekery. Go make some robots dance!

6 comments:

  1. We're using a 8192 element Sine table in place of PowerPC intrinsic functions. It wins hands down for speed when you're using it frequently, but I'm not sure if it's actually better for sporadic usage because the penalty from an L2 miss is pretty severe.

    Er.. I mean "Dancing robots rock"

    ReplyDelete
  2. Actually, I tried a lazily-instantiated sine table with even fewer elements than that, and noticed no improvement in speed. Of course, maybe it's because I only calculate a couple of sine waves per frame....

    What I really need is optimized point interpolation. And I need to refactor the whole thing with optimization in mind, really.

    I mean, YEAH GET DOWN!

    ReplyDelete
  3. Obviously, all the techy stuff is a bit confusing to me, but I do love those dancing robots!

    ReplyDelete
  4. The speedup from a Sine table probably isn't going to be noticeable unless you're doing hundreds or thousands per frame.

    ReplyDelete
  5. Awesome! Now do it with hominids. You could call it:

    Party (Pilt)down!

    ReplyDelete
  6. Keezey, you're pretty much my hero

    ReplyDelete