Четверг, 26 Декабря 2024, 23:43

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Помогите со сложным вращением (кватернионы)
AlerrДата: Пятница, 26 Августа 2016, 12:17 | Сообщение # 1
был не раз
Сейчас нет на сайте
Привет всем, долго бился с кватернионами и безуспешно :crazy:
Надеюсь кто-то поможет. Вот формулировка задачи:
Есть куб и игрок.
Игрок может брать и вращать куб по 1-й из 3-х осей относительно себя.
Куб должен свободно вращаться колесиком мыши по оси, которая в данный момент выбрана как ось вращения. По остальным осям куб должен “выравниваться”. Оси вращения считаются относительно игрока.
Во время вращения куб могут толкать другие предметы, куб физический и может взаимодействовать с окружающими предметами.
Нефизическое поведение куба недопустимо.

Что значит “выровнять” куб:
Если игрок вращает куб по OY, то это значит, что нижняя грань куба должна быть паралельна полу.
Если игрок вращает куб по оси OZ (forward , вращение от нас и к нам(да, у меня камера свернутна на 90 градусов и right соответствует forward)), то по оси OX он выравнивается относительно пола. По оси OY докручивается к нам так, что плоскость OYZ становится паралельна плоскости экрана.
Если игрок вращает куб по оси OX (right), то куб вращается перед нами по/прочив часовой стрелки. Ближайшая к нам грань должна быть паралельна плоскости экрана. По оси OZ куб становится на угол кратный 90 градусам (Пытается выровняться “по полу”).

Итого получается что куб вращается сам (самостоятельно докручивается) и под действием игрока (игрок вращает по заданной оси).
——————————
Теперь по коду:
Вот что я имею ввиду под выравниванием:
Код
myTransform - player
rigidBody/rigidTransform - куб
rotStep = Mathf.Sqrt (0.5f);// 90 degree
Этот код “выравнивает” куб.
void FixedUpdate(){
  Quaternion qq = rigidBody.rotation;
  qq.x = Mathf.Round (qq.x / rotStep) * rotStep;
  qq.y = Mathf.Round (qq.y / rotStep) * rotStep;
  qq.z = Mathf.Round (qq.z / rotStep) * rotStep;
  qq.w = Mathf.Round (qq.w / rotStep) * rotStep;

  rigidBody.rotation = Quaternion.Lerp (rigidBody.rotation, qq, 5f*Time.fixedDeltaTime);
}

————
Этот код позволяет крутить куб по выбранной оси:
Код
delta - сюда поступает displacement с колесика мыши
void FixedUpdate(){
  if (axisIndex==0) {         
    localAxis = rigidTransform.InverseTransformPoint (Vector3.up + rigidTransform.position);     
    Debug.Log ("OY");       
  }  else {
          if (axisIndex==1) {// z axis
            localAxis = rigidTransform.InverseTransformPoint(myTransform.TransformPoint (Vector3.forward)+(rigidBody.position-myTransform.position));     
            Debug.Log ("OZ");         
    }  else {           
      if (axisIndex == 2) {            
         localAxis = rigidTransform.InverseTransformPoint (myTransform.TransformPoint (Vector3.right) + (rigidBody.position - myTransform.position));                         
         Debug.Log ("OX");           
      }  else {             
         eulerAngleVelocity = Vector3.zero;          
      }
     }
  }
    Quaternion deltaRotation = Quaternion.AngleAxis(delta * 400f * Time.fixedDeltaTime, localAxis);
    rigidBody.rotation *= deltaRotation;
}

Теперь какие стоят проблемы:
1) Ось по которой вращают куб не свободна, куб постоянно пытается выровнять себя по той оси, по которой его крутят.
2) Ближайшая к игроку грань куба (при axisIndex == 1 || axisIndex == 2) не паралельна плоскости экрана.
Вот ссылк на видео того что получилось добиться через углы эйлера и что “почти” работает (вообщем-то так и должен выглядеть результат):
YandexDisk Video1
YandexDisk Video2
Cсылка на проект с вращением куба: ProjectSource (все делал через кватернионы и куб передергивает при вращении)
По тем видео можно понять как все должно работать. Что скажете?

Задачка для меня сложновата, обычно кватернионами вообще не занимался.
Ассы кватернионов, помогите, в долгу не останусь :)

