# How weights of 2D Blending are calculated?

Could you share information about how weights of 2D Blending are calculated. I’ve done 1D Blending. but have problems with 2D Freeform Cartesian. Even pseudo code sharing will be of help.

I also found function

``````public static extern void CalculateRootBlendTreeChildWeights (Animator animator, int layerIndex, int stateHash, float[] weightArray, float blendX, float blendY);
``````

but is in namespace UnityEditorInternal.

Thx

Example. Circles around Motion.positions are weights. How they are calculated? What algorithm is used.

The 2D Freeform Cartesian blend type is based on the Gradient Band interpolation described in chapter 6.3 of my Master’s Thesis.

The 2D Freeform Directional blend type is based on the Gradient Bands in Polar Space interpolation described in chapter 6.3.1.

http://runevision.com/thesis/

One example is, say you want to blend between 4 different animations for your character’s horizontal movement (forward, back, left strafe, right strafe). What you do is, you take the character’s velocity, transform it into character’s local transform space, normalize it. It gives you a velocity vector relative to the character’s direction, a vector where you can see where the body is moving towards relative to the direction it faces, so you can tell if it’s strafing or moving backwards etc.

So you feed this resulting vector into your 2D blending tree with the 4 animations set up correctly at Forward (Y:1) Backward (Y:-1) Right (X+1) Left (X-1) and it blends between those 4 animations. If the relative velocity vector is (0.5, 0, 0.5) the animation forward and right strafe will both play at 0.5 blend weight (If you set the max blending values at 1.0 that is).

I think u are speaking about 2D Simple Directional, and I was more interested to FreeForm and Cartesian flavour, but even this if you experiment with red dot can see that blue points weights depend of distance to red dot(input) and also I think to angular distance. So if we use Formula 1/d1/(1/d1+1/d2+1/d3…)
d1 can be just distance or combination of distance + angular distance…

`````` void UpdateSimpleDirectional2D(ref float[] inputs, RuntimeBlendTree runtimeBlendTree) {
int numChildren;

if (runtimeBlendTree.children == null || (numChildren = runtimeBlendTree.children.Length) == 0)  return;

AnimationMixerPlayable animationMixerPlayable = runtimeBlendTree.mixerPlayable;

if (numChildren == 1) {
animationMixerPlayable.SetInputWeight(0, 1f);
}

__inputVector2.x = inputs[runtimeBlendTree.index0];
__inputVector2.y = inputs[runtimeBlendTree.index1];

#if DEBUG

//__inputVector2.x=0.5f;

if(showLog){
Debug.Log("RuntimeBlendTree "+runtimeBlendTree+" "+runtimeBlendTree.blendType+" numChildren="+runtimeBlendTree.children.Length);
Debug.Log("Input Vector: "+__inputVector2);

}
#endif

//FORMULA  // 1/dc /(1/di+1/di+1+1/dc+..1/dn)
//dc - distance of "c" point to "input" point

//FORMULA  // 1/ac /(1/ai+1/ai+1+1/ac+..1/an)
//ac - angular distance of "c" point to "input" point

float[] sqrDistancesToInput = new float[numChildren];
float[] angleDistancesToInput = new float[numChildren];

int i = 0;
float weight = 0f;
float currentSqrDistanceToInput;
float currentAngleDistanceToInput;
float sqrDistanceToInputSum = 0f;
float angleDistanceToInputSum = 0f;
RuntimeChildMotion childMotion;

//calculate distances of every childMotion.position to input
for (; i < numChildren; i++) {
childMotion = children*;*
``````

currentSqrDistanceToInput = (__inputVector2 - childMotion.position).sqrMagnitude;

if (currentSqrDistanceToInput > 0) {

//??? Could I bind together this distances together??

//currentAngleDistanceToInput=Vector2.Angle(__inputVector2,childMotion.position);

//cosinus similarity from -1f to -1f shifted to 0 to 1f( same angle cosinus similarity=0f and max opposite direction -1f)
currentAngleDistanceToInput=(Mathf.Clamp(Vector2.Dot(__inputVector2.normalized,childMotion.position.normalized),-1f,1f)-1f)*(-0.5f);

// 1/d1+1/d2+1/d3…
sqrDistanceToInputSum+=1/currentSqrDistanceToInput;

if(currentAngleDistanceToInput>0)
angleDistanceToInputSum+=1/currentAngleDistanceToInput;

sqrDistancesToInput = currentSqrDistanceToInput;

angleDistancesToInput*=currentAngleDistanceToInput;*

}
else {
//input is same with childMotion.position

//reset all to min weight
for (int j = 0; j < numChildren; j++) {

animationMixerPlayable.SetInputWeight(j, 0f);
}

//maximum weight of playable channel where input(value/position) and childMotion.position overlapped
animationMixerPlayable.SetInputWeight(i, 1f);

#if DEBUG

if(showLog){
for (int j = 0; j < numChildren; j++)

Debug.Log(j+“: “+((Playable.GetTypeOf(animationMixerPlayable.GetInput(j)) is AnimationClipPlayable) ? animationMixerPlayable.GetInput(j).CastTo().clip.name : “”)+” w=”+animationMixerPlayable.GetInputWeight(j));

}
#endif

return;
}

}

for (i = 0; i < numChildren; i++) {
//
//
childMotion = children*;*

//reuse currentSqrDistanceToInput var

// 1/d1 /(1/d1+1/d2+…1/dn)/1 => 1/(d1*(1/d1+1/d2+…1/dn))

//(d1*(1/d1+1/d2+…1/dn))
currentSqrDistanceToInput = sqrDistanceToInputSum * sqrDistancesToInput*;*
currentAngleDistanceToInput=angleDistanceToInputSum * angleDistancesToInput*;*

//1/(d1*(1/d1+1/d2+…1/dn))
if(currentSqrDistanceToInput>0 && currentAngleDistanceToInput>0)
weight = (1 / currentSqrDistanceToInput)*0.5f + (1/currentAngleDistanceToInput)*0.5f;
else if(currentSqrDistanceToInput>0)
weight = (1 / currentSqrDistanceToInput)*0.5f + 0.5f;//have same direction so 0.5f
else
weight=0;

animationMixerPlayable.SetInputWeight(i, weight);

#if DEBUG

if(showLog){
Debug.Log(i+“: “+((Playable.GetTypeOf(animationMixerPlayable) is AnimationClipPlayable) ? animationMixerPlayable.CastTo().clip.name : “”)+” w=”+animationMixerPlayable.GetInputWeight(i));

}
#endif

RuntimeBlendTree blendTree = childMotion.subTree;

if (blendTree != null) {
blendTree.UpdateWeights(ref inputs, blendTree);
}
}
}