User:Wmonroe4/Motion controllers

From Sirikata Wiki
< User:Wmonroe4
Revision as of 19:27, 9 August 2011 by Wmonroe4 (talk | contribs) (Collision controller)
Jump to navigation Jump to search

The motion controller library in std/movement/motion.em is a simple way to program common types of motion for presences in Sirikata without reinventing the wheel. This tutorial is an introduction to show you how to use the library to script interesting movement in a minimal amount of code.

As I work on this, I'll also be working on the automatic documentation for the API reference, so keep an eye on that ([1]) as well.

Creating a motion controller

At its most basic, setting up a motion controller is as simple as:

system.require('std/movement/motion.em');
// ...
new motion.SomeTypeOfMotion(presence, extra arguments);

As a simple example, with no extra arguments:

new motion.Gravity(presence);

This will set presence falling at about 9.81 meters per second squared downward, until the end of time.

suspend and reset

If this is all you want, then that one line is all you need. If, however, you may want to release the presence from the hold of gravity, or change the direction or acceleration of gravity, you're going to need to save the controller object for later:

var gravity = new motion.Gravity(presence);

Then when you need to, you can stop gravity:

gravity.suspend();

and restart it later:

gravity.reset();

Every motion controller has suspend and reset methods. suspend makes the controller stop acting on the presence, and reset makes it start again. (These methods are not well tested, so let me know if you think you've found a bug in one.)

Extra arguments and fields

Every motion controller also has fields that you can modify at any time to change the behavior of the controller. Gravity has two: presence and accel. By reassigning presence, you can change which presence the gravitational acceleration applies to, and by changing accel (a vector), you can change how fast the presence falls:

gravity.accel = <0, -5, 0>;

or even make the presence fall sideways:

gravity.accel = <4, 0, 0>;

These fields are initialized from arguments to the constructor, so if you simply wanted gravity to be in a bizarre direction to begin with, you could just create it like this:

var gravity = new motion.Gravity(presence, <4, 2, -9>);

The constructor arguments are much more versatile than the dynamic fields, so it can be to your advantage to set up the properties you want in the constructor. Above, you saw that the accel argument to the gravity constructor is optional; if you leave it out, it will assume a reasonable default (down, at standard Earth acceleration). If instead you just want really weak gravity, the constructor can simply take a number:

var gravity = new motion.Gravity(presence, 0.8);

Down is still implied. The dynamic accel field isn't as smart: unlike the constructor argument, the accel field has to be a vector, so assigning 0.8 to gravity.accel won't work—you have to explicitly assign <0, -0.8, 0>.

Using the Collision controller

The motion.Collision controller is probably the most useful of the controllers currently in motion.em, but it involves many complicated interactions, so it can be tricky to get right. Just constructing the controller is the hardest part:

system.require('std/movement/motion.em');
system.require('std/movement/collision.em');
// ...
var collision = new motion.Collision(myPresence, coll.TestSpheres(aBunchOfVisibles), coll.Bounce(0.8));

You need to create a new controller for every presence you want to react to collisions, putting the presence in the first argument to the constructor.

The second argument is the test function, which specifies how the presence detects collisions. In this case, we're using coll.TestSpheres(visibles), which detects collisions with bounding spheres of a specified list of visibles. The "hardest part" I was referring to isn't even in the code above—it's coming up with that list of visibles. Although it's tempting to use system.getProxSet, it's almost always better to use a list that you control more directly, preferably by having created each of the presences the list refers to, if only because getProxSet might include a terrain and other large objects that you don't want to treat as spheres (I've seen other, less predictable problems with getProxSet).

The third argument is the response function, which says how the presence reacts to a collision. Here we use the predefined response template coll.Bounce(elasticity), which causes the presence to bounce with a certain fraction of its original momentum. The 0.8 is optional (defaults to 1), but don't forget the parentheses! Simply passing in coll.Bounce won't do what you want—you need to call it with an optional parameter, which it uses to construct and return a customized response function for you. You then pass this returned function in to the Collision constructor. coll.TestSpheres follows the same pattern; don't forget to call it!

Executing the last line above many times, with a different myPresence each time, will set up a bunch of presences to bounce off the visibles you specified. If the presences themselves are among the visibles, they will bounce off each other as well!