Unity3D关于VR的Demo
Posted 喜欢雨天的我
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D关于VR的Demo相关的知识,希望对你有一定的参考价值。
最近有点忙,只有挤时间去了解VR这方面的Demo了,之前关注了一个android平台的视频VR的demo研读的差不多了,现在开始关注Unity3d建造VR游戏环境的demo.
Android下demo例子地址。
* https://github.com/ashqal/MD360Player4AndroidVR视频播放demo
* https://github.com/ejeinc/RajawaliCardboardExamplecardboard的demo
正题
- 准备demo地址http://unity3d.com/cn/learn/tutorials/topics/virtual-reality点击跳转到VR sample界面
- unity3d
- vistual studio 2015
- 现在还等啥 嗨起来
前提界面介绍
初始界面。
主界面
飞机游戏的模型界面
迷宫的模型界面
射击场景有两个 一个是180度的视角比较远,一个是360度的视角(等距离)
了解结构
- 我们从飞机那个unity的工程中来看,其Hierarchy的结构(主要讲重点部分)
- 首先我们从相机开始说起
- 首先我们从每个C#脚本去看实现的过程
VREyeRaycaster.cs
- 首先我们从每个C#脚本去看实现的过程
using System;
using UnityEngine;
namespace VRStandardAssets.Utils
// In order to interact with objects in the scene
// this class casts a ray into the scene and if it finds
// a VRInteractiveItem it exposes it for other classes to use.
// This script should be generally be placed on the camera.
//为了与场景中的物体相互作用
//这个类投射到场景中的光线,如果它发现
// vrinteractiveitem它暴露了它的其他类使用。
//这个脚本应该被放置在相机上。
public class VREyeRaycaster : MonoBehaviour
public event Action<RaycastHit> OnRaycasthit; // This event is called every frame that the user's gaze is over a collider.
[SerializeField] private Transform m_Camera;
[SerializeField] private LayerMask m_ExclusionLayers; // Layers to exclude from the raycast. 光线投射的层
[SerializeField] private Reticle m_Reticle; // The reticle, if applicable.网线
[SerializeField] private VRInput m_VrInput; // Used to call input based events on the current VRInteractiveItem.
[SerializeField] private bool m_ShowDebugRay; // Optionally show the debug ray.是否显示调试光线
[SerializeField] private float m_DebugRayLength = 5f; // Debug ray length.光线的长度
[SerializeField] private float m_DebugRayDuration = 1f; // How long the Debug ray will remain visible.光线存活时间
[SerializeField] private float m_RayLength = 500f; // How far into the scene the ray is cast.光线投射到场景的距离
private VRInteractiveItem m_CurrentInteractible; //The current interactive item 当前交互点
private VRInteractiveItem m_LastInteractible; //The last interactive item 最后交互点
//从其他类中获得当前的交互点
// Utility for other classes to get the current interactive item
public VRInteractiveItem CurrentInteractible
get return m_CurrentInteractible;
private void OnEnable()
m_VrInput.OnClick += HandleClick;
m_VrInput.OnDoubleClick += HandleDoubleClick;
m_VrInput.OnUp += HandleUp;
m_VrInput.OnDown += HandleDown;
private void OnDisable ()
m_VrInput.OnClick -= HandleClick;
m_VrInput.OnDoubleClick -= HandleDoubleClick;
m_VrInput.OnUp -= HandleUp;
m_VrInput.OnDown -= HandleDown;
//unity的更新方法
private void Update()
EyeRaycast();
private void EyeRaycast()
//如果是必要的话,则显示调试线(在unity上设置 )
// Show the debug ray if required
if (m_ShowDebugRay)
Debug.DrawRay(m_Camera.position, m_Camera.forward * m_DebugRayLength, Color.blue, m_DebugRayDuration);
//创建相机前方的光线
// Create a ray that points forwards from the camera.
Ray ray = new Ray(m_Camera.position, m_Camera.forward);
RaycastHit hit;
//这里主要判断前方光线的展示与否
// Do the raycast forweards to see if we hit an interactive item
if (Physics.Raycast(ray, out hit, m_RayLength, ~m_ExclusionLayers))
VRInteractiveItem interactible = hit.collider.GetComponent<VRInteractiveItem>(); //attempt to get the VRInteractiveItem on the hit object
m_CurrentInteractible = interactible;
// If we hit an interactive item and it's not the same as the last interactive item, then call Over
if (interactible && interactible != m_LastInteractible)
interactible.Over();
// Deactive the last interactive item
if (interactible != m_LastInteractible)
DeactiveLastInteractible();
m_LastInteractible = interactible;
// Something was hit, set at the hit position.
if (m_Reticle)
m_Reticle.SetPosition(hit);
if (OnRaycasthit != null)
OnRaycasthit(hit);
else
// Nothing was hit, deactive the last interactive item.
DeactiveLastInteractible();
m_CurrentInteractible = null;
// Position the reticle at default distance.
if (m_Reticle)
m_Reticle.SetPosition();
private void DeactiveLastInteractible()
if (m_LastInteractible == null)
return;
m_LastInteractible.Out();
m_LastInteractible = null;
private void HandleUp()
if (m_CurrentInteractible != null)
m_CurrentInteractible.Up();
private void HandleDown()
if (m_CurrentInteractible != null)
m_CurrentInteractible.Down();
private void HandleClick()
if (m_CurrentInteractible != null)
m_CurrentInteractible.Click();
private void HandleDoubleClick()
if (m_CurrentInteractible != null)
m_CurrentInteractible.DoubleClick();
VRCameraUi.cs
using System;
using UnityEngine;
namespace VRStandardAssets.Utils
//这类保证UI(如网线和选择吧)
//正确地设置。
// This class ensures that the UI (such as the reticle and selection bar)
// are set up correctly.
public class VRCameraUI : MonoBehaviour
[SerializeField] private Canvas m_Canvas; // Reference to the canvas containing the UI.包含用户界面的画布的参考。
private void Awake()
// Make sure the canvas is on.确保画布上。
m_Canvas.enabled = true;
// Set its sorting order to the front.把它的排序顺序设置为前面。
m_Canvas.sortingOrder = Int16.MaxValue;
// Force the canvas to redraw so that it is correct before the first render.在第一次渲染前 力将会重绘。
Canvas.ForceUpdateCanvases();
SelectionRadial.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System;
namespace VRStandardAssets.Utils
// This class is used to control a radial bar that fills
// up as the user holds down the Fire1 button. When it has
// finished filling it triggers an event. It also has a
// coroutine which returns once the bar is filled.
public class SelectionRadial : MonoBehaviour
public event Action OnSelectionComplete; // This event is triggered when the bar has filled.
[SerializeField] private float m_SelectionDuration = 2f; // How long it takes for the bar to fill.
[SerializeField] private bool m_HideOnStart = true; // Whether or not the bar should be visible at the start.
[SerializeField] private Image m_Selection; // Reference to the image who's fill amount is adjusted to display the bar.
[SerializeField] private VRInput m_VRInput; // Reference to the VRInput so that input events can be subscribed to.
private Coroutine m_SelectionFillRoutine; // Used to start and stop the filling coroutine based on input.
private bool m_IsSelectionRadialActive; // Whether or not the bar is currently useable.
private bool m_RadialFilled; // Used to allow the coroutine to wait for the bar to fill.
public float SelectionDuration get return m_SelectionDuration;
private void OnEnable()
m_VRInput.OnDown += HandleDown;
m_VRInput.OnUp += HandleUp;
private void OnDisable()
m_VRInput.OnDown -= HandleDown;
m_VRInput.OnUp -= HandleUp;
private void Start()
// Setup the radial to have no fill at the start and hide if necessary.
m_Selection.fillAmount = 0f;
if(m_HideOnStart)
Hide();
public void Show()
m_Selection.gameObject.SetActive(true);
m_IsSelectionRadialActive = true;
public void Hide()
m_Selection.gameObject.SetActive(false);
m_IsSelectionRadialActive = false;
// This effectively resets the radial for when it's shown again.
m_Selection.fillAmount = 0f;
private IEnumerator FillSelectionRadial()
// At the start of the coroutine, the bar is not filled.
m_RadialFilled = false;
// Create a timer and reset the fill amount.
float timer = 0f;
m_Selection.fillAmount = 0f;
// This loop is executed once per frame until the timer exceeds the duration.
while (timer < m_SelectionDuration)
// The image's fill amount requires a value from 0 to 1 so we normalise the time.
m_Selection.fillAmount = timer / m_SelectionDuration;
// Increase the timer by the time between frames and wait for the next frame.
timer += Time.deltaTime;
yield return null;
// When the loop is finished set the fill amount to be full.
m_Selection.fillAmount = 1f;
// Turn off the radial so it can only be used once.
m_IsSelectionRadialActive = false;
// The radial is now filled so the coroutine waiting for it can continue.
m_RadialFilled = true;
// If there is anything subscribed to OnSelectionComplete call it.
if (OnSelectionComplete != null)
OnSelectionComplete();
public IEnumerator WaitForSelectionRadialToFill ()
// Set the radial to not filled in order to wait for it.
m_RadialFilled = false;
// Make sure the radial is visible and usable.
Show ();
// Check every frame if the radial is filled.
while (!m_RadialFilled)
yield return null;
// Once it's been used make the radial invisible.
Hide ();
private void HandleDown()
// If the radial is active start filling it.
if (m_IsSelectionRadialActive)
m_SelectionFillRoutine = StartCoroutine(FillSelectionRadial());
private void HandleUp()
// If the radial is active stop filling it and reset it's amount.
if (m_IsSelectionRadialActive)
if(m_SelectionFillRoutine != null)
StopCoroutine(m_SelectionFillRoutine);
m_Selection.fillAmount = 0f;
Reticle.cs
using UnityEngine;
using UnityEngine.UI;
namespace VRStandardAssets.Utils
// The reticle is a small point at the centre of the screen.
// It is used as a visual aid for aiming. The position of the
// reticle is either at a default position in space or on the
// surface of a VRInteractiveItem as determined by the VREyeRaycaster.
public class Reticle : MonoBehaviour
[SerializeField] private float m_DefaultDistance = 5f; // The default distance away from the camera the reticle is placed.
[SerializeField] private bool m_UseNormal; // Whether the reticle should be placed parallel to a surface.
[SerializeField] private Image m_Image; // Reference to the image component that represents the reticle.
[SerializeField] private Transform m_ReticleTransform; // We need to affect the reticle's transform.
[SerializeField] private Transform m_Camera; // The reticle is always placed relative to the camera.
private Vector3 m_OriginalScale; // Since the scale of the reticle changes, the original scale needs to be stored.
private Quaternion m_OriginalRotation; // Used to store the original rotation of the reticle.
public bool UseNormal
get return m_UseNormal;
set m_UseNormal = value;
public Transform ReticleTransform get return m_ReticleTransform;
private void Awake()
// Store the original scale and rotation.
m_OriginalScale = m_ReticleTransform.localScale;
m_OriginalRotation = m_ReticleTransform.localRotation;
public void Hide()
m_Image.enabled = false;
public void Show()
m_Image.enabled = true;
// This overload of SetPosition is used when the the VREyeRaycaster hasn't hit anything.
public void SetPosition ()
// Set the position of the reticle to the default distance in front of the camera.
m_ReticleTransform.position = m_Camera.position + m_Camera.forward * m_DefaultDistance;
// Set the scale based on the original and the distance from the camera.
m_ReticleTransform.localScale = m_OriginalScale * m_DefaultDistance;
// The rotation should just be the default.
m_ReticleTransform.localRotation = m_OriginalRotation;
// This overload of SetPosition is used when the VREyeRaycaster has hit something.
public void SetPosition (RaycastHit hit)
m_ReticleTransform.position = hit.point;
m_ReticleTransform.localScale = m_OriginalScale * hit.distance;
// If the reticle should use the normal of what has been hit...
if (m_UseNormal)
// ... set it's rotation based on it's forward vector facing along the normal.
m_ReticleTransform.rotation = Quaternion.FromToRotation (Vector3.forward, hit.normal);
else
// However if it isn't using the normal then it's local rotation should be as it was originally.
m_ReticleTransform.localRotation = m_OriginalRotation;
VR CameraaFade
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Audio;
namespace VRStandardAssets.Utils
// This class is used to fade the entire screen to black (or
// any chosen colour). It should be used to smooth out the
// transition between scenes or restarting of a scene.
public class VRCameraFade : MonoBehaviour
public event Action OnFadeComplete; // This is called when the fade in or out has finished.
[SerializeField] private Image m_FadeImage; // Reference to the image that covers the screen.
[SerializeField] private AudioMixerSnapshot m_DefaultSnapshot; // Settings for the audio mixer to use normally.
[SerializeField] private AudioMixerSnapshot m_FadedSnapshot; // Settings for the audio mixer to use when faded out.
[SerializeField] private Color m_FadeColor = Color.black; // The colour the image fades out to.
[SerializeField] private float m_FadeDuration = 2.0f; // How long it takes to fade in seconds.
[SerializeField] private bool m_FadeInOnSceneLoad = false; // Whether a fade in should happen as soon as the scene is loaded.
[SerializeField] private bool m_FadeInOnStart = false; // Whether a fade in should happen just but Updates start.
private bool m_IsFading; // Whether the screen is currently fading.
private float m_FadeStartTime; // The time when fading started.
private Color m_FadeOutColor; // This is a transparent version of the fade colour, it will ensure fading looks normal.
public bool IsFading get return m_IsFading;
private void Awake()
m_FadeOutColor = new Color(m_FadeColor.r, m_FadeColor.g, m_FadeColor.b, 0f);
m_FadeImage.enabled = true;
private void Start()
// If applicable set the immediate colour to be faded out and then fade in.
if (m_FadeInOnStart)
m_FadeImage.color = m_FadeColor;
FadeIn(true);
private void OnLevelWasLoaded()
// If applicable set the immediate colour to be faded out and then fade in.
if (m_FadeInOnSceneLoad)
m_FadeImage.color = m_FadeColor;
FadeIn(true);
// Since no duration is specified with this overload use the default duration.
public void FadeOut(bool fadeAudio)
FadeOut(m_FadeDuration, fadeAudio);
public void FadeOut(float duration, bool fadeAudio)
// If not already fading start a coroutine to fade from the fade out colour to the fade colour.
if (m_IsFading)
return;
StartCoroutine(BeginFade(m_FadeOutColor, m_FadeColor, duration));
// Fade out the audio over the same duration.
if(m_FadedSnapshot && fadeAudio)
m_FadedSnapshot.TransitionTo (duration);
// Since no duration is specified with this overload use the default duration.
public void FadeIn(bool fadeAudio)
FadeIn(m_FadeDuration, fadeAudio);
public void FadeIn(float duration, bool fadeAudio)
// If not already fading start a coroutine to fade from the fade colour to the fade out colour.
if (m_IsFading)
return;
StartCoroutine(BeginFade(m_FadeColor, m_FadeOutColor, duration));
// Fade in the audio over the same duration.
if(m_DefaultSnapshot && fadeAudio)
m_DefaultSnapshot.TransitionTo (duration);
public IEnumerator BeginFadeOut (bool fadeAudio)
// Fade out the audio over the default duration.
if(m_FadedSnapshot && fadeAudio)
m_FadedSnapshot.TransitionTo (m_FadeDuration);
yield return StartCoroutine(BeginFade(m_FadeOutColor, m_FadeColor, m_FadeDuration));
public IEnumerator BeginFadeOut(float duration, bool fadeAudio)
// Fade out the audio over the given duration.
if(m_FadedSnapshot && fadeAudio)
m_FadedSnapshot.TransitionTo (duration);
yield return StartCoroutine(BeginFade(m_FadeOutColor, m_FadeColor, duration));
public IEnumerator BeginFadeIn (bool fadeAudio)
// Fade in the audio over the default duration.
if(m_DefaultSnapshot && fadeAudio)
m_DefaultSnapshot.TransitionTo (m_FadeDuration);
yield return StartCoroutine(BeginFade(m_FadeColor, m_FadeOutColor, m_FadeDuration));
public IEnumerator BeginFadeIn(float duration, bool fadeAudio)
// Fade in the audio over the given duration.
if(m_DefaultSnapshot && fadeAudio)
m_DefaultSnapshot.TransitionTo (duration);
yield return StartCoroutine(BeginFade(m_FadeColor, m_FadeOutColor, duration));
private IEnumerator BeginFade(Color startCol, Color endCol, float duration)
// Fading is now happening. This ensures it won't be interupted by non-coroutine calls.
m_IsFading = true;
// Execute this loop once per frame until the timer exceeds the duration.
float timer = 0f;
while (timer <= duration)
// Set the colour based on the normalised time.
m_FadeImage.color = Color.Lerp(startCol, endCol, timer / duration);
// Increment the timer by the time between frames and return next frame.
timer += Time.deltaTime;
yield return null;
// Fading is finished so allow other fading calls again.
m_IsFading = false;
// If anything is subscribed to OnFadeComplete call it.
if (OnFadeComplete != null)
OnFadeComplete();
ReturnToMainMenu.cs
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
namespace VRStandardAssets.Utils
// This class simply allows the user to return to the main menu.
public class ReturnToMainMenu : MonoBehaviour
[SerializeField] private string m_MenuSceneName = "MainMenu"; // The name of the main menu scene.
[SerializeField] private VRInput m_VRInput; // Reference to the VRInput in order to know when Cancel is pressed.
[SerializeField] private VRCameraFade m_VRCameraFade; // Reference to the script that fades the scene to black.
private void OnEnable ()
m_VRInput.OnCancel += HandleCancel;
private void OnDisable ()
m_VRInput.OnCancel -= HandleCancel;
private void HandleCancel ()
StartCoroutine (FadeToMenu ());
private IEnumerator FadeToMenu ()
// Wait for the screen to fade out.
yield return StartCoroutine (m_VRCameraFade.BeginFadeOut (true));
// Load the main menu by itself.
SceneManager.LoadScene(m_MenuSceneName, LoadSceneMode.Single);
VR Tracking Reset.cs
using UnityEngine;
using UnityEngine.VR;
namespace VRStandardAssets.Utils
// This class simply insures the head tracking behaves correctly when the application is paused.
public class VRTrackingReset : MonoBehaviour
private void OnApplicationPause(bool pauseStatus)
InputTracking.Recenter();
以上是相机中的C#脚步代码,本来想全部注释的,后来发现英文解释很全,这里大家看英文介绍就好。有空再写环境的demo实现。
以上是关于Unity3D关于VR的Demo的主要内容,如果未能解决你的问题,请参考以下文章