【仅用于个人复习使用,顺序较乱】
[Detection SDK]
关于手势检测的官方SDK主要有这么几个脚本。
Detector是所有检测器的父类,主要保存的属性是IsActive。
DetectorLogicGate继承自Detector,主要提供多个Detector的逻辑操作,比如AND/OR/NAND/NOR。
PinchDetector是用来检测手是否握紧,继承自AbstractHoldDetector。而AbstractHoldDetector又继承自Detector。
FingerDirectionDetector用来检测某个手指是否沿某个指定的方向。
PalmDirectionDetector用来检测手掌是否沿某个指定的方向。
ExtendedFingerDetector用来检测手指是否展开。
ProximityDetector用来进行距离检测。
(LeapMotion脚本代码思路教程:http://blog.csdn.net/admintan/article/details/50319757)
(LeapMotion的名字空间:http://blog.csdn.net/eagle_pre21/article/details/51776830)
这两个链接还是值得一看的。
我们先对官方提供的一些demo和源码进行解析,从而了解LeapMotion提供了哪些主要的接口或者说我们应该怎么编写LeapMotion的代码。
五、SDK的使用 之 PinchDetector
我们这里将以LeapMotion Detection Module中的PinchDrawDemo场景为例。这是PinchDrawDemo的结构:
LMHeadMountedRig是CoreAsset中的一个预制件,应该对应的是HeadMounted模式。PinchDrawing是执行绘制功能的object。DirectionLight则是场景中的灯光。
我们对这个场景做简单的分析,来了解LM SDK的使用方法。
(1)PinchDraw.cs
这是PinchDrawing对象上的唯一的脚本,具体代码如下:
using UnityEngine;
using System.Collections.Generic;
namespace Leap.Unity.DetectionExamples {
public class PinchDraw : MonoBehaviour {
//使用Tooltip,当我们在Inspector面板中,把鼠标停留在PinchDetector变量上,就会显示这一句字符串。这里的PinchDetector是一个DetectionUtility文件夹中的自定义类,左右手模型上各有一个。
[Tooltip("Each pinch detector can draw one line at a time.")]
[SerializeField]
//左右手的PinchDetector
private PinchDetector[] _pinchDetectors;
[SerializeField]
//mesh的材质
private Material _material;
[SerializeField]
//mesh的颜色
private Color _drawColor = Color.white;
[SerializeField]
//SmoothedVector3的平滑系数
private float _smoothingDelay = 0.01f;
[SerializeField]
//绘制的Ring的半径
private float _drawRadius = 0.002f;
[SerializeField]
//绘制一个Ring所使用的顶点数
private int _drawResolution = 8;
[SerializeField]
//判断是否需要添加Ring的最短距离
private float _minSegmentLength = 0.005f;
private DrawState[] _drawStates;
//get/set,前面已经介绍了,这里不再多提
public Color DrawColor {
get {
return _drawColor;
}
set {
_drawColor = value;
}
}
public float DrawRadius {
get {
return _drawRadius;
}
set {
_drawRadius = value;
}
}
//当Inspector面板中的值被修改时触发,用于保证值的合法性。类似的函数还有Reset(),当面板中脚本的值被reset时触发。
//具体可参考:http://www.tuicool.com/articles/AfqY32
void OnValidate() {
_drawRadius = Mathf.Max(0, _drawRadius);
_drawResolution = Mathf.Clamp(_drawResolution, 3, 24);
_minSegmentLength = Mathf.Max(0, _minSegmentLength);
}
void Awake() {
//至少要有一个PinchDetector被传入
if (_pinchDetectors.Length == 0) {
Debug.LogWarning("No pinch detectors were specified! PinchDraw can not draw any lines without PinchDetectors.");
}
}
//初始化DrawState数组,并加入新建DrawState对象。DrawState是这个脚本中定义的内部类,具体参见下方的注释。
void Start() {
_drawStates = new DrawState[_pinchDetectors.Length];
for (int i = 0; i < _pinchDetectors.Length; i++) {
//把this作为参数传入DrawState,保存在parent变量中。
_drawStates[i] = new DrawState(this);
}
}
void Update() {
for (int i = 0; i < _pinchDetectors.Length; i++) {
var detector = _pinchDetectors[i];
var drawState = _drawStates[i];
//以下三个是PinchDetector类的方法,根据名字很容易猜到他们的功能。捏成拳头就开始画线,拳头松开就结束画线。画线调用的则是内部类DrawState封装的函数。
//DidStartHold——手刚捏成拳头
if (detector.DidStartHold) {
drawState.BeginNewLine();
}
//DidRelease——手从拳头松开
if (detector.DidRelease) {
drawState.FinishLine();
}
//IsHolding——手保持拳头的姿势
if (detector.IsHolding) {
drawState.UpdateLine(detector.Position);
}
}
}
//期待已久的DrawState类~
private class DrawState {
private List<Vector3> _vertices = new List<Vector3>();
private List<int> _tris = new List<int>();
private List<Vector2> _uvs = new List<Vector2>();
private List<Color> _colors = new List<Color>();
//外部类在初始化DrawState数组时(Start()函数),会将this(外部类对象的引用)传入新建的DrawState。
private PinchDraw _parent;
private int _rings = 0;
private Vector3 _prevRing0 = Vector3.zero;
private Vector3 _prevRing1 = Vector3.zero;
private Vector3 _prevNormal0 = Vector3.zero;
private Mesh _mesh;
//SmoothedVector3是LM CoreAsset中Algorithm文件夹里的自定义类,大致就是让Vector3的值在改变时更平滑。
private SmoothedVector3 _smoothedPosition;
public DrawState(PinchDraw parent) {
_parent = parent;
//初始化SmoothedVector3对象
_smoothedPosition = new SmoothedVector3();
_smoothedPosition.delay = parent._smoothingDelay;
_smoothedPosition.reset = true;
}
//开始画线,为后面的Update做一些初始化的准备工作
public GameObject BeginNewLine() {
_rings = 0;
_vertices.Clear();
_tris.Clear();
_uvs.Clear();
_colors.Clear();
_smoothedPosition.reset = true;
//每次BeginNewLine会新建一个mesh
_mesh = new Mesh();
_mesh.name = "Line Mesh";
_mesh.MarkDynamic();//标记为动态,这样mesh就会使用动态的buffer,在渲染时的性能更高。
//用脚本动态添加GameObject
GameObject lineObj = new GameObject("Line Object");
//初始化transform
lineObj.transform.position = Vector3.zero;
lineObj.transform.rotation = Quaternion.identity;
lineObj.transform.localScale = Vector3.one;
//添加MeshFilter以及材质
lineObj.AddComponent<MeshFilter>().mesh = _mesh;
lineObj.AddComponent<MeshRenderer>().sharedMaterial = _parent._material;
return lineObj;
}
public void UpdateLine(Vector3 position) {
//更新SmoothedPosition,这里传入的position是PinchDetector.position。
_smoothedPosition.Update(position, Time.deltaTime);
bool shouldAdd = false;
//当vertices为空 或者 position与prevRing0的距离超过minSegmentLength的时候,需要调用addRing()去添加ring。
shouldAdd |= _vertices.Count == 0;
shouldAdd |= Vector3.Distance(_prevRing0, _smoothedPosition.value) >= _parent._minSegmentLength;
//addRing添加ring,updateMesh去更新mesh的数据。
if (shouldAdd) {
addRing(_smoothedPosition.value);
updateMesh();
}
}
public void FinishLine() {
;
//通过vertices、normals、triangles等可以通过脚本绘制mesh,mesh的data会被标记为modified,在下一次传给GraphicsAPI去渲染。而调用UploadMeshData可以强制将data立刻传给API渲染。
//而markNoLongerReadable参数则表示是否清空mesh data在内存中占用的空间。由于这里是FinishLine,所以设置为true。
_mesh.UploadMeshData(true);
}
//将顶点等数组的数据给刷新到mesh中去,从而更新场景中的mesh。
private void updateMesh() {
_mesh.SetVertices(_vertices);
_mesh.SetColors(_colors);
_mesh.SetUVs(0, _uvs);
//以Triangles的方式设置mesh的sub-mesh
_mesh.SetIndices(_tris.ToArray(), MeshTopology.Triangles, 0);
//重新计算边界和法向量
_mesh.RecalculateBounds();
_mesh.RecalculateNormals();
}
//添加Ring&#x