HOME   |   TUTORIALS   |   DEVELOPMENT   |   GALLERY   |   GAMES   |   ABOUT US 
  

M:Script Tutorial Part 1: CenterWaist

By Max Glick

In the "Breaking Down Exp's" section of the manual, you were introduced to a complex expression that centers a character's hips between its feet. You did read that section, right? Yes, of course you did. Here's a link just in case: "Expressions: Breaking Down & Deciphering". With a Messiah script, we can make this complex expression easier to reuse and add some additional functionality directly into the script.

First we will examine an example of waist centering done using traditional expressions.

In this image we see 5 expressions, the first uses the X-axis value of the foot goals and positions the waist between them on that axis. The second does the same for the Z-axis. The third expression is used with a slide, in this case "SLD_Hip:xpos" to shift the hips over the foot goal on the X-axis. A value of .5 would center the hips and a value of 0 or 1 would place the hips over the respective foot. The fourth expression does the same thing, but for the Z-axis. The fifth expression, using Keyfader() and another slider channel to turn the effect on and off, would remove all procedural motion applied to the hips prior to this expression in the list.

From what we observed in this example, we will now create a solution using M:Script that will require only one entry per axis, as well as a more specific solution for removing the effect.

The first task is to define the goals of this script:

  1. Center the waist between the feet
  2. Provide a slider to adjust the position of the waist
  3. Provide a slider to turn off the script so we can key frame the waist when needed.

The second task is to determine what this script needs to return:

  • The position of the hips

Finally, we need to define what variables to provide to the script to accomplish these goals:

  1. The position of left foot
  2. The position of right foot
  3. The position of the waist
  4. The axis you want to center on X or Z
  5. The name of the slider you will use
  6. The slider channel that will adjust the position
  7. The slider channel that will turn the script on and off.

Now that we know what we're doing, we can start writing the script. See, it's not so hard so far. Once we start making the script, you'll see it's not much different then writing expressions. So now we can start our script.

First we define that this is a Messiah script:

//#messiahscript

Then we need to identify the script so we know what it is:

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

Now we will need the default Messiah header for the preprocessor. This defines channels and axis. We can use simple references for them like "xpos" or the integer 0 to access the position of an object on the X-axis, when using the motchan() function. In this example, we will use the integer value, to define the axis for our main objects.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////
#include <messiah.h>

Next we must define our function. We will name this function "main" so it will be the default function when the script is run. Now declare the type of value the function will return. In our case, we decided that we are returning the position of the waist centered on a single axis, so we know this will be of type "double". The 7 variables being sent to the script will need to be identified and named. These are listed at the beginning.

So first we write the function:

double main(){

}

Now we add in the 7 variables we are sending:

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

}

So to identify and name the 7 variables, we first declare their type and then we give them a name to reference them in the script.

  1. The left foot - type object - reference name obj1
  2. The right foot - type object - reference name obj2
  3. The waist to be centered - type object - reference name objC ('object Centered' shortened)
  4. The axis you want to center on X or Z - type int - reference name curAxis ('current Axis' shortened)
  5. The name of the slider you will use - type object - reference name objSld ('object Slider' shortened)
  6. The slider channel that will adjust the position - type string - reference name bias
  7. The slider channel that will turn the script on and off - type string - reference name power

So our script now looks like this:

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

}

Now save this file as fb_waistCenterTUT.msa and remember to save frequently as you add to the script. For script name conventions, I prefer to start the name with a unique identifier so I know it is one of my scripts. Here I have used "fb" as the prefix, this identifies the script as a FullBurner script. This also helps in case someone else writes a script called waistCenter. After the prefix, I place a "_" to separate it from the name of the script. If the file contains one main script, I'll name the file for what the script does. If the script is a collection of functions, I'll give it a general name like "fb_utility.msa".

Our next step is to define the variables we will need to calculate our results. We will need to define the type of variable, give it a name to reference and finally a short comment to identify what the variable is used for. Since we will be using values from specific channels of the objects we are passing to our function, we will need to define variables to hold the values derived from those objects. This is done because Messiah:Script does not support Bracket Expressions. Instead, there are pre-defined functions for retrieving a channels value. So first we will add a comment marking the beginning of our variables definition, and then define the value holders for the objects.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power

}

Next we need to add two variables: to store the results of the calculations, and the variable that will be returned with the final value.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value


}

Now we need to find the values of the objects we are using. To find the value, use the function motchan(). Since the sliders use a string to define their channel, we will need to use the chanindex() function first to determine the actual channel we want the value for, and then use the motchan() function with a string value to get the value. Both of these functions are discussed in the "Bracket Expressions" section of the manual. For the main objects we are passing to the script, we will use the motchan() function with integer values. So first, let's find the real channels for our 2 sliders. To get the channel from the slider, we pass to chanindex() the slider object reference and the string that identifies the slider channel. This will give us the true channel ID.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);


}

Now we can find the values for the sliders and other objects using motchan() function. To get these values, we pass to motchan() the object we want to reference, the string name or channel ID of the value we want and the point in time we want this value for. In this case, NOW.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);


}

Our next step is to add the true functionality of our script. First, we will check and see if the on/off slider is in the off position. If it is, we'll just have the script return the current keyframed value. To do this, we will check to see if the on/off slider is set to 0, the off position. If so, we'll place the current value for the waist position into the value we will return. Also add an "else" option for us to place our centering calculations into during the next step.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);

  if (sldPowerValue == 0){
    newPosition = objCValue;

  } else {

  }


}

