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:
Center the waist between the feet
Provide a slider to adjust the position of the waist
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:
The position of left foot
The position of right foot
The position of the waist
The axis you want to center on X or Z
The name of the slider you will use
The slider channel that will adjust the position
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.
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
//////////////////////////////////////
//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
//////////////////////////////////////
//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
//////////////////////////////////////
//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 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
//////////////////////////////////////
//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 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
//////////////////////////////////////
//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 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
//////////////////////////////////////
//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 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
//////////////////////////////////////
//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
//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
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.
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
//////////////////////////////////////
//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