Добавлено (26 августа 2016, 12:17)
---------------------------------------------
Неужели это на самом деле сложная задача? Я думал, что для профи это как 2 вектора сложить. За помощь отблагодарю денежно.

Сообщение отредактировал Alerr - Четверг, 25 Августа 2016, 19:56
PsychoДата: Пятница, 26 Августа 2016, 13:57 | Сообщение # 2
Психоламер
Сейчас нет на сайте
Цитата Alerr ()
Неужели это на самом деле сложная задача?

Да, довольно сложная. Особенно учитывая то, что для ее решения квантернионы нафиг не упёрлись, но Вам виднее.
AlerrДата: Пятница, 26 Августа 2016, 14:07 | Сообщение # 3
был не раз
Сейчас нет на сайте
Можете посоветовать альтернативное рещение?
VickaДата: Пятница, 26 Августа 2016, 14:32 | Сообщение # 4
Another ART
Сейчас нет на сайте
Alerr,

посмотри вот этот урок)
Автор очень доступно все объясняет)



AlerrДата: Пятница, 26 Августа 2016, 14:36 | Сообщение # 5
был не раз
Сейчас нет на сайте
Спасибо! Обязательно посмотрю. Я уже кучу времени потратил на эту штуку и у меня толкового ничего не вышло. Так что предложение мое в силе.
VickaДата: Пятница, 26 Августа 2016, 15:11 | Сообщение # 6
Another ART
Сейчас нет на сайте
Цитата Alerr ()
Я уже кучу времени потратил на эту штуку и у меня толкового ничего не вышло.

я их так и не победила) Просто нашла кусок на тот момент нужного когда) С тех пор его и юзаю) Но у меня другая задачка)


berilДата: Суббота, 27 Августа 2016, 11:23 | Сообщение # 7
Я не ленивый, я — энергосберегающий
Сейчас нет на сайте
еще эту статью глянь



Накодил? Убери за собой!
Инвентарь в Unity(UI)
Инвентарь в Unity(GUI)
AlerrДата: Суббота, 27 Августа 2016, 20:00 | Сообщение # 8
был не раз
Сейчас нет на сайте
Да, спасибо) Я уже видел эти статьи. Вообщем-то что там написано понял, но это мне не помогло. Пытаюсь доворачивать куб до ближайшего угла, который кратен 90 градусам, но ничего не выходит :( ...

Добавлено (27 августа 2016, 20:00)
---------------------------------------------
Вот мне подсказали как это можно сделать, буквально за меня написали, я сделал, но не работает:

Код
Quaternion[] rot = new Quaternion[] {
    new Quaternion(0,0,0,1),
    new Quaternion(0,0,0.707f,-0.707f),
    new Quaternion(0,0,0.707f,0.707f),

    new Quaternion(0,0,1,0),
    new Quaternion(0,0.707f,-0.707f,0),
    new Quaternion(0,0.707f,0,-0.707f),
    new Quaternion(0,0.707f,0,0.707f),
    new Quaternion(0,0.707f,0.707f,0),

    new Quaternion(0,1,0,0),
    new Quaternion(0.5f,-0.5f,-0.5f,-0.5f),
    new Quaternion(0.5f,-0.5f,-0.5f,0.5f),
    new Quaternion(0.5f,-0.5f,0.5f,-0.5f),
    new Quaternion(0.5f,-0.5f,0.5f,0.5f),

    new Quaternion(0.5f,0.5f,-0.5f,-0.5f),
    new Quaternion(0.5f,0.5f,-0.5f,0.5f),
    new Quaternion(0.5f,0.5f,0.5f,-0.5f),
    new Quaternion(0.5f,0.5f,0.5f,0.5f),

    new Quaternion(0.707f,-0.707f,0,0),
    new Quaternion(0.707f,0,-0.707f,0),
    new Quaternion(0.707f,0,0,-0.707f),
    new Quaternion(0.707f,0,0,0.707f),

    new Quaternion(0.707f,0,0.707f,0),
    new Quaternion(0.707f,0.707f,0,0),
    new Quaternion(1,0,0,0)
  };

int NearestRot(Quaternion cubeRot){
    int nearestIndex = 0;

    for (int i = 0; i < 24; ++i) {
      float maxDot = -10000.0f; //NEG INF
      float dot = Mathf.Abs(Quaternion.Dot(
        cubeRot, rot[i]));
      if (dot > maxDot) {
        maxDot = dot;
        nearestIndex = i;
      }
    }
    return nearestIndex;
  }

void FixedUpdate() {
  cubeRigid.rotation = Quaternion.Slerp(cubeRigid.rotation, rot[NearestRot(cubeRigid.rotation)], 10f*Time.fixedDeltaTime);
}
ЛевшаДата: Воскресенье, 28 Августа 2016, 11:15 | Сообщение # 9
почти ветеран
Сейчас нет на сайте
Вращайте свой куб физикой, методы найдете в справке.
Старайтесь не перемудрять в своем коде, всегда надо искать простые решения. :)


