夹紧旋转/四元数

时间:2019-12-18 13:08:50

标签: c# unity3d math quaternions

仍在进行VR交互,我希望能够旋转对象,但是面临一个问题。

例如,我想用我的手在VR中打开/关闭笔记本电脑的上部。我正在实现这一目标的方法是:

laptopForward

我正在使用向前,向上的位置来创建飞机。然后获取飞机上与我的VR控制器相对应的最近点,然后使用transform.LookAt。

这工作正常,但是我希望能够固定旋转,所以我不能旋转太多(请参见视频结尾)。

我一直在尝试使用eulersAngle和Quaternion进行所有操作,但是我做不到。

我做了一些帮助(显示localEulerAngles的文本,以及对LookAt的转换,因此我不必使用VR头盔,因为它变得非常乏味)

以下是显示正在发生的事情的视频:https://www.youtube.com/watch?v=UfN97OpYElk

这是我的代码:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.wildfly.quickstarts</groupId>
    <artifactId>quickstart-parent</artifactId>
    <!--
    Maintain separation between the artifact id and the version to help prevent
    merge conflicts between commits changing the GA and those changing the V.
    -->
    <version>19.0.0.Beta1-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
</parent>
<artifactId>helloworld</artifactId>
<packaging>war</packaging>
<name>Quickstart: helloworld</name>
<description>Helloworld</description>

<licenses>
    <license>
        <name>Apache License, Version 2.0</name>
        <distribution>repo</distribution>
        <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
    </license>
</licenses>

<dependencies>

    <!-- Import the CDI API, we use provided scope as the API is included in WildFly -->
    <dependency>
        <groupId>jakarta.enterprise</groupId>
        <artifactId>jakarta.enterprise.cdi-api</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- Import the Common Annotations API (JSR-250), we use provided scope
        as the API is included in WildFly -->
    <dependency>
        <groupId>org.jboss.spec.javax.annotation</groupId>
        <artifactId>jboss-annotations-api_1.3_spec</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- Import the Servlet API, we use provided scope as the API is included in WildFly -->
    <dependency>
        <groupId>org.jboss.spec.javax.servlet</groupId>
        <artifactId>jboss-servlet-api_4.0_spec</artifactId>
        <scope>provided</scope>
    </dependency>

</dependencies>

2 个答案:

答案 0 :(得分:1)

假设监视器的父转换是笔记本电脑的主体/键盘。父级的局部轴如下所示:

local axes of parent

要描述运动范围,您可以定义父体局部的“旋转中心”矢量(例如标为C的灰色矢量)以及每个紫色矢量和灰色矢量之间的角度(例如110度) )。例如:

[SerializeField] private Vector3 LocalRotationRangeCenter = new Vector3(0f, 0.94f, 0.342f);
[SerializeField] private float RotationRangeExtent = 110f;

然后,您可以将其“想要”的前向矢量移走,找到RotationRangeCenter的世界方向和该点之间的正负角,然后将其钳位为±RotationRangeExtent

Vector3 worldRotationRangeCenter = toRotate.parent.TransformDirection(RotationRangeCenter);

Vector3 targetForward = _targetPosition - toRotate.position;

float targetAngle = Vector3.SignedAngle(worldRotationRangeCenter, targetForward, 
        toRotate.right);

float clampedAngle = Mathf.Clamp(targetAngle, -RotationRangeExtent, RotationRangeExtent);

然后,找到与该角度对应的方向。最后,旋转显示器,使其前部与夹紧的前部对齐,并且其右侧不改变。您可以使用叉积查找显示器的状态,然后使用Quaternion.LookRotation查找相应的旋转角度:

Vector3 clampedForward = Quaternion.AngleAxis(clampedAngle, toRotate.right)
        * worldRotationRangeCenter;

toRotate.rotation = Quaternion.LookRotation(clampedForward, 
        Vector3.Cross(clampedForward, toRotate.right));

如果有人试图将监视器拖到“边界”之外,它将从一个限制转移到另一个限制。如果这不是您想要的行为,则可以考虑从SignedAngle(worldRotationRangecenter, targetForward, toRotate.right)插值到clampedAngle,以在两个极限之间移动:

private float angleChangeLimit = 90f; // max angular speed

// ...

Vector3 worldRotationRangeCenter = toRotate.parent.TransformDirection(RotationRangeCenter);

Vector3 targetForward = _targetPosition - toRotate.position;

float targetAngle = Vector3.SignedAngle(worldRotationRangeCenter, targetForward, 
        toRotate.right);

float clampedAngle = Mathf.Clamp(targetAngle, -RotationRangeExtent, RotationRangeExtent);

float currentAngle = Vector3.SignedAngle(worldRotationRangeCenter, toRotate.forward, 
        toRotate.right);

clampedAngle = Mathf.MoveTowards(currentAngle, clampedAngle, 
        angleChangeLimit * Time.deltaTime);

Vector3 clampedForward = Quaternion.AngleAxis(clampedAngle, toRotate.right)
        * worldRotationRangeCenter;

toRotate.rotation = Quaternion.LookRotation(clampedForward, 
        Vector3.Cross(clampedForward, toRotate.right));

答案 1 :(得分:0)

@Ruzihm的答案只需要一点调整即可!我不能诚实地做到这一点。

如果有人感兴趣,这里是VR更新的完整代码:

using UnityEngine;

public class JVRLookAtRotation : MonoBehaviour, IJVRControllerInteract
{

    [SerializeField] private Transform toRotate;

    [SerializeField] private Vector3 minRotationDelta;
    [SerializeField] private Vector3 maxRotationDelta;

    private JVRController _jvrController;
    private bool _isGrabbed;
    private Vector3 _targetPosition;

    // No clue where does this come from
    private Vector3 _localRotationRangeCenter = new Vector3(0, 0.999f, 0.044f);

    private void LateUpdate()
    {

        if (!_isGrabbed) return;

        if (_jvrController.Grip + _jvrController.Trigger < Rules.GrabbingThreshold)
        {
            _isGrabbed = false;
            _jvrController.StopGrabbing();
            _jvrController = null;
            return;
        }

        Vector3 up = toRotate.up;
        Vector3 forward = toRotate.forward;
        Vector3 right = toRotate.right;
        Vector3 rotatePosition = toRotate.position;
        Vector3 pos1 = rotatePosition + up;
        Vector3 pos2 = rotatePosition + forward;
        Plane p = new Plane(rotatePosition, pos1, pos2);
        _targetPosition = p.ClosestPointOnPlane(_jvrController.CurrentPositionWorld);

        Vector3 worldRotationRangeCenter = toRotate.parent.TransformDirection(_localRotationRangeCenter);
        Vector3 targetForward = _targetPosition - rotatePosition;

        float targetAngle = Vector3.SignedAngle(worldRotationRangeCenter, targetForward, right);

        float clampedAngle = Mathf.Clamp(targetAngle, minRotationDelta.x, maxRotationDelta.x);

        Vector3 clampedForward = Quaternion.AngleAxis(clampedAngle, right) * worldRotationRangeCenter;

        toRotate.rotation = Quaternion.LookRotation(clampedForward, Vector3.Cross(clampedForward, right));

    }

    public void JVRControllerInteract(JVRController jvrController)
    {
        if (_isGrabbed) return;
        if (!(jvrController.Grip + jvrController.Trigger > Rules.GrabbingThreshold)) return;

        _jvrController = jvrController;
        _jvrController.SetGrabbedObject(this);
        _isGrabbed = true;
    }
}