unity实现3d摇杆 结合leapmotion控制实现VR场景移动


unity实现3d摇杆 结合leapmotion控制实现VR场景移动

Created by miccall (转载请注明出处 miccall.tech

3D UI设想 解决VR场景移动

  • 开发vr很恼火的就是场景的移动 真实地方本来就小,如果vr里面的场景也小,那还玩个啥,所以虚拟现实的场景是很大的 如何实现小空间对大空间的移动,这也是vr开发要解决的问题之一 。

  • 我们老大设想了摇杆操作 我就顺着思路做了一套 先看看基本效果

这里写图片描述

-当手移动中间的球时候 ,就像触碰摇杆一样 ,可以向四周摇动 。
外面的环是一个指示器 当球触发到环的时候,给一个颜色的变化,让用户知道自己将朝哪一个方向运动 。

这里写图片描述

  • 下面是一个围棋场地 用来感受自己在环境中的移动。

  • 摄像机 包括controller都在一个游戏物体上面 也很好移植 ,下面就一步步来展示这个的制作过程 。

3d摇杆的基本框架

这里写图片描述

  • 首先建立一个MianPivot的空物体 它代表着游戏人物 到时候可以自己设定 。
  • 其下呢 ,一个是VR视角 可以选用官方的LMHeadMountedRig 这个预制体 。
  • 另一个就是我门要做的这个摇杆了 。
  • 摇杆分为三部分 BoundaryRing 就是外面的一个指示环 。root是一个中心点 用来归位 sphere是一个移动的球,用手来触碰它 让它在空间中移动,实现摇杆效果 。

实现思路

  • 看过摇杆的自动归位 ,一开始是拿弹簧力来实现,但是弹簧回弹的惯性极其的难掌控 ,来回摆动简直让人抓狂 。于是, 果断放弃了unity的弹簧力 改自己写插值移动 ,让它在手不触碰他的时候,就让它移动到root这个点 。

  • 大概一分析就是这么简单的东西了,但是写起来还是比较烦的,bug极其的多,如何判断手的hold 如何判断移动 回弹的触发条件 等等一系列 做出好的效果还是很不容易的。

  • 还有就是状态的类,球是否是移动的状态 手是否是hold的状态 这个也得写个类来管理

  • 那么物体上呢 ,就很简单了 加一个触发器 当触发的时候 改变他们的状态 。

  • 大家自己建立两个球(一个root点 一个移动的球)并把他们放到一个父物体下面,就可以开始写脚本了。

开始写脚本

  • 先在父类上面建立一个Controller脚本 用来管理游戏移动对象, root和sphere 。
    好 那就先这样写

    public class springController : MonoBehaviour
    {
        [Tooltip("Navigation Controls the walking game object")]
        public GameObject player;
        [Tooltip("Used to move the navigation ball")]
        public GameObject sphere;
        [Tooltip("The root node of the navigation (reset point)")]
        public GameObject root;
        Vector3 sphereposition;
        Vector3 rootposition;
        float speed = 5f;  // 导航球复位的速度
        // Use this for initialization
        void Start()
        {
            //获取  初始位置
            sphereposition = sphere.transform.localPosition;
            rootposition = root.transform.localPosition;
        }
        // Update is called once per frame
        void Update()
        {
            //更新   导航球  的位置 
            sphereposition = sphere.transform.localPosition;
            Movecheck();      //判断并控制player的移动
            setstop();        //导航球归位
        }
    
        void setstop()
        {
            if (HandState.Handstate == HandState.HandStateRelease)
            {
                float step = speed * Time.deltaTime;
                sphere.GetComponent<Rigidbody>().velocity = Vector3.zero;
                sphere.transform.localPosition = new Vector3(Mathf.Lerp(sphereposition.x, rootposition.x, step), Mathf.Lerp(sphereposition.y, rootposition.y, step), Mathf.Lerp(sphereposition.z, rootposition.z, step));//插值算法也可以
                if (SphereState.Spherestate == SphereState.Spherestateclosed)
                    sphere.transform.localPosition = new Vector3(0, 0, 0);
            }
        }
    
        void debugshpereposition()
        {
            print("sphereposition" + sphereposition);
        }
    
        void Movecheck()
        {
            if(HandState.Handstate == HandState.HandStateHold)
            {
                startfollow();
            }
            else if(SphereState.Spherestate == SphereState.Spherestateclosed)
            {
                Stopfollow();
            }
        }
    
        void startfollow()
        {
            transform.parent.GetComponent<test>().enabled = true;
        }
        void Stopfollow()
        {
            transform.parent.GetComponent<test>().enabled = false;
        }
    
        void OnTriggerExit(Collider other)
        {
            if (issphere(other))
            {
                HandState.Handstate = HandState.HandStateRelease;
                setstop();
            }
        }
    
        bool issphere(Collider other)
        {
            return other.transform.name == "Sphere";
        }
    }

然后就是root和sphere上面 各有一个triiger判断的类

public class rootTrigger : SphereState
{

    Collider currentcollider;
    enum currentstate
    {
        colse,move
    }
    currentstate state; 
    void OnTriggerEnter(Collider collider)
    {
        currentcollider = collider;
        state = currentstate.colse;
        //spheredebug(collider, "in");
    }
    void OnTriggerExit(Collider collider)
    {
        currentcollider = collider;
        state = currentstate.move;
        //spheredebug(collider, "out");
    }

    public override bool isclosed()
    {
        if (Isphere(currentcollider))
        {
            if (state == currentstate.colse)
                return true;
            else return false;
        }
        return false;
    }

    private bool Isphere(Collider collider)
    {
        return collider.transform.name == "Sphere" ;
    }

    void spheredebug(Collider collider,string state)
    {
        if (Isphere(collider))
        {
            print("Sphere"+"   "+ state);
        }
    }
}

第二个

public class HnadTrigger : HandState
{
    int  handcount = 0 ;
    //public Text handcounttext ;
    void OnTriggerEnter(Collider other)
    {
        if(IShand(other))
        {
           //handdebug(other, "in");
           handcount++;
        }
    }

    void OnTriggerExit(Collider other)
    {
        if(IShand(other))
        {
           //handdebug(other, "out");
           handcount--;
        }
    }
    bool IShand(Collider other)
    {
        string Collidername = other.transform.parent.name;
        if (Collidername == "thumb") return true;
        else if (Collidername == "index") return true;
        else if (Collidername == "middle") return true;
        else if (Collidername == "thumb") return true;
        else if (Collidername == "RigidRoundHand_L" || Collidername == "RigidRoundHand_R") return true;
        else return false;
    }
    void handdebug(Collider other, string state)
    {
        print("hand" + state+" ++++++++++++" + Time.time);
    }

    public override bool ishold()
    {
        //print(" ishold() child ");
        //handcounttext.text = " count"+handcount;
        if (handcount != 0)
        {
            //print("is  hold");
            return true;
        }
        return false;
    }
}
  • 这个hold的状态也是搞了我很久 没办法处理判断hold和release两个状态
    最后想到的用碰撞器的数量来判断吧 ,当进入trigger就加加 出去就减减 最后数量为0 那么就是松开状态了
  • 仅供参考

  • 状态脚本

public class HandState : MonoBehaviour {

    public static int HandStateHold = 0 ;
    public static int HandStateRelease = 1;

    public static int Handstate = HandStateRelease ;
    public virtual bool ishold()
    {

        return true;
    }
    void setvalue()
    {
        if (ishold())
        {
            Handstate = HandStateHold;
        }
        else
           StartCoroutine(WaitAndPrint(0.5F));


    }
    IEnumerator WaitAndPrint(float waitTime)
    {
        yield return new WaitForSeconds(waitTime);
        //等待之后执行的动作 
        Handstate = HandStateRelease;
    }
    void Update()
    {
        setvalue();
    }
}
public class SphereState : MonoBehaviour {

    public static int Spherestateclosed = 0;
    public static int Spherestatemove = 1;

    public static int Spherestate = Spherestateclosed;

    public virtual bool isclosed()
    {
        return false;
    }
    void setvalue()
    {
        if (isclosed())
        {
            Spherestate = Spherestateclosed;
        }
        else
        {
            Spherestate = Spherestatemove;
        }
    }
    void Update()
    {
        setvalue();
    }
}
  • 这个就比较简单了 也就不多解释了 有想要源码的,可以留言联系我 。
  • 好了 ,今天就将怎么多啦 ,祝自己新年快乐。
评论 3 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

miccall

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值