In this manual, we'll talk about the Spline Editor.
With Spline Editor, you could make a spline in 3D-space:
Combine with constraint system to drive object movement, e.g.: FollowPath constraint;
Retrieve data on demand from the spline path in run-time;
Here's a GIF animation that demostrates the usage of "Spline + FollowPath" to move object.
Okay, now let's get started, ლ(╹◡╹ლ)
Let's start with the very basic.
Currently we've these Spline implementations available:
Uniform Catmull-Rom Spline;
Centripetal Catmull-Rom Spline
And to create a spline, just add one of the available spline MonoBehaviour.
Once you added the spline MonoBehaviour, you will see it in the scene like this:
Good, we've just made the first spline here. :D
In the next page, we'll see how to make the spline more curvy.
Let's continue with what we've done in the previous page.
The spline is drawn in the scene as long as you have the GameObject with spline behaviour in selection. Developers could click the blue control points(abbr. CP) and drag them around to modify the spline.
One thing to note that, if you click another object in the scene, it will remove the SplineGO (Spline GameObject) from the selection, and stop editing the spline. This is troublesome as it's hard to not click on other objects.
To handle this, Spline editor will prevent deselecting current SplineGO by clicking on other object, if you've selected a control point. So if you need to select other objects when you're editing spline, you could click on hierarchy window to select other objects, or click "ESC" to deselect current control points.
Enough foreword, let's see how to manipulate the spline.
The image below shows the inspector GUI of Catmull-Rom Uniform Spline (the Catmull-Rom Centripetal spline's GUI is the same as this one).
Add Point: insert a new CP after current selected one; if current selected CP is the last one, will add the new CP along the tangent direction with average distance of segment. the selection will move to the newly created CP after this operation.
Del Point: delete current selected CP, move the selection to the next CP if possible;
Make Cycle/Break Cycle: use this option to control whether the spline should form a closed cycle;
The second line provides interface to select CP directly; the first CP has index of 0; Clicking arrow button to select the prev/next CP.
The third line is used to edit the position of selected CP;
The fourth line is to specify the "tilt" angle, which is used to tweak the "UP" direction around the CP; it makes spline to roll specified angle additively, which affects the existing "UP" direction;
The spline inherently defines "FORWARD" with derivative, but it cannot defines "UP" by itself, it needs we impose some arbitrary rule to define "UP". However, in order to define the object movement with spline, "UP" is a must. (consider a jet flying straight forward and rolling, you cannot define it with spline without "UP" control)
Skele's spline editor provides two method to define "UP": "Y-up" & "Minimum";
"Y-up" could provide better control, as long as your spline doesn't have strict up/down segment;
"Minimum" could ensure smooth "UP" direction change, but might be harder to manipulate than Y-up.
Now you know how to add/delete CP, the only thing left is to drag them around.
Click any control point to select, and drag it around, the spline will respond to the change.
Be sure to remember to use Ctrl+Z/Y to undo/redo.
The "Settings" sections defines some extra parameters that you could ignore in most situations.
Point/Seg: This defines resolution of spline, with higher value, the spline drawn in editor will be more smooth, and the UP direction calculation will be closer to theory value; just keep it at default value.
Twist Method: This defines the method to calculate "UP" direction, both're okay to select.
Draw Arrow: enable to draw the arrow lines in scene view;
Draw Up: enable to draw the up direction in scene view;
Arrow LineLen: define the length of arrow line, in the world unit.
Draw points: enable to draw the Control Points;
Point size: define the drawn size of Control Points, in world unit.
Look At dist: define the distance between camera and focus point when use "C" to focus on ControlPoint;
T slider: expand the foldout to enable follow mode, and use left/right arrow to follow around the spline (this one is FUN, you could ride your spline like riding a rollercoaster, you should have a play with it)
Spline editor provides several hotkeys that might help improve your workflow.
I: Insert new control point
X: Delete selected control point;
C: Set camera focus on selected control point;
ESC: deselect current control point;
Constraint System Manual
In Skele v1.9 we introduced the Constraint System.
Constraints are special MonoBehaviours that connect multiple Transform components' properties (position/rotation/scale) in various way and can execute in edit-mode / play-mode / run-time.
All constraints only affect its owner GameObject. (Except the IK constranit which affect the whole bone link)
Each constraint has an "influence" value ranged from [0,1], it controls the percentage of effect this constraint has on its owner, from "no-effect" to "full-effect".
Every constraint is managed by some "ConstraintStack" MonoBehavioiur, which is used to control the evaluation order and activation state. One GameObject can have one constraint stack, one constraint stack can have multiple constraints. The constraints are evaluated from top to bottom in stack. we can achieve many useful motions with combination of multiple constraints. (e.g.: we could make the player's position.xz to be the average of his two feet's position.xz, with two CopyPosition constraint and influence setting. Sounds like RootMotion?)
Skele's constraint system is made to be similar to Blender's constraints system, you could also refer to the Blender's constraints reference manual.
NOTE: most of the constraints don't work with non-uniform scaling, please keep the target transform and ancestors in uniform scaling (i.e.: localscale.x == localscale.y == localscale.z )
Here's an intermediate example of constraint usage: We used CopyPosition, Floor, IK constraints to make walking animation. (click to open gif)
You can find examples scenes for each constraint type in the Skele installation directory at "Assets/Skele/_ExampleScenes/Constraints", check the description and play with each example to understand how it works.
In the next pages, we'll explain each constraint's usage and options.
Okay, now let's get started! ლ(╹◡╹ლ)
There're some operations that all constraints share, we would explain in this page together.
ConstraintStack is the main hub when access constraints on GameObject. When work with constraints, the first thing to do is to add ConstraintStack on GameObjects.
ConstraintStack is a standard MonoBehaviour, so you could add it like any other Behaviours.
The image above is a constraint stack with three constraints. As you can see, the editor mainly contains three sections:
The first section is the list of constraints. There're five items per line:
The constraint button, clicking this will bring up the editor panel for that constraint, also this button's background indicates the "active" and "influence" state;
The active button, clicking this button will toggle the active state for the constraint;
The move-up button, clicking this button will bring the constraint up one slot in the stack;
The move-down button, clicking this button will push the constraint down one slot in the stack;
The delete button, clicking this button will remove the constraint from stack and destroy it from owner GameObject;
The second section is the "Exec Order" used to decide the execution order among multiple ConstraintStacks. Like the Unity's script execution order setting, the lower the "ExecOrder", the earlier it gets evaluated.
The third section is the "Add Constraint" button, it will bring up the list of available constraints when you click the button. Clicking anyone in the list will automatically add the constraint into current ConstraintStack.
◊ Common settings of constraints
Each constraint has its own unique functionality, and unique settings.
But they do share some common settings, let's examine them here.
The image above shows the editor for a constraint. Although there're many options inside, we only focus on those are shared by most constraints.
The first line has the "active" & "gizmos" toggles, Although all of the constraints has "Active" property, only about half of the constraints has gizmos;
The second line is the "target" field, except the LimitPostioin/Rotation/Scale, all of the constraints need a target to work;
The last line is the "influence" bar, which is shared by all of the constraints. It controls how much influence this constraint has on the owner GameObject;
In the next pages, we will explain each constraint with the corresponding examples scenes.
CopyPosition constraint uses the target's position to calculate owner's position.
Check the preview image below:
Here we explain each options in the CopyPosition editor;
Specify the target object, owner's Position/Rotation/Scale will be dependent on the target;
"Target Space" decides how to extract source vector from target, "world" means using transform.position/eulerAngles/lossyScale, "self" means using transform.localPosition/localEulerAngles/localScale;
"Owner Space" decides how to apply result vector to owner, "world" means using transform.position/eulerAngles/localScale(refer to the formula in CopyScale), "self" means using transform.localPosition/localEulerAngles/localScale;
LimitDistance constraint ensures the distance between owner & target meets specific conditionts.
Check the preview image below:
Here we explain each options in the LimitDistance editor;
The "Limits" provides six available limit options; The options are all independent, developers could toggle on/off any of them to fit specific needs;
If we toggle on XMin & XMax, then the scale.x of owner will be limited with the given range;
If we toggle on only XMin, then the scale.x of owner will be limited to be always larger than XMin;
If we toggle off both XMin & XMax, then the scale.x of owner will not be affected by this constraint;
"Owner Space" decides how to apply result vector to owner. (v is the result vector)
"world" means using transform.localScale = v / transform.parent.lossyScale,
"self" means using transform.localScale = v;
TransformMapping constraint extracts vector from target's pos/euler/scale property, lerp it to generate a new vector, and applies the result to owner's some Transform property .
Check the preview image below:
Here we explain each options in the TransformMapping editor;
This constraint is more complex than the previous ones, well, you can even tell that form the length;
This constraint could extract a source vector from target's position/euler/scale property, then mapping the source vector to destination vector with some calculation, finally, put the destination vector into the owner's specified property (could be different from source vector's source)
This section decides how to map vector from target to owner.
"Src data type" decides which property of target transform is used, options include: position / euler / scale;
"Dst data type" decides which property of owner transform is set to, options include: position / euler / scale;
"Source to Destination Mapping" gives a finer granularity control of vector mapping, you could control which component of source vector to be mapped to which component of destination vector. For each component of destination vector, we could select "X/Y/Z/None" to indicate which component from source vector is used (None means unchanged)
"Extrapolate" decides whether the value mapping is continued or clamped outside the given range;
So, let's take the image above as an example.
it means to mapping target.position to owner.localScale;
target.position.y => owner.localScale.x;
target.position.x => owner.localScale.y;
target.position.y => owner.localScale.z;
This section maps each component from source range to destination range.
For example, for component x, source range is [0,2], destination range is [10,20]; if input.x = 1, then the output.x = 15;
if "Extrapolate" is enabled, then with input.x = 4, we get the output.x = 30;
if "Extrapolate" is disabled, then with input.x = 100, we still get the output.x = 20;
"Target Space" decides the how to extract source vector from target,
"world" means using transform.position/eulerAngles/lossyScale,
"self" means using transform.localPosition/localEulerAngles/localScale;
"Owner Space" decides how apply result vector to owner,
"world" means using transform.position/eulerAngles/lossyScale(*),
"self" means using transform.localPosition/localEulerAngles/localScale;
(*) calculation for scale with owner_space=world, check the CopyScale page;
MaintainVolume constraint squashes/stretches object along given axis.
Check the preview image below:
Here we explain each options in the MaintainVolume editor;
Volume section contains three parts: Base, Axis, Multiplier.
Base value is calculated with scale.x * scale.y * scale.z and is automatically set when MaintainVolume is added. Usually you could modify the Multiplier field instead of directly manipulating this.
Axis specify the axis that will trigger the squash/stretch, only scaling on that axis will make the GameObject squash/stretch;
Multiplier is a parameter used to multiplied on Base;
The result scale would be: < Sqrt(Base*Multiplier/y), y, Sqrt(Base*Multiplier/y) > when Axis=Y;
"Owner Space" decides how to get source vector and apply result vector to owner. (v is the result vector)
src = transform.lossyScale,
transform.localScale = v / transform.parent.lossyScale;
src = transform.localScale;
transform.localScale = v;
ClampTo constraint binds the owner on a spline path, and moves owner based on an offset value, which is calculated based on some component of owner's position. When offset = 0, owner at the start; When offset = 1, owner at the end of spline.
Developers need to specify a spline in this field to make the constraint work.
Affect section contains three parts: Axis, Offset, Cyclic.
ClampTo constraint will use one component of owner's position to calculate the offset for binding owner on spline. offset = (v - startVal) / Dimension;
Main Axis: decides which component of position to use;
Offset: if you don't want the owner to stay strictly on spline, enable the offset and add an vector on the result;
Cyclic: extra fix when offset out of range.
when disabled, offset is directly clamped to [0,1];
when enabled, offset = Mathf.Repeat(offset, 1.0f);
Dimension is calculated by sampling on spline, find out the bounding box, then calculate the (max - min) on specified axis;
startVal is the min value calculated in the same time of Dimension calculation.
The offset is calculated with the formula: offset = (v - startVal) / Dimension;
The sampling is not trivial calculation, so it will not be called on every changes on spline, click the "Recalculate Dimension" button whenever you need to update the dimension;
FollowPath constraint binds the owner on a spline path, and moves owner based on an offset value. The constraint can not only affect the owner's position, but also set the rotation of owner based on the spline's setting.
When offset = 0, owner at the start; When offset = 1, owner at the end of spline.
The target transform, this constraint needs this target to work;
RotateAxis, owner will only rotate around this local axis, you could imagine this axis as the pivot of compass.
LookAxis, owner will use this axis to point toward target, you could image this axis as the needle of compass;
CCD Solver(IK Solver) constraint, directly calls the CCDSolver to bend the IK link to reach the specified target, it can also utilize IK-constraints such as AngleConstraint & ConeConstraint to improve quality;
To find out how to setup IK-constraints to improve the IK solver calculation quality, check the CCDIK part of skeleton animation manual.
Check the preview image below:
Here we explain each options in the CCDSolver editor;
CCDSolver can setup the IK-link in auto-mode by "BoneCount" if all the joints are in a hierarchy formed by transform.parent.
Or user can setup manually the IK-link in manual-mode, this could enable to make up a link without constraint of hierarchy.
In most cases, auto-mode should be able to satisfy the needs.
(Be noted to click "Apply" button after modifying the link)
The target transform, the IK link will try best to reach the target constantly;
"Recollect IKConstraints" button: when this constraint is firstly added, it will collect the IK-Constraints on the IK link. If you add new IK-Constraints later, you need to click this button to order to recollect it.
"Bone Count": decide how many bones are in the IK-link, there would be "BoneCount + 1" transforms, as there's an end-effect transform;
"Dist Threshold": during IK iterations, if the distance between end-effector & target is less than this value, this calculation is taken as success and ended; You'd better leave it as it is, making it bigger could end the calculation with fewer iterations, but also could cause visible jitter if you move the target in a continuous way.
"Damp": damp could make the IK looks smoother, it limits the max angles a joint can rotate in one iteration. This is by default on.
"Use TargetRotation": If you turn on this, the end-effector transform will use the target's rotation, could be useful in some circumstances. Default off.
"Revert Options": If you have added IK-constraint, there're chances that the IK calculation failed for given target , this option decides what to do under such circumstances.
No Revert: just leave it as it was at the last iteration. It might be fun to watch this, but it's not recommended to use this for continuous movement, could cause visible jitters.
Revert to Start State: revert the IK-link to the state before IK calculation is started. If there're multiple constraints in the ConstraintStack, this option could leave the IK-link in an intermediate state.
Revert to Prev Reasonable State: revert the IK-link to the previous "non-failed" state, this is the default option.
"BoneMarker size": used in editor, decide the marker size for joint position;
"Target": Locked in this mode if target is set. IK-link will constantly trying to reach the target;
"Manual": In this mode, developer could drag the handle to set the IK-target position, no IK calculation is invoked until the "Follow" button is clicked;
"Continuous": developer could drag the IK-target handle around, and the IK-link will try to reach it. This is like "Target" mode except there's no target transform set;
"Normal": No IK-target handle is drawn, think this as an idle mode;
"Debug": This is for me to single-step debug the IK iterations, developers should not need this one.
ChildOf constraint makes the owner to operate like it's the child of the target transform. Plus you could also opt-out position/rotation/scale property for this pseudo relation.
This is useful when you need to attach something to some transform but doesn't want to change the real hierarchy.
Check the preview image below:
Here we explain each options in the ChildOf editor;
The target's mesh provides the vertex position for owner to project to.
This value will push the pivot of owner away from the nearest vertex, along the line from vertex to the original position of pivot;
Empty Marker is not a constraint, it's a helper component to help developers to select GameObjects which're hard to visualize and select by usual means;
(If you're familiar with the other 3D-softwares such as Blender/Maya/etc, you should know about such helper objects already)
See the preview image above, usually we cannot select the "spine" or "neck" transform by clicking the model in the scene, it will cycle between the root GameObject & the one with SkinnedMeshRenderer(SMR). An alternative is to select the transforms in the hierarchy window or load the selection with Ctrl+Alt+number.
EmptyMarker would be very effective and easy to use in such cases. Want to select the spine transform? just click the RectFrame mesh in the above image.
EmptyMarker is a great partner for constraints and IK system. Check the image below (click to open gif).
These markers are automatically disabled & hidden when in game-mode & runtime, so you don't need to worry about they disrupt the scene setting or increase the performance cost.
* How to add the EmptyMarker?
1. Navigate to the target GameObject, add the "EmptyMarker" MonoBehaviour. e.g.: Hips;
2. Click the "Presets", select one from the popup list, recommend the "RectFrame";
(You could use custom meshes too, just put the mesh file under the directory "Assets/Skele/Common/Res/Marker/Models". Then when you press Presets, the new model will be found and shown in the list)
3. When the GameObject with EmptyMarker is selected, the mesh will be tinted yellow. Now click it again to navigate to the marker's mesh GameObject (which is named as "mesh").
4. When you select the mesh object, you could move/rotate/scale it to make it easier to select and handle later.
Example: Walk Motion
In this page, we'll show an example of using constraint to make animation clips. It's an intermediate-level example which involve IK, IK-constraint, constraints, helper-object, AnimationView, clip baking, clip compressing.
Be noted that making clips is only one of the many usage of constraints.
This example consists of three parts:
In the first part, you would see how to setup the IK:
example to set up helper objects;
example to set up IK solver;
example to set up IK-constraints;
Move the limb with IK solver
To avoid confusion, I need to point it out that IK-solver could act as an independent component, developer could using it outside the Skele's Bone-Editor.
In the second part, we will show how to use CopyPosition constraint to connect the position properties from hips and the feet, so we could move the whole model by only moving the two feet.
Also we'll use Floor constraint to prevent the foot-target to burrow into the ground.
In the third part, we will show how to make the clip with what we've setup.
Then we'll show you how to bake the clip to make it also work on models without constraints;
Also you'll see how to use the Animation Compressor to reduce keyframes in the clips;
Auto Biped Constraint Setup Utility
In v1.9.6,we introduced this new utility to help developers setup their biped constraints swiftly.
This includes constraints to:
limit elbow to rotate in one plane, and limit the rotation angle;
limit knee to rotate in one plane, and limit the rotation angle;
limit shoulder's rotation range, and limit the twist range;
limit thigh's rotation range, and limit the twist range;
(By 'twist', we mean rotation around the primary axis, e.g.: shoulder to elbow axis)
1. get started
This utility can be brought up via the menu item at "Window/Skele/SetupBipedConstraint";
(1) The first thing to do is to fill the "Root" field; you should put the topmost GameObject of model (usually with the Animator) in there.
This field is automatically filled for you if the model is selected when the EditorWindow is opened up;
(2) The "bones" list is a bone mapping table, it is automatically filled when the "Root" field is changed.
Usually you don't need to touch anything in the list, there're some special cases though, e.g.:
The skeleton structure is kind of unusual, and you might need to manually change the bone mapping here;
You don't want to setup constraints on some limbs, so you could try removing the hands references from the list;
(3) The "Options" will affect what kind of constraints be added to the model
If nothing is selected, constraints will be added on shoulder/elbow/thigh/knee, this is the basic setting, it improves the IK quality when manipulate the bones;
If 'Add IKTarget' is selected, besides the constraints above, IK-targets will be added at wrists & ankles. They enable devs to move the limbs by dragging the IK-target, without starting the SMREditor;
If 'Floor constraints' is selected, you need to specify another object as Floor(check the Floor constraints page), then the IK-targets on ankles will be constrainted to not go below the floor;
If 'Pelvis Follow' is selected, two 'CopyPosition' constraints will be added at the 'Hips' bone, to make it stay at the center position of two ankles' IK-targets. i.e.: the model will move with the two feet.
You could also check the tutorial video below for reference: