0

我正在做一个 VR 项目,我正在努力做一些不应该那么难的事情,但我真的不擅长数学:x

我想做的是在世界上有“物理”按钮,我可以用手指按下/松开它,这部分效果非常好,感觉很完美,但是:

目前,要“按下”按钮,我使用的是控制器的 z delta 位置,所以我不能从顶部按下按钮,因为我需要 y delta。

我试图让按钮面向任何方向,并能够使用控制器的右轴推动它们,但无法绕开它:x

我需要能够根据它的位置/旋转按下按钮。

我不知道它是否真的可以理解(可能不是),也许我只是错过了一些非常明显的东西。

这是我的按钮代码,枚举部分可能没用,我只是在尝试,哈哈

using System;
using UnityEngine;

public class JVRButton : MonoBehaviour, IJVRFingerInteract
{

    [SerializeField] private Vector3 pressedPosition;
    [SerializeField] private Vector3 defaultPosition;
    [SerializeField] private Vector3 unpressPosition;

    [SerializeField] private LinearDragNormal direction;

    public bool Pressed { get; private set; }
    public event Action<bool> OnStateChange;

    private bool _changedState;
    private Transform _transform;
    private Vector3 _tmp;
    private float _delay;

    private float _delta; 

    private void Awake()
    {
        _transform = transform;
        _transform.localPosition = Pressed ? pressedPosition : defaultPosition;
    }

    private void Update()
    {
        _delay += Time.deltaTime;
        if (_delay < 0.1f) return;

        // "Spring" effect
        _transform.localPosition = Vector3.MoveTowards(_transform.localPosition, Pressed ? pressedPosition : defaultPosition, Time.deltaTime / 2);
    }

    public void JVRFingerInteract(JVRFinger jvrFinger)
    {
        Vector3 test = Quaternion.FromToRotation(jvrFinger.transform.forward, _transform.forward).eulerAngles;

        Debug.Log(test);

        switch(direction)
        {
            case LinearDragNormal.XPositive:
                _delta = Mathf.Min(jvrFinger.JVRController.DeltaPositionLocal.z, 0);
                break;
            case LinearDragNormal.XNegative:
                _delta = Mathf.Max(jvrFinger.JVRController.DeltaPositionLocal.z, 0);
                break;
            case LinearDragNormal.YPositive:
                _delta = Mathf.Max(jvrFinger.JVRController.DeltaPositionLocal.y, 0);
                break;
            case LinearDragNormal.YNegative:
                _delta = Mathf.Min(jvrFinger.JVRController.DeltaPositionLocal.y, 0);
                break;
             case LinearDragNormal.ZPositive:
                _delta = Mathf.Min(jvrFinger.JVRController.DeltaPositionLocal.x, 0);
                break;
            case LinearDragNormal.ZNegative:
                _delta = Mathf.Max(jvrFinger.JVRController.DeltaPositionLocal.x, 0);
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        MoveButton(Pressed ? unpressPosition : pressedPosition);
    }

    private void MoveButton(Vector3 position)
    {
        if (_changedState && _delay < 0.5f) return;
        _delay = 0;
        _changedState = false;            
        _tmp = _transform.localPosition;
        _tmp = Vector3.MoveTowards(_tmp, position, _delta);
        if (_tmp.x < position.x) _tmp.x = position.x;
        if (_tmp.y < position.y) _tmp.y = position.y;
        if (_tmp.z < position.z) _tmp.z = position.z;
        _transform.localPosition = _tmp;

        if (_transform.localPosition == pressedPosition)
        {
            Pressed = true;
            _changedState = true;
            OnStateChange?.Invoke(Pressed);
        }
        else if (_transform.localPosition == unpressPosition)
        {
            Pressed = false;
            _changedState = true;
            OnStateChange?.Invoke(Pressed);
        }
    }    
}

public enum LinearDragNormal
{
    XPositive,
    XNegative,
    YPositive,
    YNegative,
    ZPositive,
    ZNegative
}

JVRFingerInteract 在我的手指触摸按钮的每一帧都被调用,我只是在手指上做一个重叠球来获得可交互的对象。

按钮的推动轴是按钮的局部 Z 轴,正 Z 指向按钮表面。

4

2 回答 2

1

好的,我通过尝试找到了解决方案,我不明白它的工作原理和原因,但它确实......

_delta = (_forward.x * jvrPos.z + _forward.y * -jvrPos.y + _forward.z * -jvrPos.x);
if (_delta < 0) _delta = 0;

_forward 是按钮前进,jvrPos 是我的控制器/手指的本地增量

于 2019-09-25T08:38:33.413 回答
0

假设您的按钮表面面向按钮的本地up方向:

按钮的局部轴

然后,您可以在世界空间中的按钮和世界空间中的增量之间使用点积up来确定手指在该方向上移动了多少:

Vector3 worldFingerDelta;
Transform buttonTransform;

float deltaInButtonDown = Vector3.Dot(worldFingerDelta, 
        -buttonTransform.up);

// when deltainButtonDown is positive, the finger is moving in the 
// same direction as "pushing the button" 
// When it is negative, the finger is moving in the opposite direction.
// The magnitude of deltaInButtonDown is how much the finger is 
// moving in that direction.

按钮的局部轴

如果一个不同的局部方向指向按钮的表面,例如它的负方向forward,您只需在世界空间中使用相应的局部向量:

负前锋指向按钮外

float deltaInButtonDown = Vector3.Dot(worldFingerDelta, 
        buttonTransform.forward); // negatives cancel out

此外,如果按钮所在的表面在世界空间中移动,您将需要使用Vector3.Dot(worldFingerDelta - worldButtonSurfaceDelta, buttonTransform.forward);以允许按钮通过移动到静止的手指来按下自身。


关于你的回答......

_delta = (_forward.x * jvrPos.z + _forward.y * -jvrPos.y 
        + _forward.z * -jvrPos.x);
if (_delta < 0) _delta = 0; 

_forward作为按钮前进,并且jvrPos是我的控制器/手指的本地增量

就矢量数学而言,在对其应用某种反转/反射变换后,您正在按钮的前向和局部增量之间进行点积。所以另一种写答案的方法是:

new Vector3 vec = new Vector3(jvrPos.z, -jvrPos.y, -jvrPos.x);
_delta = Vector3.Dot(vec, buttonTransform.forward);

正如我们在您的答案的评论中发现的那样,该转换基于控制器父级的世界旋转,将您的本地增量变为世界增量的负值。因此,通过将向量的负数取为负数并在Dot通话中将其设为负数,另一种写答案的方法是:

new Vector3 calculatedWorldFingerDelta = new Vector3(-jvrPos.z, jvrPos.y, jvrPos.x);
_delta = Vector3.Dot(-calculatedWorldFingerDelta, buttonTransform.forward);

由于 A 和 -B 的点积等于 -A 和 B 的点积,因此您的答案相当于:

new Vector3 calculatedWorldFingerDelta = new Vector3(-jvrPos.z, jvrPos.y, jvrPos.x);
_delta = Vector3.Dot(calculatedWorldFingerDelta, -buttonTransform.forward);

其中,如果calculatedWorldFingerDelta等于worldFingerDelta,则与上述按钮的公式相同,正 z 方向指向按钮表面。

我会worldFingerDelta直接使用,因为如果手指父级的世界旋转或位置发生变化,那么您现在使用的转换将不能保证给您calculatedWorldFingerDeltaequals worldFingerDelta,并且您的答案将不再有效。

于 2019-09-25T14:24:59.910 回答