Google Cardboard、 GVR Unity3D 开发

2016年,公司让开发google cardboard VR,下载google cardboard sdk并研究了一下,下面把自己的感受写出来,牢记一下过去的知识,也算共享。首先,请大家上网上下载google VR SDK,过去叫cardbord,现在叫GVR。sdk的下载请上https://developers.google.cn/vr/下载。现在google 已经将VR 发展为DayDream了,VR会越来越火啦。

Unity3d 的版本一定要用5.3.3f1版本,不然,发布出来的app会一直增长内存。

公司让我研究google VR 相机围绕目标点旋转,以鸟瞰俯视的角度来观看场景,点击 按钮,相机会平滑移动到某一位置观察场景。经过研究,google VR 是不能够改变角度位置的,不能修改GVRHead.cs脚本中的 GvrViewer.Instance.HeadPose.Orientation,为了实现围绕 中心点 旋转,我特意找到了类似 魔兽、仙剑奇侠传的相机功能的脚本WowMainCamera.cs。下面将WowMainCamera.csde 代码贴上

using UnityEngine;
using System.Collections;

public class WowMainCamera : MonoBehaviour
{
    public bool HideAndShowCursor = true;
    public bool LockRotationWhenRightClick = false;
    public bool UseBlurEffect = true;
    public bool UseFogEffect = true;
    public Transform target;
   
    public float targetHeight = 1.0f;
    public float distance = 5.0f;

    public float maxDistance = 20;
    public float minDistance = .6f;

    public float xSpeed = 250.0f;
    public float ySpeed = 120.0f;

    public int yMinLimit = -80;
    public int yMaxLimit = 80;

    public int zoomRate = 40;

    public float rotationDampening = 3.0f;
    public float zoomDampening = 10.0f;

    private float x = 0.0f;
    private float y = 0.0f;
    private float currentDistance;
    private float desiredDistance;
    private float correctedDistance;
    private bool grounded = false;
    /// <summary>
    /// 控制相机状态切换
    /// </summary>
    bool State;
    /// <summary>
    /// 外部调用
    /// </summary>
    public bool _State
    {
        get { return State; }
        set { State = value; }
    }

    void Start ()
    {
        Vector3 angles = transform.eulerAngles;

        x = angles.x;
        y = angles.y;

        currentDistance = distance;
        desiredDistance = distance;
        correctedDistance = distance - 0.2f;

        // Make the rigid body not change rotation
        if (rigidbody)
        {
            rigidbody.freezeRotation = true;
        }
    }

    void LateUpdate ()
    {
        if(HideAndShowCursor)
        {
            if (Input.GetMouseButton(1) || Input.GetMouseButton(2))
            {
                Screen.lockCursor = true;
            }
            else
            {
                Screen.lockCursor = false;
            }
        }
        // Don't do anything if target is not defined
        if (!target)
        {
            GameObject go = GameObject.Find("Player");
            target = go.transform;
            transform.LookAt(target);
            return;
        }
        // If either mouse buttons are down, let the mouse govern camera position
        if (Input.GetMouseButton(1) || Input.GetMouseButton(2))
        {
            if (LockRotationWhenRightClick == false)
            {
                x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;

                //控制一直摁住鼠标右键,镜头向前推进
                if (!State)
                {
                    y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
                }
            }
        }        
        // otherwise, ease behind the target if any of the directional keys are pressed
        else if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
        {
            float targetRotationAngle = target.eulerAngles.y;
            float currentRotationAngle = transform.eulerAngles.y;
            x = Mathf.LerpAngle(currentRotationAngle, targetRotationAngle, rotationDampening * Time.deltaTime);
        }

        y = ClampAngle(y, yMinLimit, yMaxLimit);

        // set camera rotation
        Quaternion rotation = Quaternion.Euler(y, x, 0);

        #region  反方向控制角色的方向

        /*
         * 2013.3.11新增代码 反方向控制角色(Charactor)与第三人称相机旋转方向一致,可以使相机、角色方向始终超前
         */

        Quaternion TargetRotation = Quaternion.Euler(0, x, 0);
        if (target != null)
        {
            target.rotation = TargetRotation;
        }

        #endregion

        desiredDistance -= Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * zoomRate * Mathf.Abs(desiredDistance);
        desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);

        

        correctedDistance = desiredDistance;

        // calculate desired camera position
        Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + new Vector3(0, -targetHeight, 0));

        // check for collision using the true target's desired registration point as set by user using height
        RaycastHit collisionHit;
        Vector3 trueTargetPosition = new Vector3(target.position.x, target.position.y + targetHeight, target.position.z);

        // if there was a collision, correct the camera position and calculate the corrected distance
        bool isCorrected = false;

        if (Physics.Linecast(trueTargetPosition, position, out collisionHit))
        {
            position = collisionHit.point;
            correctedDistance = Vector3.Distance(trueTargetPosition, position);
            isCorrected = true;
        }
        
        // For smoothing, lerp distance only if either distance wasn't corrected, or correctedDistance is more than currentDistance
        currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp(currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance;

        // recalculate position based on the new currentDistance
        position = target.position - (rotation * Vector3.forward * currentDistance + new Vector3(0, -targetHeight - 0.05f, 0));

        transform.rotation = rotation;
        transform.position = position;
        
    }

    private static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}


