I am looking at how Unity handles bone rotations. When I import a model and put it in the scene all bones appear as childs in the hierarchy panel. When I select the foot bone, I see the position and rotation info of this bone in the inspector panel, which is quite useful. I tried the script below (attached to the footbone object) to align the footbone to the normal of the terrain mesh at that point.
function Update () {
var hit : RaycastHit;
Physics.Raycast(Vector3(transform.position.x,transform.position.y + 1,transform.position.z), Vector3(0,-1,0), hit, 10);
var vNormal : Vector3 = hit.normal;
var tx = transform.eulerAngles.x;
var ty = transform.eulerAngles.y;
var tz = transform.eulerAngles.z;
transform.rotation = Quaternion.FromToRotation(Vector3.up, vNormal);
// transform.eulerAngles.x += tx;
// transform.eulerAngles.y += ty;
// transform.eulerAngles.z += tz;
}
It does align the footbone to the normal of the land however the orientation of the footbone also changes. The foot is facing sideways now and it is flipped upside down with the top of the foot facing down. I tried some stuff with eulerAngles with a little a bit of success but it will never keep the orientation of the foot more or less the same. How is this done?
The trick is to find the appropriate Quaternion function. What really needs to be done would be to take the foot’s original .forward vector, flatten it with regard to the surface’s normal, and then use Quaternion.LookRotation(flattenedVector, surfaceNormal);
I’m not sure what math or functions would be required for those first steps, but hopefully that will at least point you in the right direction.
var hit : RaycastHit;
Physics.Raycast(Vector3(transform.position.x,2,transform.position.z), Vector3(0,-1,0), hit, 100);
var vNormal : Vector3 = hit.normal;
var vForward : Vector3 = transform.forward;
var vFlattened : Vector3 = Vector3(vForward.x, vNormal.y, vForward.z);
transform.rotation = Quaternion.LookRotation(vFlattened, vNormal);
the orientation of the right foot remains the same but it is not aligned correctly with the land. The left foot is pointing backwards know, it is also not correctly aligned and the top of the foot is almost facing downwards.
I asked somewhere else on how to approach this. They came up with this:
foot left = cross of normal and current forward
new forward = cross of new foot left and normal
new up = cross new foot left and new foot forward (or normal)
So I tried this:
Physics.Raycast(Vector3(transform.position.x,2,transform.position.z), Vector3(0,-1,0), hit, 100);
var vNormal : Vector3 = hit.normal;
var vForward : Vector3 = transform.forward;
var vLeft = Vector3.Cross(vNormal,vForward);
vForward = Vector3.Cross(vLeft,vNormal);
var vUp = Vector3.Cross(vForward, vLeft); // vNormal
transform.right = vLeft;
transform.forward = vForward;
transform.up = vUp;
Unfortunately no success. It seems both feet are aligned with the normal but they are facing backward now.
So I wonder if I can actually use “transform.up”, “transform.right” and “transform.forward” the way I use it?
I also wonder if the fact that unity automatically rotates the model (fbx from Max) 270 degrees on the x-axis, could be the reason why everything is failing so far? Am I using incorrect transform data to make the calculations?
I also wonder why attaching the same script to both the left and right foot has different results on the same surface? They both face backward but the left foot is also flipped upside down.
Possibly…I haven’t really done anything like this, but maybe you could try using an empty as a parent, and base all transform calculations on the empty, so you’d be sure that up really is up, and forward really is forward?
I know how other people arrange it to align the footbone to the normal of a terrain mesh at a point. They are using another engine, but the method maybe could be used also with Unity.
What they do is to build some animations for just the legs, including their acomodation to a oblique ramp: one from say -45 to +45 degrees in Z and another for the same diference but in the X. Then they script a way to read the slope of the terrain at the point where the player is and aply those animations blending them with the current others (walk, run or idle), depending on the slope found. I haven´t tested this method myself and I don´t know how to acomplish it with Unity yet, but maybe is worth to try it.
I get the idea and I will defintely look into this. I also had an alternative approach in mind. Just hold a bunch of data in a table with set transformations for a certain normal, get the normal and seek the closest solution in the table and apply it.
But I still hope to solve this by code, the more as Joachim secured me in another thread that this is actually a simple thing to do in unity (hint, hint ;-))!
If you’re getting close, like having the feet in the right place just facing backwards, why not just flip that by hand afterwards? It’s not elegant, but it’s only one line of inelegance
QuaternionUtil.AlignRotation is an expanded form of the FromToRotation natively provided in unity. I developed it because usually you would like to have a similar functionality of FromToRotation with the addition of controlling the final orientation of the transform.
use it to get a quaternion q; then “Pre” multiply q with your transform’s rotation.
public class QuaternionUtil
{
/// <summary>
/// Rotates to make mainDirectionFrom along
/// mainDirectionTo. Then further rotates around
/// this last direction = mainDirectionTo to get
/// aligningDirectionFrom, aligningDirectionTo,
/// and the axis of rotation mainDirectionTo coplanar.
/// </summary>
/// <param name="mainDirectionFrom"></param>
/// <param name="mainDirectionTo"></param>
/// <param name="aligningDirectionFrom"></param>
/// <param name="aligningDirectionTo"></param>
/// <returns></returns>
public static Quaternion AlignRotation
(
Vector3 mainDirectionFrom,
Vector3 mainDirectionTo,
Vector3 aligningDirectionFrom,
Vector3 aligningDirectionTo
)
{
// the axis of rotation of the rotation mainFromTo
// is mainDirectionFrom cross mainDirectionTo
Quaternion mainFromTo = Quaternion.FromToRotation (mainDirectionFrom, mainDirectionTo);
// rotate every thing using mainFromTo first
// mainDirectionFrom becomes mainDirectionTo (no need to calculate)
// aligningDirectionFrom becomes mainFromTo * aligningDirectionFrom
aligningDirectionFrom = mainFromTo * aligningDirectionFrom;
// the projection of aligningDirectionFrom and
// aligningDirectionTo onto the plane normal to
// mainDirectionTo, the next axis of rotation
Vector3 ProjectedAligningDirectionFrom = aligningDirectionFrom - Vector3.Project (aligningDirectionFrom, mainDirectionTo);
Vector3 ProjectedAligningDirectionTo = aligningDirectionTo - Vector3.Project (aligningDirectionTo, mainDirectionTo);
// now we need to rotate aligningDirectionFrom
// around mainDirectionTo to arrive at the
// plane (mainDirectionTo, aligningDirectionTo).
// this boils down to rotating ProjectedAligningDirectionFrom
// to arrive at ProjectedAligningDirectionTo
// aligningFromTo rotates aligningDirectionFrom around
// mainDirectionTo to become coplanar with aligningDirectionTo
Quaternion aligningFromTo = Quaternion.FromToRotation (ProjectedAligningDirectionFrom, ProjectedAligningDirectionTo);
// the overall rotation
return aligningFromTo * mainFromTo;
}
}
I still didn´t solve this. I did what Eric suggested and there is actually a difference, the feet are now rotated 90 degrees on the y axis. But still no control over the orientation. I will look at your code and see if that works for me!
Thanks Firas, got it right now! It works great, however there is one small problem. When you run the script the feet are aligned perfectly with the land and they keep their orientation according the forward vector of the feet. But when you start rotating the main character the feet slowly loose their orientation. To avoid this, I think the easiest way would be to somehow store the initial x and z rotation values of the feet bones and apply these each time before calculating the new rotation. In other words, before calculating the new alignment rotation, make the rotation of the feet look like it never had the alignment rotation applied to it before. I tried this by storing the eulerAngles x and z in the Start() function and then set these values back before calculating the new rotations. But that didn´t work. What is the best way to do this?
At this stage I am rotating the transform of the character. For testing purposes character has a script added where transform.eulerAngles.y is incremented by 1 each frame.
But yes, the next steps are:
manipulating bone rotations of the legs to make sure that both feet are on the ground and not in it or above it.
running animations on the character
I have yet to see what this will do to the feet alingment rotations.
Store Transform.localRotation in the Start() function and restore it before doing new alignment routines solves the issue with the feet slowly going out of position.
r they going out of position or orientation?
what is invariant as they go out of position? r their verticals always aligned with the terrain normal? what is the thing that is changing?
Sorry for using the word position in my previous post. I meant orientation! The bigger the normal.x or/and normal.z of the land the faster it looses orientation. Alignment with the land remains perfect.