Now will add our centering code. This is the same equation that was used in the "Expressions: Breaking Down & Deciphering" section. The value from this equation will be placed in the centerValue variable.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);

  if (sldPowerValue == 0){
    newPosition = objCValue;

  } else {
    //find the centered value
    centerValue = (((obj1Value+obj2Value)/2)*(1-(abs(sldBiasValue-.5)*2))) + (obj1Value*((max(sldBiasValue,.5)*2)-1)) + (obj2Value*((abs(min(sldBiasValue,.5)-.5)))*2);

  }

}

Now that we have our centered position, we want to have the on/off slider provide a smooth transition between the script-based position and the keyframed position. As the function is right now, there would be a jump when the script is turned off. We could use keyfader() for this, but in this example we will focus on only fading the position of the current channel from the effects of this script, not all prior procedural positioning. To do this, we will compare the original keyframed position to the new, centered position, and blend between the 2 based on where the on/off slider is positioned, which we are referencing as sldPowerValue, between 1 and 0. This resulting value we will place into the newPosition variable, which will be returned by the function.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);

  if (sldPowerValue == 0){
    newPosition = objCValue;

  } else {
    //find the centered value
    centerValue = (((obj1Value+obj2Value)/2)*(1-(abs(sldBiasValue-.5)*2))) + (obj1Value*((max(sldBiasValue,.5)*2)-1)) + (obj2Value*((abs(min(sldBiasValue,.5)-.5)))*2);

    newPosition = objCValue - (( objCValue - centerValue)*sldPowerValue);   }

}

This equation takes the distance difference between objCValue and centerValue: Step1 = objCValue - centerValue

It then multiplies this by the slider value to find the position between the two values: Step2 = results of step1* sldPowerValue

Then this final result is subtracted from the position of the object: Step3 = objCValue - results of step2

Now all we have to do is return the resulting value of the newPosition variable.

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);

  if (sldPowerValue == 0){
    newPosition = objCValue;

  } else {
    //find the centered value
    centerValue = (((obj1Value+obj2Value)/2)*(1-(abs(sldBiasValue-.5)*2))) + (obj1Value*((max(sldBiasValue,.5)*2)-1)) + (obj2Value*((abs(min(sldBiasValue,.5)-.5)))*2);

    newPosition = objCValue - (( objCValue - centerValue)*sldPowerValue);   }

  return( newPosition );
}

And there we have it. This script can be easily reused. To use this script, we'll take a rigged character and add a new expression. We'll call this CenterX. In the expression section, we'll add the function "fb_waistCenter(GOAL_footL,GOAL_footR,bone_Waist,"xpos",waistSlider,"biasXZ","on__off")" and then set the channel to "bone_Waist" x channel. Now we'll copy this expression and change it to the Z-axis. Rename it CenterZ, and change the function to "fb_waistCenter(GOAL_footL,GOAL_footR,bone_Waist,"zpos",waistSlider,"biasXZ","on__off")" and then set the channel to "bone_Waist" z channel. Now the script is working in our scene.

One last thing you may want to add is a "help" function. To do this, we can use the MessageBox function to display usage information for the script if we forget what variables are needed. After everything else in our script, including the closing } for the main function, we will add this function.

string help()
{
  string msg;
  msg = "Usage: fb_waistCenterTUT(leftObject, rightObject, centerObject, 0||2 [x||z axis], slider, ''biasChannel'', ''powerChannel'')";
  MessageBox( msg, 0 );
  return( msg );
}

To us this function, we just type "fb_waist.help()" into the command line and press return. This is just like the HelloWorld function that was described in the previous section "Writing your first script".

Our final Script:

//#messiahscript
//////////////////////////////////////
// fb_waistCenterTUT() rev 2.0
//
// By Max Glick 8/12/2002
//////////////////////////////////////

#include <messiah.h>

double main(object obj1, object obj2, object objC, int curAxis, object objSld, string bias, string power){

  //Default variables
  double obj1Value;  //value for object 1
  double obj2Value;  //value for object 2
  double objCValue;  //value for the object to center
  double sldBiasValue;  //value of bias slider
  double sldPowerValue;  //value of power slider
  int chnBias;  //channel name to find channel ID with for Bias
  int chnPower;  //channel- name to find channel ID with for power
  double centerValue;  //base center value between object 1 and object 2
  double newPosition  //Return value

  //get slider channels
  chnBias = chanindex(objSld, bias);
  chnPower = chanindex(objSld, power);

  //get slider values
  sldBiasValue = motchan(objSld, chnBias, NOW);
  sldPowerValue = motchan(objSld, chnPower, NOW);

  //get current position
  obj1Value = motchan(obj1, curAxis, NOW);
  obj2Value = motchan(obj2, curAxis, NOW);
  objCValue = motchan(objC, curAxis, NOW);

  if (sldPowerValue == 0){
    newPosition = objCValue;

  } else {
    //find the centered value
    centerValue = (((obj1Value+obj2Value)/2)*(1-(abs(sldBiasValue-.5)*2))) + (obj1Value*((max(sldBiasValue,.5)*2)-1)) + (obj2Value*((abs(min(sldBiasValue,.5)-.5)))*2);

    newPosition = objCValue - (( objCValue - centerValue)*sldPowerValue);   }

  return( newPosition );
}


string help()
{
  string msg;
  msg = "Usage: fb_waistCenterTUT(leftObject, rightObject, centerObject, 0||2 [x||z axis], slider, ''biasChannel'', ''powerChannel'')";
  MessageBox( msg, 0 );
  return( msg );
}

Here is an example of the script in the expression list.

Script Usage