我将GVRHead.cs修改了一下,实现了围绕中心点旋转又能通过点击VR UI 中的按钮,实现 相机的旋转、相机的拉近拉远,下面将代码贴上


using UnityEngine;


[AddComponentMenu("GoogleVR/GvrHead")]
public class GvrHead : MonoBehaviour
{
    public bool trackRotation = true;

    public bool trackPosition = false;

    public Transform target;
    public AngleData angleData;

    public bool updateEarly = false;

    /// Returns a ray based on the heads position and forward direction, after making
    /// sure the transform is up to date.  Use to raycast into the scene to determine
    /// objects that the user is looking at.
    public Ray Gaze
    {
        get
        {
            UpdateHead();
            return new Ray(transform.position, transform.forward);
        }
    }

    public delegate void HeadUpdatedDelegate(GameObject head);

    /// Called after the head pose has been updated with the latest sensor data.
    public event HeadUpdatedDelegate OnHeadUpdated;


    private static GvrHead instance;

    public static GvrHead Instance
    {
        get { return instance; }
    }
    
    public bool isOverrideOribit = true;
    private bool updated;

    Quaternion rot=Quaternion.identity;
    public float rotLimitTime = 1.0f;
    public float targetHeight = 1.0f;
    public float distance = 5.0f;
    public float maxDistance = 20;
    public float minDistance = .6f;
    public int yMinLimit = -80;
    public int yMaxLimit = 80;
    private float x = 0.0f;
    private float y = 0.0f;
    public float zoomRate = 40;
    public float zoomDampening = 10.0f;
    [SerializeField]
    private float currentDistance;
    public float desiredDistance;
    [SerializeField]
    private float correctedDistance;
    private bool grounded = false;
    private float scrollWheel;
    public ScrollWheelDatas scrollWheelDatas;
    private bool isMove;
    public float moveTime = 1.5f;
    private CamMoveDatas middlePoint;
    private bool desiredDistanceMove;
    private float desiredMoveTime;
    public float lastDesiredDistance;

    void Awake()
    {
        GvrViewer.Create();

        if (!instance)
        {
            instance = this;
        }
    }

    private void Start()
    {
        if (this.enabled)
        {
            currentDistance = distance;
            desiredDistance = distance;
            correctedDistance = distance - 0.2f;

            if (GetComponent<Rigidbody>())
            {
                GetComponent<Rigidbody>().freezeRotation = true;
            }

            scrollWheelDatas.scrollWheelAdd = scrollWheelDatas.scrollWheelMax;
        }
    }

    private void Update()
    {
        updated = false;  // OK to recompute head pose.

        if (updateEarly)
        {
            UpdateHead();
        }
    }

    // Normally, update head pose now.
    private void LateUpdate()
    {
        UpdateHead();
    }

   // Compute new head pose.
    private void UpdateHead()
    {
        // Don't do anything if target is not defined
        if (!target&& updated)
        {
            return;
        }

        updated = true;

        GvrViewer.Instance.UpdateState();

        if (trackRotation)
        {
            if (isMove)
            {
                if (middlePoint != null)
                {

                    angleData.x = Mathf.Lerp(angleData.x, middlePoint.x, moveTime * Time.deltaTime);

                    angleData.y = Mathf.Lerp(angleData.y, middlePoint.y, moveTime * Time.deltaTime);

                    desiredDistance = Mathf.Lerp(desiredDistance, middlePoint.desiredDistance, moveTime * Time.deltaTime);

                    if ((Mathf.Abs(angleData.x - middlePoint.x) <= 0.01f) && (Mathf.Abs(angleData.y - middlePoint.y) <= 0.01f) && (Mathf.Abs(desiredDistance - middlePoint.desiredDistance) <= 0.01f))
                    {
                        angleData.x = middlePoint.x;
                        angleData.y = middlePoint.y;
                        desiredDistance = middlePoint.desiredDistance;
                        isMove = false;
                    }
                }

            }

            if (desiredDistanceMove)
            {
                lastDesiredDistance = Mathf.Clamp(lastDesiredDistance, minDistance, maxDistance);

                desiredDistance = Mathf.Lerp(desiredDistance, lastDesiredDistance, desiredMoveTime * Time.deltaTime);

                if ((Mathf.Abs(desiredDistance - lastDesiredDistance) <= 0.01f))
                {
                    desiredDistance = lastDesiredDistance;
                    desiredDistanceMove = false;
                }

            }


            Quaternion limitRotation = GvrViewer.Instance.HeadPose.Orientation*Quaternion.Euler(angleData.x, angleData.y, 0);

            rot = Quaternion.Slerp(rot, limitRotation, rotLimitTime);

            if (isOverrideOribit)
            {

                x = rot.eulerAngles.y;
                y = rot.eulerAngles.x;
                y = ClampAngleX(y, yMinLimit, yMaxLimit);

                Quaternion rotation = Quaternion.Euler(y, x, 0);

                #region  反方向控制角色的方向

                /*
                    * 2013.3.11新增代码 反方向控制角色(Charactor)与第三人称相机旋转方向一致,可以使相机、角色方向始终超前
                    */

                Quaternion TargetRotation = Quaternion.Euler(0, x, 0);

                if (target != null)
                {
                    target.rotation = TargetRotation;
                }

                #endregion

                #region 限制鼠标中轮向前推进

                desiredDistance -= scrollWheel * Time.deltaTime * zoomRate;
                desiredDistance = Mathf.Clamp(desiredDistance, minDistance, maxDistance);
               
                #endregion

                correctedDistance = desiredDistance;

                Vector3 position = target.position - (rotation * Vector3.forward * desiredDistance + new Vector3(0, -targetHeight, 0));
                    
                #region 当相机遇到碰撞后,相机收缩

                RaycastHit collisionHit;

                Vector3 trueTargetPosition = new Vector3(target.position.x, target.position.y + targetHeight, target.position.z);

                bool isCorrected = false;

                if (Physics.Linecast(trueTargetPosition, position, out collisionHit))
                {
                    position = collisionHit.point;
                    correctedDistance = Vector3.Distance(trueTargetPosition, position);
                    isCorrected = true;
                }


                #endregion

                currentDistance = !isCorrected || correctedDistance > currentDistance ? Mathf.Lerp(currentDistance, correctedDistance, Time.deltaTime * zoomDampening) : correctedDistance;

                position = target.position - (rotation * Vector3.forward * currentDistance + new Vector3(0, -targetHeight - 0.05f, 0));

                transform.rotation = rotation;
                transform.position = position;
            }
            else
            {
                transform.rotation = Quaternion.Euler(rot.eulerAngles.x, rot.eulerAngles.y, 0);
            }

        }

        if (OnHeadUpdated != null)
        {
            OnHeadUpdated(gameObject);
        }
        
    }