X.cor.R (Prologue)

Сообщение отредактировал Левша - Воскресенье, 28 Августа 2016, 11:16
NEBRДата: Воскресенье, 28 Августа 2016, 18:27 | Сообщение # 10
почти ветеран
Сейчас нет на сайте
Alerr, решена твоя задачка. записал видео ответ


Прикрепляю код

Код

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class quaternionTest : MonoBehaviour {

    // UI иконки включенного направления
    public Image xImg;
    public Image yImg;
    public Image zImg;
    // собственно тело
    public Transform cube;
    // приват перем для переключения напр
    int currentDim = 0;

    void Start () {
        SetActiveDim(1); // сразу включаем направление Х
    }
    
    void FixedUpdate () {
        if (Input.GetKeyDown(KeyCode.Alpha1))
            SetActiveDim(1);
        if (Input.GetKeyDown(KeyCode.Alpha2))
            SetActiveDim(2);
        if (Input.GetKeyDown(KeyCode.Alpha3))
            SetActiveDim(3);

        float msDelta = Input.GetAxis("Mouse ScrollWheel");

        if (msDelta != 0) {
            Vector3 axis = Vector3.zero;

            switch (currentDim) {
                case 1: axis = Vector3.left; break;
                case 2: axis = Vector3.up;   break;
                case 3: axis = Vector3.forward; break;
            }

            cube.Rotate(axis, 10f * msDelta);
        }
    }

    void SetActiveDim(int dim) {
        xImg.color = yImg.color = zImg.color = Color.gray;
        // у меня иконка просто рамка из дефолт спрайта, а в ней буква :)
        Text xTxt = xImg.GetComponentInChildren<Text>();
        Text yTxt = yImg.GetComponentInChildren<Text>();
        Text zTxt = zImg.GetComponentInChildren<Text>();
        xTxt.color = yTxt.color = zTxt.color = Color.gray;

        currentDim = dim;

        switch (dim) {
            case 1:
                xImg.color = xTxt.color = Color.red; // UI элемент
                // тут "выравниваем" оси, перед поворотом
                cube.rotation = Quaternion.Euler(cube.rotation.eulerAngles.x, 0f, 0f);
                break;
            case 2:
                yImg.color = yTxt.color = Color.green;
                cube.rotation = Quaternion.Euler(0f, cube.rotation.eulerAngles.y, 0f);
                break;
            case 3:
                zImg.color = zTxt.color = Color.blue;
                cube.rotation = Quaternion.Euler(0f, 0f, cube.rotation.eulerAngles.z);
                break;
        }
    }
}


И ссылку на сцену
https://drive.google.com/file/d/0B4sVvmh_XM8BOVZEcklIcGljRlU/view?usp=drive_web


King Size #Gamiron12
AlerrДата: Воскресенье, 28 Августа 2016, 18:54 | Сообщение # 11
был не раз
Сейчас нет на сайте
Спасибо большое за внимание!
Но услоие Вы не до конца поняли.
1) Смотрите, куб физический, есть rigidBody.
Это значит что его могут толкать окружающие предметы и во время вращения тоже.
2) Куб должен "прилипать"(выравнивться) по углам кратным 90 градусам по тем осям, по которым его не вращают.
Если посмотрите видео по ссылкам из вопроса, то увидите как прилипает к углам кратным 90. Ну смотрите, например вращаем по OY. Значит по OZ и OZ он должен пытаться пристать к ближайшему углу, который кратен 90 градусам.

Задачка вообщем-то почти решена, если будет интересно, то когда доделаю покажу исходник. А так, если хотите ради спортивного интереса, то предлагайте варианты) Всегда интерсно посмотреть еще варианты решения)
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг