【仅用于个人复习使用,顺序较乱】
一、环境搭建
1、unity(5.0以上版本)
2、LeapMotion SDK(https://developer.leapmotion.com/get-started/)
压缩包(几百MB)下载下来解压安装就okay~
3、Core Asset For Unity(https://developer.leapmotion.com/unity/)
这个只有1.9MB,一个很小的unityPackage~
二、连接测试
1、基本连接
http://jingyan.baidu.com/article/c843ea0bb19a3d77931e4af7.html
右键leap motion控制器可以打开visualizer,按v键可以切换模式。显示橙色则需要清除leap motion上的污迹。(启动会一直闪绿色,只需稍等片刻即可)
如果提示leap service未启动,点击电脑-管理-服务-leap service,右键选择启动即可。
2、Visualizer的使用
http://blog.csdn.net/guoming0000/article/details/9566563
不知道为什么很多命令按了并没有用O>o
三、LeapMotion&Unity
(PS:如果Unity中无法new新的工程,重新登录账号即可,不要问我是怎么知道的O.O)
Core Asset中只有Capsule Hand和Debug Hand,可以从官网下载Hand Module来获得其他的Hand Model,例如Rigged Hand。
四、Core Asset
1、Plugins文件夹
插件?不是很清楚。。。
2、Leap Motion文件夹
这个文件夹是CoreAsset的核心内容。
Gizmos:传感器的贴图
Materials:材质
Models:手的模型
Resources:自定义的Shander,用于Materials中的材质
Textures:一些贴图。。。
OK,以上这些都不重要,属于花里胡哨,下面才是重点。
Editor:一些Editor脚本。
Prefab:预制件,比如HandController。拖入Hierarchy中即可直接使用。
Scripts:一些脚本,例如LeapHandController,后面会具体分析。
Scenes:提供的demo场景,有AR、VR、Desktop。我们这里以Desktop为例。双击进入该场景。
HandModels就是双手的模型在hierarchy中生成的对象,LeapHandController就是双手的控制器。需要将HandModels中的Graphics Hands(用来渲染的mesh)和Physics Hands(碰撞器和刚体)传入LeapHandController中,LeapHandController才可以正常work。
LeapHandController对象包含三个脚本:LeapHandController.cs、LeapServiceProvider.cs、HandPool.cs。以下是对这些脚本的解释,当然这对正常使用影响不大,你也可以选择直接跳过这部分。
(1)LeapHandController.cs
直接看注释就好,比较详细~
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Leap;
//名字空间
namespace Leap.Unity {
/**
* LeapHandController uses a Factory to create and update HandRepresentations based on Frame's received from a Provider */
public class LeapHandController : MonoBehaviour {
protected LeapProvider provider;
protected HandFactory factory;
protected Dictionary graphicsReps = new Dictionary();
protected Dictionary physicsReps = new Dictionary();
// Reference distance from thumb base to pinky base in mm.
// 也就是拇指到小指在unity中代表的距离
protected const float GIZMO_SCALE = 5.0f;
// 这个是内部变量,为了封装的考虑,在下面使用C#中的get和set方法。由于是protected,所以在Inspector面板中不会显示,添加[SerilizeField]将其序列化,可以强制显示在面板上。
protected bool graphicsEnabled = true;
protected bool physicsEnabled = true;
// get和set方法,主要是权限控制,以及在赋值或读值的时候加入代码检验。带有get/set方法的变量不会显示在Inspector面板中。主要是用在脚本中,暴露给其他类调用来修改相应的变量。如果在Inspector面板修改,则需要Editor去手动关联。当然unity5.0之后的版本可以通过SetProperty来自动实现这一功能。
//关于get/set以及Editor,可以参考这个链接:http://www.cnblogs.com/lixiang-share/p/4658132.html。
public bool GraphicsEnabled {
get {
return graphicsEnabled;
}
set {
graphicsEnabled = value;
}
}
public bool PhysicsEnabled {
get {
return physicsEnabled;
}
set {
physicsEnabled = value;
}
}
/** Draws the Leap Motion gizmo when in the Unity editor. */
//所谓的Gizmos,也就是在Scene窗口中显示的小玩意儿,在Scene窗口顶栏可以设置显示的Gizmos种类。而在Game窗口中就不在显示了。
void OnDrawGizmos() {
Gizmos.matrix = Matrix4x4.Scale(GIZMO_SCALE * Vector3.one);
//这就是Gizmos文件夹的那张贴图
Gizmos.DrawIcon(transform.position, "leap_motion.png");
}
protected virtual void OnEnable() {
//LeapProvider和HandFactory是Scripts文件夹下两个抽象类,requireComponent是这个脚本中自定义的一个函数,相当于GetComponent,只不过如果没有Component的话会写log。
//其实拿到的就是LeapServiceProvider和HandPool。这两个对象就是那两个抽象类的实现类。
provider = requireComponent();
factory = requireComponent();
provider.OnUpdateFrame += OnUpdateFrame;
provider.OnFixedFrame += OnFixedFrame;
}
protected virtual void OnDisable() {
provider.OnUpdateFrame -= OnUpdateFrame;
provider.OnFixedFrame -= OnFixedFrame;
}
/** Updates the graphics HandRepresentations. */
protected virtual void OnUpdateFrame(Frame frame) {
if (frame != null && graphicsEnabled) {
UpdateHandRepresentations(graphicsReps, ModelType.Graphics, frame);
}
}
/** Updates the physics HandRepresentations. */
protected virtual void OnFixedFrame(Frame frame) {
if (frame != null && physicsEnabled) {
UpdateHandRepresentations(physicsReps, ModelType.Physics, frame);
}
}
/**
* Updates HandRepresentations based in the specified HandRepresentation Dictionary.
* Active HandRepresentation instances are updated if the hand they represent is still
* present in the Provider's CurrentFrame; otherwise, the HandRepresentation is removed. If new
* Leap Hand objects are present in the Leap HandRepresentation Dictionary, new HandRepresentations are
* created and added to the dictionary.
* @param all_hand_reps = A dictionary of Leap Hand ID's with a paired HandRepresentation
* @param modelType Filters for a type of hand model, for example, physics or graphics hands.
* @param frame The Leap Frame containing Leap Hand data for each currently tracked hand
*/
protected virtual void UpdateHandRepresentations(Dictionary all_hand_reps, ModelType modelType, Frame frame) {
//Frame应该是LeapMotion自己定义的类,猜测可能是将LeapMotion捕捉到的一帧画面进行解析之后,用来保存信息的类。比如这里的frame.Hands.Count就是指这帧画面中的手的数量。
for (int i = 0; i < frame.Hands.Count; i++) {
var curHand = frame.Hands[i];
HandRepresentation rep;
//如果有就直接get value,如果没有则add一个新的pair
if (!all_hand_reps.TryGetValue(curHand.Id, out rep)) {
rep = factory.MakeHandRepresentation(curHand, modelType);
if (rep != null) {
all_hand_reps.Add(curHand.Id, rep);
}
}
//这里会将HandRepresentation的IsMarked置为true,也就是说每一帧画面中active的rep都会被标记。
if (rep != null) {
rep.IsMarked = true;
rep.UpdateRepresentation(curHand);
rep.LastUpdatedTime = (int)frame.Timestamp;
}
}
/** Mark-and-sweep to finish unused HandRepresentations */
//遍历Dictionary,如果发现未被标记的rep,则他一定不是active的,则直接remove掉。
HandRepresentation toBeDeleted = null;
for (var it = all_hand_reps.GetEnumerator(); it.MoveNext();) {
var r = it.Current;
if (r.Value != null) {
if (r.Value.IsMarked) {
r.Value.IsMarked = false;
} else {
/** Initialize toBeDeleted with a value to be deleted */
//Debug.Log("Finishing");
toBeDeleted = r.Value;
}
}
}
/**Inform the representation that we will no longer be giving it any hand updates
* because the corresponding hand has gone away */
if (toBeDeleted != null) {
all_hand_reps.Remove(toBeDeleted.HandID);
toBeDeleted.Finish();
}
}
private T requireComponent() where T : Component {
T component = GetComponent();
if (component == null) {
string componentName = typeof(T).Name;
Debug.LogError("LeapHandController could not find a " + componentName + " and has been disabled. Make sure there is a " + componentName + " on the same gameObject.");
enabled = false;
}
return component;
}
}
}
总的来说,就是LeapMotion实时的捕捉每帧的画面。拿到某帧画面后,通过解析将信息保存在Frame当中。
Frame当中的Hands实际上就是Hand类型的List。
当前手部信息(例如finger的位置啊)保存在Hand中,通过Factory产生HandRepresentation并保存在Dictionary中。
每次Update()时,对Dictionary中的active的HandRepresentation调用UpdateRepresentation()函数,其实就是更新HandRepresentation中的Hand属性,也就是更新了场景中的HandModel。
(由于更细节或者更底层的东西还没有看过,所以目前提及的并不一定全都正确,先note一下~)
(2)LeapServiceProvider和HandPool
(代码较长,暂时没看~大致上前者是负责提供frame,而后者是负责将frame的Hand给封装成HandRepresentation)