    public void VRCameraMoveForward()
    {
        scrollWheelDatas.isForward = true;
        scrollWheelDatas.isReverse = false;
        scrollWheel = 0;
        scrollWheel += scrollWheelDatas.scrollWheelAdd;
    }

    public void VRCameraMoveReverse()
    {
        scrollWheelDatas.isForward = false;
        scrollWheelDatas.isReverse = true;
        scrollWheel = 0;
        scrollWheel -= scrollWheelDatas.scrollWheelAdd;
    }

    public void CameraMove(CamMoveDatas camMoveDatas)
    {
        isMove = true;
        middlePoint = camMoveDatas;
    }

    public void CameraDesiredMove(float  targetDistance,float time=1.5f)
    {
        desiredDistanceMove = true;
        lastDesiredDistance = targetDistance;
        desiredMoveTime = time;
    }

    public void SetDistances(float value)
    {
        currentDistance = desiredDistance = correctedDistance = value;
    }
    private  float ClampAngleX(float angle, float min, float max)
    {
        if (angle < 0)
            angle -= angle * 2;
        if (angle > 90)
            angle -= ((angle - 90) * 2);
        return Mathf.Clamp(angle, min, max);
    }



}
[System.Serializable]
public class ScrollWheelDatas
{
    public bool isForward;
    public bool isReverse;
    public float scrollWheelMax;
    public float scrollWheelMin;
    public float scrollWheelAdd;
}
[System.Serializable]
public class AngleData
{
    public float x;
    public float y;
}
[System.Serializable]
public class CamMoveDatas
{
    public float x;
    public float y;
    public float desiredDistance;
    
}


测试调用代码  CameraJump.cs

using UnityEngine;
using System.Collections;

public class CameraJump : MonoBehaviour
{
    public GvrHead gvrhead;
    
    public CamMoveDatas original;
    public CamMoveDatas point;

    

    private void Start()
    {
        
    }
    


    void OnGUI()
    {
        if (GUILayout.Button("位置1", GUILayout.MinWidth(200), GUILayout.MinHeight(80)))
        {
            gvrhead.CameraMove(point);
        }

        if (GUILayout.Button("Original", GUILayout.MinWidth(200), GUILayout.MinHeight(80)))
        {
            gvrhead.CameraMove(original);
        }


        if (GUILayout.Button("扩大", GUILayout.MinWidth(200), GUILayout.MinHeight(80)))
        {
            gvrhead.CameraDesiredMove(400, 5.0f);
        }

        if (GUILayout.Button("缩小", GUILayout.MinWidth(200), GUILayout.MinHeight(80)))
        {
            gvrhead.CameraDesiredMove(150,5.0f);
        }


    }

}

下面给几个cardboard的学习网址

http://www.chinaar.com/unity/2212.html

http://www.taidous.com/thread-43648-1-1.html

http://www.taidous.com/thread-44054-1-1.html

http://www.taidous.com/thread-50872-1-1.html

http://www.taidous.com/thread-50876-1-1.html


学习如果遇到问题,请google  查看 API 或 github 上找到google  开源 GVR 上面的   https://github.com/googlevr/gvr-unity-sdk/issues


下面上传demo地址http://download.csdn.net/detail/yuyingwin/9757218

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值