Unity 3D Как проецировать Vector3 на плоскость или получить угол между двумя Vector3s в указанной c плоскости? - PullRequest
2 голосов
/ 22 апреля 2020

Я пытаюсь запрограммировать символ от третьего лица, например, при нажатии клавиши направления, например. Нажата кнопка D, если персонаж в настоящее время направлен в том же направлении, что и камера, воспроизводится анимация Поворота на правый четверть, если он направлен на камеру, воспроизводится Поворот на левую четверть, аналогично GTA V. Но у меня возникают проблемы с получением углов между камерой и игрок в плоскости Y. Я пробовал это в скрипте управления плеером:

void Right()
    {
        Vector3 pVec = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
        Vector3 cVec = Vector3.ProjectOnPlane(mainCam.transform.forward, Vector3.up);
        print(cVec);
        float angle = Vector3.Angle(pVec, cVec);
        print(angle);
        if(angle >= 345 && angle <= 15)
        {
            animator.Play("StandQuarterTurnRight");
        }
        else if(angle >= 255 && angle <= 285)
        {
            animator.Play("StandHalfTurnRight");
        }
        else if(angle >= 165 && angle <= 195)
        {
            animator.Play("StandQuarterTurnLeft");
        }
        else if(angle >=75 && angle <= 105)
        {
            float forw = Input.GetAxis("Horizontal");
            if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
            else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
            animator.SetFloat("Speed", forw);
        }
    }

Но это не работает, я получаю неправильные углы. Я даю мне 90, когда персонаж направлен вправо или влево от камеры вперед, и неправильные углы для других, и это вообще не дает 180+, что я делаю неправильно? Есть ли лучший способ добиться того, что я пытаюсь?

Ответы [ 3 ]

1 голос
/ 23 апреля 2020

Я бы преобразовал направление обзора камеры в систему координат вашего персонажа. Тогда это очень легко, поэтому посмотрите, куда вы хотите, чтобы он повернулся или переместился.

Предполагая, что вращение вашей камеры составляет mainCam.transform.rotation, вы можете использовать этот код:

float target_angle = 90.0f; // assuming want to turn 'right'.

// direction of camera, in worldspace.
Vector3 cam_dir = mainCam.transform.forward;
// now transform to a direction vector in character's local space.
cam_dir = transform.InverseTransformDirection(cam_dir);
// ignore y part, take X/Z to get the angle.
// 0 degrees is forward, 90 deg is toward positive X, so normally right.
float cam_angle = Mathf.Atan2(cam_dir.x,cam_dir.z)*Mathf.Rad2Deg;

// angle we need to turn
float turn_angle = target_angle - cam_angle;
// [.....] do it now.

Использование InverseTransformXxx Функция () часто очень полезна, если вы пытаетесь мыслить локально. Вы также можете преобразовать положение камеры в пространство персонажа с помощью char.transform.InverseTransformPoint(mainCam.transform.position) и использовать его для справки.

0 голосов
/ 23 апреля 2020

Я бы посоветовал не использовать здесь углы в максимально возможной степени. Вместо этого вы можете использовать точечные продукты между правым направлением камеры и местным направлением проигрывателя, чтобы определить, какое местное направление наиболее соответствует прямому направлению камеры. Объяснение в комментариях.

void Right()
{
    float dotThreshold = Mathf.Sin(Mathf.PI * 0.25f); 

    // Take the dot products between the camera's right and  
    // each direction from the player. 
    // Either exactly one dot product will exceed this threshold 
    // (sin 45 degrees) or two will equal it.
    // Either way, when we see one dot product >= the threshold, 
    // we know what direction we should face.

    Vector3 camRight = mainCam.transform.right;

    if(Vector3.Dot(camRight, transform.right) >= dotThreshold) 
    {
        // camera's right ~ player's right
        animator.Play("StandQuarterTurnRight");
    }
    else if(Vector3.Dot(camRight, -transform.forward) >= dotThreshold) 
    {
        // camera's right ~ player's back
        animator.Play("StandHalfTurnRight");
    }
    else if(Vector3.Dot(camRight, -transform.right) >= dotThreshold)
    {
        // camera's right ~ player's left
        animator.Play("StandQuarterTurnLeft");
    }
    else
    {
        // camera's right ~ player's forward
        float forw = Input.GetAxis("Horizontal");
        if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
        else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
        animator.SetFloat("Speed", forw);
    }
}

Если вы не можете предположить, что плеер и камера имеют одинаковое направление оси y, вы должны проецироваться в одну плоскость, как в вопросе:

void Right()
{
    float dotThreshold = Mathf.Sin(Mathf.PI * 0.25f); 

    // Take the dot products between the camera's right and  
    // each direction from the player. 
    // Either exactly one dot product will exceed this threshold 
    // (sin 45 degrees) or two will equal it.
    // Either way, when we see one dot product >= the threshold, 
    // we know what direction we should face.

    Vector3 camRight = Vector3.ProjectOnPlane(mainCam.transform.right, Vector3.up);
    Vector3 playerRight = Vector3.ProjectOnPlane(transform.right, Vector3.up);
    Vector3 playerForward = Vector3.ProjectOnPlane(transform.forward, Vector3.up);

    if(Vector3.Dot(camRight, playerRight) >= dotThreshold) 
    {
        // camera's right ~ player's right
        animator.Play("StandQuarterTurnRight");
    }
    else if(Vector3.Dot(camRight, -playerForward) >= dotThreshold) 
    {
        // camera's right ~ player's back
        animator.Play("StandHalfTurnRight");
    }
    else if(Vector3.Dot(camRight, -playerRight) >= dotThreshold)
    {
        // camera's right ~ player's left
        animator.Play("StandQuarterTurnLeft");
    }
    else
    {
        // camera's right ~ player's forward
        float forw = Input.GetAxis("Horizontal");
        if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
        else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
        animator.SetFloat("Speed", forw);
    }
}
0 голосов
/ 23 апреля 2020

Вот небольшое исправление в вашем коде, которое настраивает ваш результат на работу, не меняя ничего другого:

void Right()
{
    Vector3 pVec = Vector3.ProjectOnPlane(transform.forward, Vector3.up);
    Vector3 cVec = Vector3.ProjectOnPlane(mainCam.transform.forward, Vector3.up);
    print(cVec);

    float angleA = Vector3.Angle(pVec, cVec); //Get the angle between the 2 vectors, projected on Y-plane
    float perspectiveAngle = Vector3.Angle(transform.right, cVec); //Get the angle between camera and player's right vector
    float angle = angleA; //In case angle is below 180, angle is AngleA

    if (perspectiveAngle > 90f) //If angle between player's right vector and camera is > 90, then we need to adjust the angle, as it is equal to or greater than 180
        angle = 360f - angleA;

    print(angle);
    if (angle >= 345 && angle <= 15)
    {
        animator.Play("StandQuarterTurnRight");
    }
    else if (angle >= 255 && angle <= 285)
    {
        animator.Play("StandHalfTurnRight");
    }
    else if (angle >= 165 && angle <= 195)
    {
        animator.Play("StandQuarterTurnLeft");
    }
    else if (angle >= 75 && angle <= 105)
    {
        float forw = Input.GetAxis("Horizontal");
        if (forw > 0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = 0.5f;
        else if (forw < -0.5f && !Input.GetKey(KeyCode.LeftShift)) forw = -0.5f;
        animator.SetFloat("Speed", forw);
    }
}

См. Комментарии выше, что-то вроде опишите, как это работает. Надеюсь, это поможет

...