This documentation is still under construction!

There's a lot of documentation here. It can be scary when you first see it. This particular document is for the hands-on person who wants to be walked through concrete examples step by step without much explanation. You'll still need to study other documents here for the details these examples leave out.

Getting started

You're going to need to get the Clockworks scripts. You'll also want to get all following files for these demos:

You can optionally download source files for the 3D models if you want to play with them.

In Sansar, you need to upload the "Disk" and "Barrel" models. For each, you need to choose the "Collision" model as the collision volume. Don't bother editing the materials. Your upload panels should look similar to this:

In all the examples below, you'll start with a copy of the "My CW controller.cs" script you downloaded and only show the small amount of code that differs in each for brevity.

I'll also have you enter commands in the chat window sometimes.

I recommend that you create a new experience/scene using the "Base Template scene" template:

Keep very little in it and use the default scene settings and it will build and start up quickly.

The first things you will add are two of the "barrel" models you just uploaded. Put them to the left of your spawn point at (-4, 0, 0.4) and (-3, 0, 0.4), respectively Name them "Clockworks - Controller" and "CW demo controller. Don't make them dynamic; static is fine.

Drop the "Clockworks - Controller" script into the same-named barrel and change its ListenToChat setting to On.

From now on, when we upload a custom control script, we'll drop it into the other barrel.

Now we're ready to walk through the demos below. Let's do it!

Simple rotator

Add a copy of the Disk model we uploaded to the scene out in front of the spawn point.

Mark it as dynamic. Drop the "Clockworks - Unit" script into it. Name its set "X" and unit "A". The script settings should look like this:

Click the "Build" button and visit the scene. Don't worry that we don't have a custom script. We don't need it yet.

In the chat window, enter the following live programming messages, hitting Enter after each:

/cw x arm a 0,4,1
/cw x start

Your disk should now be hovering out in front of you. Now this command:

/cw x prog a * s:1

Your disk should now be rotating like this:

Let's put that in a script so the scene always starts up running this program. Make a copy of the My CW controller.cs script you downloaded earlier and call it whatever you want. Edit the .ConfigureMachines() method code so it looks like this:


private void ConfigureMachines() {

    // Give the controller and units a chance to initialize
    Wait(TimeSpan.FromMilliseconds(WaitSecondsBeforeInit * 1000));

    ProgramMsg(SetName, "Arm", "A", FromVector(BasePosition), FromVector(BaseOrientation));
    ProgramMsg(SetName, "Prog", "A", "", "S:1");

    if (UpdateRate > 0) ProgramMsg(SetName, "UpdateRate", "*", "" + UpdateRate);

    ProgramMsg(SetName, "Start", "");

}
 

Upload it and drop the script into your second stationary barrel on the left; the one you named "CW demo controller". Edit its settings to look like this:

Now build and view it. It should immediately start out rotating like before without you entering any commands in chat.

Dancing rotator

Let's play with what we just created above using more live programming commands in the chat window:

/cw x prog a *   s:1 | s:-1

It should now be oscillating one full turn one way and then a full turn back the other way. Now let's change it from a rotator to a linear actuator:

/cw x actuation a L

Now let's alter the track, which defaults to (1, 0, 0). We'll make it go up and down instead.

/cw x track a 0,0,2

How about we make it rotate again but tilt the axis a bit?

/cw x actuation a R
/cw x arm a _ 20,20,0

The underscore (_) means leave the offset vector as it currently is. We only want to change the axis.

So let's move the unit in relation to its arm/axis using an offset/tilt without changing the arm/axis:

/cw x offset a   1,0,0   90,0,0

Same exact rotation, believe it or not.

Three rotators

Let's create a machine with a hierarchy of three disks connected to each other and rotating. Edit your scene and duplicate your disk twice. Set the UnitName setting for one to "B" and the other to "C". Use the Scale slider to set B's scale to 0.5 and C's to 0.25. Edit your running demo control script or create a copy with these changes:


private void ConfigureMachines() {

    // Give the controller and units a chance to initialize
    Wait(TimeSpan.FromMilliseconds(WaitSecondsBeforeInit * 1000));

    ProgramMsg(SetName, "Attach", "B", "A");
    ProgramMsg(SetName, "Attach", "C", "B");

    ProgramMsg(SetName, "Arm", "A", FromVector(BasePosition), FromVector(BaseOrientation));
    ProgramMsg(SetName, "Arm", "B", "0, 0.4, 0.16");
    ProgramMsg(SetName, "Arm", "C", "0, 0.2, 0.8");

    ProgramMsg(SetName, "Prog", "A", "", "S:1");

    if (UpdateRate > 0) ProgramMsg(SetName, "UpdateRate", "*", "" + UpdateRate);

    ProgramMsg(SetName, "Start", "");

}
 

Drop this updated script into the appropriate barrel off to the side. Then edit its settings to be the same as before. Build and visit your scene.

Looks like the previous demo, right? The two new disks look like they are glued to the first one. They are stationary relative to it. But notice that they are, in fact, moving? So now let's give them their own programs:

/cw x prog a *   s:1/4; e:L
/cw x prog b *   p:4; s:-1/8; e:be
/cw x prog c *   p:2; s:1 | s:-1

That's different, right? little too fast to see? Let's slow everything down from the default of 60 BPM to 30 BPM:

/cw x bpm 30

How about we change their arms/axes?

/cw x arm b   0,0.6,0.2   -45,0,0
/cw x arm c   0,0.3,0   90,0,0

Let's update our script to do this:


private void ConfigureMachines() {

    // Give the controller and units a chance to initialize
    Wait(TimeSpan.FromMilliseconds(WaitSecondsBeforeInit * 1000));

    ProgramMsg(SetName, "Attach", "B", "A");
    ProgramMsg(SetName, "Attach", "C", "B");

    ProgramMsg(SetName, "Arm", "A", FromVector(BasePosition), FromVector(BaseOrientation));
    ProgramMsg(SetName, "Arm", "B", "0, 0.6, 0.2", "-45, 0, 0");
    ProgramMsg(SetName, "Arm", "C", "0, 0.3, 0", "90, 0, 0");

    ProgramMsg(SetName, "Prog", "A", "", "S:1/4; E:L");
    ProgramMsg(SetName, "Prog", "B", "", "P:4; S:-1/8; E:BE");
    ProgramMsg(SetName, "Prog", "C", "", "P:2; S:1 | S:-1");

    if (UpdateRate > 0) ProgramMsg(SetName, "UpdateRate", "*", "" + UpdateRate);

    ProgramMsg(SetName, "Start", "", "30");

}
 

Upload this new script and drop it into the demo controller barrel again. Don't forget to update its settings as before. Build and view the scene. It should look the same as before you left.

Let's make a simplified carousel. Clear the animated disks from your scene, leaving only the two control drums. Set out two disks and give them both scales of 2.0. Be sure to make them dynamic and add the "Clockworks - Unit" script to each. Set out six drums and do the same, but leave their scales at 1.0.

Here are the settings for the first disk:

Use these same settings for all 8 objects, but the UnitName settings should be as follows:

Now for the configuration script code:


private void ConfigureMachines() {

    // Give the controller and units a chance to initialize
    Wait(TimeSpan.FromMilliseconds(WaitSecondsBeforeInit * 1000));

    int HorseCount = 6;

    ProgramMsg(SetName, "Attach", "Boom.$.B", "Boom.$.A");
    ProgramMsg(SetName, "Attach", "Boom.$.C", "Boom.$.B");
    ProgramMsg(SetName, "Attach", "Carousel.$.Base", "Boom.$.C");
    ProgramMsg(SetName, "Attach", "Carousel.$.Top", "Carousel.$.Base");

    for (int I = 1; I <= HorseCount; I++) {
        ProgramMsg(SetName, "Attach", "Carousel.$.Horse." + I, "Carousel.$.Base");
    }

    ProgramMsg(SetName, "Arm", "Boom.$.A", FromVector(BasePosition), FromVector(BaseOrientation));
    //ProgramMsg(SetName, "Arm", "Carousel.$.Base", FromVector(BasePosition), FromVector(BaseOrientation));
    ProgramMsg(SetName, "Arm", "Carousel.$.Top", "0, 0, 2", "0, 0, 0");

    Vector HorseAxis = new Vector(0, 0, 0.75f);
    Vector HorseArm = new Vector(1.2f, 0, 0);
    float HorseAngle = -Mathf.TwoPi / HorseCount;

    for (int I = 1; I <= HorseCount; I++) {
        Quaternion Rot = Quaternion.FromEulerAngles(new Vector(0, 0, I * HorseAngle));
        ProgramMsg(SetName, "Arm", "Carousel.$.Horse." + I,
            FromVector(HorseAxis + HorseArm.Rotate(ref Rot)),
            "0, 0, " + (I * HorseAngle * Mathf.DegreesPerRadian)
        );
    }

    ProgramMsg(SetName, "Actuation", "Carousel.$.Horse.*", "L");
    ProgramMsg(SetName, "Track", "Carousel.$.Horse.*", "0, 0, 0.5");

    ProgramMsg(SetName, "Prog", "Carousel.$.Base", "", "S:1/10; E:L");

    for (int I = 1; I <= HorseCount; I++) {
        ProgramMsg(SetName, "Prog", "Carousel.$.Horse." + I, "", 
            "P:" + HorseCount/2 + "; R:0; FR:" + I + "| P:1; S:1 | S:-1"
        );
    }

    if (UpdateRate > 0) ProgramMsg(SetName, "UpdateRate", "*", "" + UpdateRate);

    ProgramMsg(SetName, "Start");

}
 

Upload, add to the demo controller, and edit its settings. This time, with a lower base position:

Build and visit to see:

There's a lot going on here, but this is a hands-on demo with little explanation. Still, let me point out a few important things. First, we compute an angle representing 1/6 of a pie wedge and use this angle to define the arm and axis for each of the 6 horses. Second, we place a one-time delay at the start of each horse's animation program that varies for each. That's why they rise and fall in a sort of wave pattern instead of all at once.

Oh, were you wondering about "Boom.$.A", ".B", and ".C"? They are extra segments in the hierarchy above the whole carousel. They aren't doing anything just yet, so let's have some fun with them. Try these live-programming commands:

/cw x arm boom.$.c   3,0,0   -90,0,0
/cw x arm boom.$.b   _   90,0,0
/cw x prog boom.$.a *   s:1/10; e:l
/cw x prog boom.$.b *   p:1/7; s:1/10 | s:-1/10
/cw x start _ 100
/cw x start _ 200

The kids will love this ride.

Car

How about a simple car? Clear away the drums and disks and start fresh with one drum and 4 disks. The drum should have a scale of 3.0 and the disks just 1.0. Here's our initial setup script:


private void ConfigureMachines() {

    // Give the controller and units a chance to initialize
    Wait(TimeSpan.FromMilliseconds(WaitSecondsBeforeInit * 1000));

    ProgramMsg(SetName, "Attach", "Road.$.Y", "Road.$.X");
    ProgramMsg(SetName, "Attach", "Road.$.Z", "Road.$.Y");
    ProgramMsg(SetName, "Attach", "Road.$.Yaw", "Road.$.Z");
    ProgramMsg(SetName, "Attach", "Road.$.Roll", "Road.$.Yaw");
    ProgramMsg(SetName, "Attach", "Road.$.Pitch", "Road.$.Roll");
    ProgramMsg(SetName, "Attach", "Car.$.Body", "Road.$.Pitch");

    ProgramMsg(SetName, "Attach", "Car.$.Wheel.LF", "Car.$.Body");
    ProgramMsg(SetName, "Attach", "Car.$.Wheel.RF", "Car.$.Body");
    ProgramMsg(SetName, "Attach", "Car.$.Wheel.LR", "Car.$.Body");
    ProgramMsg(SetName, "Attach", "Car.$.Wheel.RR", "Car.$.Body");

    ProgramMsg(SetName, "Arm", "Road.$.X", FromVector(BasePosition), FromVector(BaseOrientation));

    ProgramMsg(SetName, "Arm", "Car.$.Wheel.LF", " 0.6,  0.8, -0.5", "-90, 0, 0");
    ProgramMsg(SetName, "Arm", "Car.$.Wheel.RF", " 0.6, -0.8, -0.5", "-90, 0, 0");
    ProgramMsg(SetName, "Arm", "Car.$.Wheel.LR", "-0.6,  0.8, -0.5", "-90, 0, 0");
    ProgramMsg(SetName, "Arm", "Car.$.Wheel.RR", "-0.6, -0.8, -0.5", "-90, 0, 0");

    ProgramMsg(SetName, "Arm",    "Car.$.Body", "", "90,-90,180");
    ProgramMsg(SetName, "Offset", "Car.$.Body", "", "90,-90,180");

    ProgramMsg(SetName, "Actuation", "Road.$.X", "L");
    ProgramMsg(SetName, "Actuation", "Road.$.Y", "L");
    ProgramMsg(SetName, "Actuation", "Road.$.Z", "L");
    ProgramMsg(SetName, "Track", "Road.$.X", "10,0,0");
    ProgramMsg(SetName, "Track", "Road.$.Y", "0,10,0");
    ProgramMsg(SetName, "Track", "Road.$.Z", "0,0,10");
    ProgramMsg(SetName, "Arm", "Road.$.Yaw", "0,0,0", "0,0,0");
    ProgramMsg(SetName, "Arm", "Road.$.Roll",  "0,0,0", "0,90,0");
    ProgramMsg(SetName, "Arm", "Road.$.Pitch", "0,0,0", "90,0,0");

    ProgramMsg(SetName, "Prog", "Car.$.Wheel.*", "", "S:1; E:L");

    if (UpdateRate > 0) ProgramMsg(SetName, "UpdateRate", "*", "" + UpdateRate);

    ProgramMsg(SetName, "Start");

}
 

And the settings for that script:

And we have our starting point:

Let's give it simulated terrain to drive on:

/cw x prog road.$.x *   p:1/2; sa:1/2; s:1/2; e:eo | s:-1/2; e:ei | s:-1/2; e:eo | s:1/2; e:ei
/cw x prog road.$.y *   p:1/2; s:1/2; e:ei | s:1/2; e:eo | s:-1/2; e:ei | s:-1/2; e:eo
/cw x prog road.$.z *   p:1/2; | s:1/10 | s:-1/10 |
/cw x prog road.$.yaw *   p:1/8; s:1; e:l
/cw x prog road.$.pitch *   p:1; | | s:1/10; e:l | s:-1/10; e:l | s:-1/10; e:l | s:1/10; e:l | |
/cw x prog road.$.roll *   p:2; sa:-1/100; s:1/50 | s:-1/50

Yee haw!