Разделить кватернион на оси вращения
У меня есть кватернион, представляющий ориентацию объекта (желтый прямоугольник и сфера). Я хотел бы знать, возможно ли разделить этот кватернион на другие кватернионы, которые дают нам вращение каждой локальной оси (X, Y и Z).
То, что я делал до сих пор, это получение представления Эйлера и работа с ним, но это не правильное решение для моего конкретного случая:
Учитывая две точки (синие прямоугольники), я хочу ограничить ориентацию моего объекта так, чтобы он не мог указывать из серой плоскости, даже если мой кватернион смотрит из этой плоскости.
Я хочу разделить (разложить) кватернион, потому что, когда мой объект достигает границы плоскости (например, справа), я хочу, чтобы он оставался там (в этом компоненте), а затем вращал мой объект в вертикальном компоненте., используя один из новых расщепленных кватернионов.
Я работаю с Unity.
Я надеюсь, это понятно, моя проблема:)
2 ответа
Вот способ получить локальное вращение только оси Y. Эту функцию можно изменить, чтобы получить ось x или z.
/// <summary> isolate the y-Component of a rotation </summary>
private Quaternion yRotation(Quaternion q)
{
float theta = Mathf.Atan2(q.y, q.w);
// quaternion representing rotation about the y axis
return new Quaternion(0, Mathf.Sin(theta), 0, Mathf.Cos(theta));
}
Вы можете проверить результат в Инспекторе Unity, преобразовав в Euler:
public float yLocal;
void Update()
{
yLocal = yRotation(this.transform.rotation).eulerAngles.y;
}
До этого я работал с IMU и, насколько мне известно, с Unity, если объект считывает данные IMU как кватернионы, вы не столкнетесь с проблемой блокировки gimbo. Таким образом, вы действительно можете создать ссылку на вращение объекта и преобразовать ее в эйлеровых ангелов, а затем преобразовать обратно в кватернионы, прежде чем применять ее к объекту.
Однако, если вы просто хотите ограничить вращение, я бы сделал что-то вроде этого:
//the last rotation was pointed at the grey zone.
private Quaternion prevAllowedRotation;
void FixedUpdate(){
if(!isValidRotation()){
this.transform.rotation = prevAllowedRotation;
}else{
this.transform.lookAt(lockToArea());
}
}
private bool isValidRotation(){
//I chose forward based on the image, find the direction that works for you
Ray ray = new Ray(this.transform.position, this.transform.forward);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, 10f){
if(hit.transform.tag == "wall"){
return true;
}
}
return false;
}
private Vector3 lockToArea(Gameobject grey){
Vector3 center = grey.transform.position;
//figure out how to get the position of the facing direction on the same plane as the grey area.
Vector3 pointerPosition = getPointerPosition();
RaycastHit hit;
if(Physics.Linecast(pointerPosition, center, hit)){
return hit.point;
}
return Vector3.zero;
}
private Vector3 getPointerOnPlane(){
Ray ray = new Ray(this.transform.position, this.transform.forward);
// you need to figure out how to dyanmically get the distance so that the ray lands on the same plane as the vector
float distance = 0;
return ray.GetPoint(distance);
}
Это блокирует его вращение, чтобы указывать только на серый куб. Это должно вам сильно помочь, вам просто нужно понять, что данные IMU считываются в и находятся в той же плоскости, что и центр серого куба.