关键词:
本文章由cartzhang编写,转载请注明出处。 全部权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/53915229
作者:cartzhang
Unity的 Steam VR插件本身也带有事件处理。可是我还想把事件给解耦出来,这样方便在各个项目中,不用关心硬件的各种处理而只用关心使用的,且能够随意的通过接受事件来触发对应的操作。
项目的參考图片可下载地址:https://github.com/cartzhang/ImgSayVRabc/tree/master/ViveEventDemo/Img
今天我们说谈论的就是以下这个东西:
图1.1
一、所需资源
所需资源,非常少。
须要用Steam VR插件 ,能够从Untiy商店下载。当然你能够使用文章后面给出本project的导出包,文章后面有下载地址:
图0
可是电脑还是须要安装steam的。这个临时还是须要FQ的。
你懂的。FQ是一项技能。
安装后:
图4
点击右上角的VR字样。链接你的Vive设备,然后就能够看到他们的状态了。
这里设备的各种设置方法和使用就不逐个说明解说了。网上搜索下吧,或去官方最正宗的。
图6
当然也能够使用桌面的快捷方式,当然前提是你有:
图5
二、制作Demo
首先。打开Unity 导入插件
图1
图2
然后。能够打开其给点例子来看看:
图3
接着就是,加入代码,给Controller加入控制代码:
图7
再然后就是须要自己写代码。
三、消息解耦
先说下。这个代码来自于其它同事。我基本没太多改动。可是确实非常好用,非常感谢!
。若有问题,请及时告知。
消息发送机制:
namespace SLQJ
{
/// <summary>
/// 消息分发,解耦
/// </summary>
public class NotificationManager
{
public static NotificationManager Instance { get { return SingletonProvider<NotificationManager>.Instance; } }
public delegate void MsgCallback(MessageObject eb);
/// <summary>
/// 回调队列
/// </summary>
private Dictionary<string, List<MsgCallback>> registedCallbacks = new Dictionary<string, List<MsgCallback>>();
/// <summary>
/// 延迟消息队列
/// </summary>
private readonly List<MessageObject> delayedNotifyMsgs = new List<MessageObject>();
/// <summary>
/// 主消息队列
/// </summary>
private readonly List<MessageObject> realCallbacks = new List<MessageObject>();
private static bool isInCalling = false;
public void Init()
{
}
public void Update()
{
lock (this)
{
if (realCallbacks.Count == 0)
{
//主消息隊列處理完時,加入延時消息到主消息列表
foreach (MessageObject eb in delayedNotifyMsgs)
{
realCallbacks.Add(eb);
}
delayedNotifyMsgs.Clear();
return;
}
//調用主消息處理隊列
isInCalling = true;
foreach (MessageObject eb in realCallbacks)
{
if (registedCallbacks.ContainsKey(eb.MsgName))
{
for (int i = 0; i < registedCallbacks[eb.MsgName].Count; i++)
{
MsgCallback ecb = registedCallbacks[eb.MsgName][i];
if (ecb == null)
{
continue;
}
#if UNITY_EDITOR
ecb(eb);
#else
try
{
ecb(eb);
}
catch (Exception e)
{
Debug.LogError("CallbackError:" + eb.MsgName + " : " + e.ToString());
}
#endif
}
}
else
{
Debug.Log("MSG_ALREADY_DELETED:" + eb.MsgName);
}
}
realCallbacks.Clear();
}
isInCalling = false;
}
public void Reset()
{
Dictionary<string, List<MsgCallback>> systemMsg = new Dictionary<string, List<MsgCallback>>();
foreach (KeyValuePair<string, List<MsgCallback>> item in this.registedCallbacks)
{
if (item.Key.StartsWith("_"))
{
systemMsg.Add(item.Key, item.Value);
}
}
this.registedCallbacks = systemMsg;
}
public void Destroy()
{
Reset();
}
/// <summary>
/// 订阅消息
/// </summary>
/// <param name="msgName"></param>
/// <param name="msgCallback"></param>
public void Subscribe(string msgName, MsgCallback msgCallback)
{
lock (this)
{
if (!registedCallbacks.ContainsKey(msgName))
{
registedCallbacks.Add(msgName, new List<MsgCallback>());
}
{
//防止反复订阅消息回调
List<MsgCallback> list = registedCallbacks[msgName];
for (int i = 0; i < list.Count; i++)
{
if (list[i].Equals(msgCallback))
{
return;
}
}
list.Add(msgCallback);
}
}
}
/// <summary>
/// 取消订阅
/// </summary>
/// <param name="msgName"></param>
/// <param name="msgCallback"></param>
public void UnSubscribe(string msgName, MsgCallback msgCallback)
{
lock (this)
{
if (!registedCallbacks.ContainsKey(msgName))
{
return;
}
//Debug.Log(msgName + ":-s-" + registedCallbacks[msgName].Count);
registedCallbacks[msgName].Remove(msgCallback);
//Debug.Log(msgName + ":-e-" + registedCallbacks[msgName].Count);
}
}
public void PrintMsg()
{
string content = "";
foreach (KeyValuePair<string, List<MsgCallback>> registedCallback in registedCallbacks)
{
int total = registedCallback.Value.Count;
if (total > 0)
{
content += registedCallback.Key + ":" + total + "\n";
for (int i = 0; i < total; i++)
{
content += "\t" + registedCallback.Value[i].Method.Name + "--" + registedCallback.Value[i].Target + "\n";
}
}
}
}
/// <summary>
/// 派发消息
/// </summary>
/// <param name="MsgName"></param>
/// <param name="MsgParam"></param>
public void Notify(string MsgName, params object[] MsgParam)
{
object msgValueParam = null;
if (MsgParam != null)
{
if (MsgParam.Length == 1)
{
msgValueParam = MsgParam[0];
}
else
{
msgValueParam = MsgParam;
}
}
lock (this)
{
if (!registedCallbacks.ContainsKey(MsgName))
{
return;
}
if (isInCalling)
{
delayedNotifyMsgs.Add(new MessageObject(MsgName, msgValueParam));
}
else
{
realCallbacks.Add(new MessageObject(MsgName, msgValueParam));
}
}
}
}
public class MessageObject
{
public object MsgValue;
public string MsgName;
public MessageObject()
{
MsgName = this.GetType().FullName;
}
public MessageObject(string msgName, object ev)
{
MsgValue = ev;
MsgName = msgName;
}
}
}
你能够看到原著者写的还是非常严谨的。使用消息队列来实现的,然后在unity某组件的Update中实现轮询调用。
先看看这个消息机制的启动,特别简单:
public class main : MonoBehaviour {
// Use this for initialization
void Awake ()
{
NotificationManager.Instance.Init();
}
// Update is called once per frame
void Update ()
{
NotificationManager.Instance.Update();
}
}
与上面说的一模一样,初始化,然后update。
至于说机制怎么用。这个在后面会接实战给出。
四、手柄Controller消息触发
手柄的事件非常多。我就捡了几个经常使用的来做个例子来说明问题,若须要。你们自己能够来加入自己的须要。
/// <summary>
/// 能够自定義加入事件,然後實現消息的傳遞。
/// </summary>
// 實現手柄的案件事件功能
public class ViveEvent : MonoBehaviour
{
void Start()
{
var trackedController = GetComponent<SteamVR_TrackedController>();
if (trackedController == null)
{
trackedController = gameObject.AddComponent<SteamVR_TrackedController>();
}
trackedController.TriggerClicked += new ClickedEventHandler(OnTriggerClicked);
trackedController.TriggerPressDown += new ClickedEventHandler(OnTriggerPressDn);
trackedController.TriggerUnclicked += new ClickedEventHandler(OnTriggerUnclicked);
trackedController.PadClicked += new ClickedEventHandler(OnPadClicked);
trackedController.PadUnclicked += new ClickedEventHandler(OnPadUnclicked);
}
void OnTriggerClicked(object sender, ClickedEventArgs e)
{
Debug.Log(e.controllerIndex + "trigger clicked");
// 开火
NotificationManager.Instance.Notify(NotificationType.Gun_Fire.ToString());
}
void OnTriggerPressDn(object sender, ClickedEventArgs e)
{
Debug.Log(e.controllerIndex + "trigger press down");
//
NotificationManager.Instance.Notify(NotificationType.Gathering_Stength.ToString());
}
void OnTriggerUnclicked(object sender, ClickedEventArgs e)
{
Debug.Log(e.controllerIndex + "trigger unclicked");
NotificationManager.Instance.Notify(NotificationType.Gun_KeyUp.ToString());
}
void OnPadClicked(object sender, ClickedEventArgs e)
{
// 扔雷
NotificationManager.Instance.Notify(NotificationType.Throw_Bomb.ToString());
Debug.Log(e.controllerIndex + "pad clicked");
}
void OnPadUnclicked(object sender, ClickedEventArgs e)
{
Debug.Log(e.controllerIndex + "padd un clicked");
}
}
主要写了按键Trigger 按下,按住和弹起和Pad的按下和弹起事件。
然后是触发事件的接受,这里就体现了解耦事件的优点。
这里真的不止于使用在vive按键处理这里。
public class ControlButtonAns : MonoBehaviour
{
// Use this for initialization
void Start()
{
NotificationManager.Instance.Subscribe(NotificationType.Gun_Fire.ToString(), GunFire);
NotificationManager.Instance.Subscribe(NotificationType.Gathering_Stength.ToString(), GatheringStength);
NotificationManager.Instance.Subscribe(NotificationType.Throw_Bomb.ToString(), ThrowBomb);
NotificationManager.Instance.Subscribe(NotificationType.Gun_KeyUp.ToString(), GunKeyUp);
}
void GunFire(MessageObject obj)
{
Debug.Log("response gun fire , trigger button click");
}
void GatheringStength(MessageObject obj)
{
Debug.Log("response gathering stength, trigger button hold");
}
void GunKeyUp(MessageObject obj)
{
Debug.Log("response key up, trigger button unclicked");
}
void ThrowBomb(MessageObject obj)
{
Debug.Log("response throw bomb , pad button click");
}
}
这个就依据个人的须要来加入自己的代码。
这里不过举例说明。
代码写完了,加入吧!!
手柄contorller接受事件:
图7.1
消息触发解耦代码:
图7.2
对应消息脚本:
图7.3
这样基本就搞定了。
五、结果
一图胜千言:
图8
就这样。
六、下载地址
project下载地址:github
https://github.com/cartzhang/ImgSayVRabc/tree/master/ViveEventDemo
steam 插件project导出地址:
2017-01-09更新,给事件加入触发手柄ID。
https://github.com/cartzhang/ImgSayVRabc/blob/master/ViveEventDemo/HTVVive_event_add_controller_inedex%20_Cartzhang.unitypackage
七、參考
[1] http://www.cnblogs.com/czaoth/p/5610883.html
[2] http://www.htc.com/managed-assets/shared/desktop/vive/Vive_PRE_User_Guide.pdf
[3] http://blog.csdn.net/qiaochaoqc/article/details/52086790
htcvive基础入门基于unrealengine4引擎
主要以讲解介绍HTCVive设备以及Unreal继承的SteamVRPlugin为主使用最新的虚幻引擎与Plugin完成VR环境的搭建然后完成一个基本的VRGames.任务5:04-配置UE4VR开发环境任务6:05-创建一个自己的VR场景任务7:06-HTCVive手柄的按键反馈19:00手柄... 查看详情
htcvive生态链揭晓,多款vr大作即将登陆
原文标题:HTCVIVE生态链揭晓,多款VR大作即将登陆 2017世界移动大会上海站正式的落下了帷幕,该会由全球通信标准组织于6月28日至7月1日在上海的新国际博览中心举行。 作为VR头显的重量级企业HTCVive在展... 查看详情
《图说vr入门》——360全景视频
...ttp://blog.csdn.net/cartzhang/article/details/53674647作者:cartzhang《图说VR入门》——360全景视频本章用使用较早的UnityOC插件来实现一个360全景视频,且通过使用不同的路径配置,可以随意切换视频内容。这样省去了多次打包的过程,简单... 查看详情
vr开发教程汇总
...流VR开发教程,并在不断的更新中…欢迎大家关注。2.HTCVIVE开发教程1.HTCVIVE开发教程(一)2.HTCVIVE开发教程(二)3.HTCVIVE开发教程(三)3.VR开发教程之LearningVirtualReality1.《V 查看详情
《图说vr入门》——googlevr他山之玉
...ttp://blog.csdn.net/cartzhang/article/details/53125482作者:cartzhang《图说VR入门》——googleVR他山之玉之前分析了一些GoogleVR的代码,画了一些图。常言:他山之石,可以攻玉。个人觉得googleVR这不能说是石头了吧,入宝山空手而归,岂是程... 查看详情
《图说vr入门》——googlevr入门
本文章由cartzhang编写,转载请注明出处。所有权利保留。文章链接:http://blog.csdn.net/cartzhang/article/details/52959035作者:cartzhang本篇为基础篇,适合用纯正小白,还请各位大神绕行。VR先从外形看起来最简单的googleVR的Cardboard说起。g... 查看详情
理解htcvive更新——控制相机旋转和位移
本文章由cartzhang编写,转载请注明出处。所有权利保留。文章链接:http://blog.csdn.net/cartzhang/article/details/72188658作者:cartzhang一、写在前面在HTC的vive头盔中,一旦Vive头盔连接都unity游戏中,就会控制所有Camera的旋转和位置。这对... 查看详情
保持htcvive头盔与手柄和人物位置的一致
参考技术A在4.11及以后版本中进行VR开发,要知道两件事这是因为官方在4.11更新里特意处理了这种偏差,原话如下:所以为了防止在使用瞬移时位置不对,或者进行移动时出现穿墙等蛋疼的事情,就要消除这种偏差,使得Character... 查看详情
htcvive开发笔记之uiguideline
本文转自HTC官方论坛,原址https://www.htcvive.com/cn/forum/chat.php?mod=viewthread&tid=1641&extra=page=1。在过去,3D程序多数是游戏,非游戏的应用一般设计成2D程序。而在VR中几乎所有程序都是3D的(左右眼需要看到的不一样画面),所... 查看详情
如何透过htcvive拍摄mixedreality(混合现实)影片
...orum/1706?extra=page%3D1 也许你是一位开发者,想为自己的HTCVive游戏制作酷炫的宣传片;或者你是游戏主播,想为观众带来高质量的VR直播体验;甚至你是一位VR的爱好者。无论如何,如果你对MixedReality(混合现实)感兴趣,请继... 查看详情
unity使用openxr和xrinteractiontoolkit开发htcvive(vivecosmos)(代码片段)
Unity使用OpenXR和XRInteractionToolkit开发HTCVive(ViveCosmos)提示:作者是Unity2020.3以上版本做的开发。开发VR程序需要安装Steam,SteamVR,(ViveCosmos,需要再安装VIVEPORT,VIVEConsole)OpenXR控制设备(头盔,手柄)通信。XR... 查看详情
htcvive与leapmotion出现位置错误的问题
LeapMotion已经支持VR,但是官方没有支持HTCVive的例子。按照官方的文档,其实是有问题的:https://developer.leapmotion.com/documentation/unity/unity/Unity_Custom_Rig.html AddtheLeapVRTemporalWarpingscriptcomponenttotheLeapSpacecompon 查看详情
关于vr开发中的穿墙问题随想
...,即用户觉得我处于这里。 而如何同步这两个坐标系HTCVIVE已经帮我们解决了。由HTCVIVE开发的应用,在应用刚初始运行阶段,将用户所处的现实世界的位置和VR世界里的位置进行一次匹配,然后以此为基准来同步两者 查看详情
实现在vr下使用手柄来操作umg
当前使用的UE412.5硬件:HTCVive如何实现在VR下使用手柄来操作UMG呢?当前有两个核心需求:1,当手柄指向UMG控件时,UMG控件会有hover的效果2,当手柄Trigger键按下时,触发UMG控件按下功能好在已经有VRUMGPlugin插件可以研究.VRUMGPlugin插件没有实... 查看详情
如何使一个按键分别触发多个事件(代码片段)
TL;DR:每次用户按空格键时,我都希望出现下一行对话框。上下文:我刚开始学习编写过去几周的代码(第一个基本的html&css,现在是JS)。我大多使用freecodecamp和YouTube。为了帮助我更有创造性地学习,我想我会开始制作一个基... 查看详情
htcvive开发笔记之手柄控制
...教程很多也很简单。接下来说下Vive手柄的控制。手柄是HTCVive的重要交互手段,我们通过第一个图片应该对其有一个直观的了解了,总共是九个按钮:第一个是菜单按钮;2,3,4,5分别对应的是Trackpad/Touchpad的上下左右,有时候对应的是XBox... 查看详情
htcvive怎么用手柄去旋转物体
参考技术A这个应该是程序的设定吧,也需要激光定位技术,vive有着lighthouse激光定位系统,而我了解的大朋E3定位版VR头盔,采用polaris双目激光定位系统,双基站可360°全方位体验,玩起VR游戏特别嗨。 参考技术B是用unity开发吗... 查看详情
用unity开发htcvive——手柄控制篇
...为现在虚拟现实非常的火爆但目前主流的虚拟现实设备(HTCVIVE)的教程却少的可怜,这个我深有体会。所以,我想将我平时开发中遇到的问题以及解决方法记录下来,分享给大家,若其中有什么错误或者大家有什么更好的方案... 查看详情