欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity VR射线拖拽物体时候的曲线效果

程序员文章站 2022-07-13 22:29:53
...

前言

作为一个Unity开发的萌新小白,也是抱着和同行交流的心态,所以想把自己开发项目中的一些新知识点记录分享一下,也许会对别人有所帮助。这也是第一次尝试写博客,存在错误的地方也希望读者帮助指出,大家一起成长。

实现功能

最近正在做诺亦腾数据手套方面的交互,涉及到一些射线的使用,比如双手拖动平移物体、单手拖动旋转物体、双手缩放物体等通用的射线和手势配合交互。其中我现在想分享的是射线拖动物体旋转、在这里我物体旋转使用了贝塞尔曲线的二阶公式实现拖拽时候的曲线效果,选中要拖拽的物体按下空格键,通过ASDW按键控制方向,下面是相关脚本。
Unity VR射线拖拽物体时候的曲线效果
HandRayFocusUpdate脚本主要是挂在手柄上,实现射线的绘制和碰撞检测。
public class HandRayFocusUpdate : MonoBehaviour
{

private Vector3 mRayDire;//射线的方向
[HideInInspector]
public RaycastHit mRaycastHit;//碰撞信息
private IHandRayHoverInteractive mIHandHoverInteractive = null;//记录当前选中的物体
private Transform mFocusPoint;//射线末端的小球
private LineRenderer mLineRenderer;
private bool mIsDrag = false;
private Vector3 mDragPoint;//开始拖动时候的物体碰撞点的本地坐标
private Vector3 mRayOriginPoint;//发射射线的点
public KeyCode mKeyCode;
public bool mIsLeftHand;
private float mCurrentAngleY;
private float mCurrentAngleX;
void Start()
{
    mLineRenderer = GetComponent<LineRenderer>();//获取LineRender
    mFocusPoint = Instantiate(Resources.Load<GameObject>("FocusPoint")).transform;//加载Resources下面名字为FocusPoint的预制体
}
//绘制曲线
void DrawLineRenderCurve(LineRenderer lineRenderer, Vector3[] points)
{
    for (int i = 1; i <= 100; i++)
    {
        float t = i / (float)100;
        Vector3 pixel = CalculateBezierPoint(t, points[0], points[1], points[2]);
        lineRenderer.positionCount = i;
        lineRenderer.SetPosition(i - 1, pixel);
    }

}
//计算贝塞尔曲线的点
Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
    float u = 1 - t;
    float tt = t * t;
    float uu = u * u;
    Vector3 p = uu * p0;
    p += 2 * u * t * p1;
    p += tt * p2;
    return p;
}
/// <summary>
/// 更新末端小球的位置。并且重新设置射线的方向
/// </summary>
/// <param name="point"> 末端小球的新位置</param>
public void UpdateFocusPoint(Vector3 point, bool isDrag)
{
    mRayOriginPoint = transform.position;
    Vector3[] middlePoint = new Vector3[3];
    middlePoint[0] = mRayOriginPoint;
    middlePoint[1] = mRayOriginPoint;
    middlePoint[2] = point;
    mRayDire = transform.forward;
    if (isDrag)
    {
        middlePoint[2] = mRaycastHit.transform.TransformPoint(mDragPoint);
        middlePoint[1] = mRayOriginPoint + (mRayDire).normalized * ((middlePoint[2] - mRayOriginPoint).magnitude);
        DrawLineRenderCurve(mLineRenderer, middlePoint);
        mFocusPoint.position = middlePoint[2];
        mFocusPoint.transform.localScale = Vector3.one * (Mathf.Clamp((Vector3.Magnitude(point - mRayOriginPoint) / 100), 0.03f, 0.08f));//计算小球的缩放
    }
    else
    {
        DrawLineRenderCurve(mLineRenderer, middlePoint);
        mFocusPoint.position = point;
        mFocusPoint.transform.localScale = Vector3.one * (Mathf.Clamp((Vector3.Magnitude(point - mRayOriginPoint) / 100), 0.03f, 0.08f));//计算小球的缩放

    }
}


// Update is called once per frame
void Update()
{
    ///
     //控制手的旋转
    float x = Input.GetAxis("Horizontal");
    float y = Input.GetAxis("Vertical");

    mCurrentAngleY += x;
    mCurrentAngleX += y;
    Mathf.Clamp(mCurrentAngleY,-90,90);
    Mathf.Clamp(mCurrentAngleX, -90, 90);
    transform.localEulerAngles = new Vector3(-mCurrentAngleX, mCurrentAngleY,0) ;
    ///

    if (mIsDrag)
    {
        UpdateFocusPoint(mRaycastHit.point, true);
        if (Input.GetKeyUp(mKeyCode))
        {
            mIsDrag = false;
        }
    }
    else
    {
        UpdateState();
    }
}

private void HoverEnter()
{
    mIHandHoverInteractive.SendMessage("HoverEnter", this);
}
private void Hover()
{
    mIHandHoverInteractive.SendMessage("Hover", this);
    if (Input.GetKeyDown(mKeyCode))
    {
        HoverKeyDown();
    }
    else
    {
        if (Input.GetKey(mKeyCode))
        {
            HoverKey();
            if (!mIsDrag)
            {
                mDragPoint = mIHandHoverInteractive.transform.InverseTransformPoint(mRaycastHit.point);
                mIsDrag = true;
            }
        }
        else
        {
            if (Input.GetKeyUp(mKeyCode))
            {
                HoverKeyUp();
            }
        }
    }
}
private void HoverExit()
{
    mIHandHoverInteractive.SendMessage("HoverExit", this);
}
private void HoverKeyDown()
{
    mIHandHoverInteractive.SendMessage("HoverKeyDown", this);
}
private void HoverKey()
{
    mIHandHoverInteractive.SendMessage("HoverKey", this);
}
private void HoverKeyUp()
{
    mIHandHoverInteractive.SendMessage("HoverKeyUp", this);
}
//更新射线的状态
private void UpdateState()
{

    mRayOriginPoint = transform.position;
    mRayDire = transform.forward;

    Ray ray = new Ray((mRayOriginPoint), mRayDire);//从手腕的位置向前发射一条射线

    if (Physics.Raycast(ray, out mRaycastHit,Mathf.Infinity))
    {
        UpdateFocusPoint(mRaycastHit.point, false);//实时更新小球得位置

        IHandRayHoverInteractive mUI_InteractiveBaseTemp = mRaycastHit.transform.GetComponent<IHandRayHoverInteractive>(); //获取当前碰撞的物体上面的UI_InteractiveBase脚本

        if (mUI_InteractiveBaseTemp != null) //如果获取的脚本不为空
        {
            if (mIHandHoverInteractive == null) //如果mUI_InteractiveBase为空,表示刚进入,调用对应的进入方法
            {
                mIHandHoverInteractive = mUI_InteractiveBaseTemp;
                HoverEnter();
            }
            else
            {
                if (mIHandHoverInteractive == mUI_InteractiveBaseTemp)
                {

                    Hover(); 
                }
                else
                {
                    mIHandHoverInteractive.HoverExit(this);
                    mIHandHoverInteractive = mUI_InteractiveBaseTemp;
                    HoverEnter();
                }
            }
        }
        else
        {
            if (mIHandHoverInteractive != null)
            {
                mIHandHoverInteractive.HoverExit(this);
                mIHandHoverInteractive = null;
            }
        }
    }
    else
    {
        if (mIHandHoverInteractive != null)
        {
            HoverExit();
            mIHandHoverInteractive = null;
        }
        UpdateFocusPoint(mRayOriginPoint + mRayDire * 5, false);
    }
}

}

IHandRayHoverInteractive:需要射线检测的物体继承于它,主要起到发送碰撞检测消息的作用。
//IHandRayHoverInteractive

public class IHandRayHoverInteractive:MonoBehaviour
{

internal virtual void HoverEnter(HandRayFocusUpdate hand) {

}
internal virtual void Hover(HandRayFocusUpdate hand) { 

}
internal virtual void HoverExit(HandRayFocusUpdate hand) { 

}
internal virtual void HoverKeyDown(HandRayFocusUpdate hand) { 

}
internal virtual void HoverKey(HandRayFocusUpdate hand) { 

}
internal virtual void HoverKeyUp(HandRayFocusUpdate hand) { 

}

}

// ObjRotateInteractive e类主要实现射线拖动物体旋转的具体实现过程
public class ObjRotateInteractive : IHandRayHoverInteractive
{
private HandRayFocusUpdate mHand = null;

private Vector3 mHandEular=Vector3.zero;//上一次的角度
private float mAngleDelta=0;//旋转角度的增量
void  Update()
{


    if (mHand != null)
    {
       
        if (Input.GetKeyUp(mHand.mKeyCode))
        {
            mHand = null;
        }
        else
        {
            mAngleDelta = mHandEular.y-mHand.transform.eulerAngles.y ;
            mHandEular =mHand.transform.eulerAngles;
            transform.rotation *= Quaternion.Euler(Vector3.up * mAngleDelta);//计算新的旋转位置
        }
    }
}

internal override void HoverKeyDown(HandRayFocusUpdate hand)
{

    if (mHand == null)
    {
        mHand = hand;
        mHandEular = mHand.transform.eulerAngles;//记录按下时候的角度
       
    }
}

}

总结

上面的代码只是简单的逻辑实现。
工程链接://download.csdn.net/download/qq_33547099/12079495

相关标签: VR