过了这么久才来更这篇实在是因为项目工程量实在是不允许
首先声明我并不是专业的UI设计人员 我们所有的leap UI设计全部来源与项目需求 且因为项目不是商业项目 所以设计方法看起来有一种“邪门歪道”的既视感 但是为VR/AR环境中的交互设计提供了一种思路
特别提示:以下内容并不是针对初学者而言的
如果你是个unity/leap开发的双面小白 那么基本可以不用看这篇文章了
整个项目的效果可以去这里看视频
OK~
言归正传
我们先来看看效果:
这是一个用手势控制选择的界面选择的时候会根据手势的挥动方向来进行左右切换
也就是我们上一篇文章中提到的手势操作交互,这类操作不与UI元素进行交运算(触发)
所以设计起来相对独立 也就是说 这类UI元素的制作可以按照普通的UI制作方式来进行
所以 在整个软件中 这个UI显得最普通 但是就是普通依旧不是那么好做
想实现一个类似IPhone音乐的选择界面并不是那么容易
像这样
这种动态效果通过静态图片无法很好地展示 但是想试想一个细微的效果实际上需要大量的工作
这个UI整个比较复杂所以我只能不太完整的去讲解
这是它的目录结构 在某一个版本中 我曾经写了动态生成这些展示标记 但是由于数量变化会引起后面的缩放系数bug 我就改回了手动添加
这是每个UI元素的组成 其中有些组件是不必要的主要是为了原先鼠标操作而设计
( 后来整体取消了软件鼠标操作的功能 但保留了这些组件 比如碰撞器 对于一个手势操纵的UI类型来说完全是没必要的)
所以
针对这个UI元素来说 可讲的就只剩下
1.怎么细致的实现元素转动效果
2.怎么用手势控制旋转
一,转动效果 这其实很多demo里面都会出现 而这个实现的那我们来看看主要的结构
命名清晰明了 enhance控制器 控制 gameobject(UItexture)
UItexture下放了一个canvas 然后canvas上有三个text组件放置三组文字
sprite是背景控制(这里的sprite其实就是截取图形的插件)
有了六七列表我们就可以在控制器里通过代码来控制了
有两个类一个 EnhancelScrollView一个 EnhanceItem
首先定义 EnhanceItem脚本附在每个UItexture上面 设置flag
using UnityEngine;
using System.Collections;
public class EnhanceItem : MonoBehaviour
{
// 在ScrollViewitem中的索引
internal int scrollViewItemIndex = 0;
// 夹角大小
internal float angla = 0f;
// 动画时间值
internal float dValueTime = 0f;
// 前后项
internal EnhanceItem front, back;
/*
*
*
*
*
* internal关键字是类型和类型成员的访问修饰符。只有在同一个程序集的文件中,内部类型或者是成员才可以访问。
* 这是msdn上对internal的描述。
* 类型就是enum(枚举类型),class(类),interface(接口),struct(结构)等类型。
* 类型成员如函数,成员变量等。
*
* 一个完整的.exe或者是.dll文件就是一个程序集,一般伴随着exe程序集产生的还有一个程序集清单
* ,.exe.config文件。下面我就用一个例子来说明“internal关键字是类型和类型成员的访问修饰符。
* 只有在同一个程序集的文件中,内部类型或者是成员才可以访问”。
*
*
*/
public int flag = 777;//flag
private Vector3 targetPos = Vector3.one;
private Vector3 targetScale = Vector3.one;
private Transform mTrs;
private UITexture mTexture;
void Awake()
{
mTrs = this.transform;
mTexture = this.GetComponent<UITexture>();
}
void Start()
{
UIEventListener.Get(this.gameObject).onClick = OnClickScrollViewItem;
}
// 当点击Item,将该item移动到中间位置
private void OnClickScrollViewItem(GameObject obj)
{
EnhancelScrollView.GetInstance().SetHorizontalTargetItemIndex(scrollViewItemIndex);
}
/// <summary>
/// 更新该Item的缩放和位移
/// </summary>
public void UpdateScrollViewItems(float xValue, float yValue, float scaleValue)
{
targetPos.x = xValue;
targetPos.y = yValue;
targetScale.x = targetScale.y = scaleValue;
mTrs.localPosition = targetPos;
mTrs.localScale = targetScale;
}
public void SetSelectColor(bool isCenter)
{
if (mTexture == null)
mTexture = this.GetComponent<UITexture>();
if (isCenter)
mTexture.color = Color.white;
else
mTexture.color = Color.gray;
}
}
EnhancelScrollView脚本作为控制脚本附着在控制物体上
他就相当于每个物体
那么手势控制:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Leap;
// [ExecuteInEditMode]
public class EnhancelScrollView : MonoBehaviour
{
// 含有滑动项目的面板
public GameObject enhanceScrollView;
// 缩放曲线
public AnimationCurve scaleCurve;
// 位移曲线
public AnimationCurve positionCurve;
// 动画时间
public float duration = 0.2f;
// 宽度
public float width = 800.0f;
// y轴坐标固定值(所有的item的y坐标一致)
public float yPositionValue = 46.0f;
// 中间显示目标时间线(0显示第一个,0.5显示中间一个)
public float horizontalTargetValue = 0.0f;
// 滑动起始力
public float touchStartPower = 0.5f;
// 滑动阻力
public int touchForce = 120;
// 目标对象列表
private List<EnhanceItem> scrollViewItems;
// 目标对象Widget脚本,用于depth排序
private List<UITexture> textureTargets;
// 开始X坐标
private float startXPos = 0f;
// 当前处于中间的item
public EnhanceItem centerItem;
// 当前出移动中,不能进行点击切换
private bool isInChange = false;
// 位置动画的中间位置时间
private float positionCenterTime = 0.5f;
// 当前精度小数位
private int curACC = 4;
// 横向变量值
private float horizontalValue = 0.0f;
// 移动动画参数
private float originHorizontalValue = 0.0f;
private float currentDuration = 0.0f;
private static EnhancelScrollView instance;
internal static EnhancelScrollView GetInstance()
{
return instance;
}
//内部类型或成员才是可访问的
//__________________________________________________________
// 动作识别部分定义
public HandController hc;
Hand lefthand=null;
Hand righthand = null;
Hand last_lefthand = null;
Hand last_righthand = null;
Frame currentFrame = null;//定义当前帧
bool lefthandexist = false;//判断左右手是否在场景中存在
bool righthandexist = false;
float sweptAngle = 0;//初始化角度为零
int mark=0;//标记 与下面的函数作用
// 0代表不转动,-1代表向左转,1代表向右转
//____________________________________________________________
void checkmark(int sign)
{
if (sign == 1) {
SetHorizontalTargetItemIndex(centerItem.front.scrollViewItemIndex);
}
if (sign == -1) {
SetHorizontalTargetItemIndex(centerItem.back.scrollViewItemIndex);
}
mark = 0;
}
//执行转动的真正操作 检查标记从而确保只执行一次否则一次挥动将转动多次
void Awake()
{
instance = this;
}
void Start()
{
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPECIRCLE);
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPESWIPE);
hc.GetLeapController().EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);
hc.GetLeapController ().EnableGesture (Gesture.GestureType.TYPEKEYTAP);
hc.GetLeapController ().EnableGesture (Gesture.GestureType.TYPEINVALID);
//开启所有手势 其实在这个脚本中只开启typeswipe就够了
// ____________________________________________
InitData();//初始化数据
// 设置第一个为选中状态
SetHorizontalTargetItemIndex(0);
}
/// <summary>
/// 初始化数据
/// </summary>
private void InitData()
{
startXPos = -(width / 2);
scrollViewItems = new List<EnhanceItem>();
scrollViewItems.AddRange(enhanceScrollView.GetComponentsInChildren<EnhanceItem>());
if (textureTargets == null)
textureTargets = new List<UITexture>();
float anglaDValue = 360 / scrollViewItems.Count;
int centerIndex = scrollViewItems.Count / 2;
for (int i = 0; i < scrollViewItems.Count; i++)
{
scrollViewItems[i].scrollViewItemIndex = i;
scrollViewItems[i].angla = anglaDValue * i;
scrollViewItems[i].dValueTime = GetCurveTimePos(scrollViewItems[i].angla);
// 构造环形链
scrollViewItems[i].front = i == 0 ? scrollViewItems[scrollViewItems.Count - 1] : scrollViewItems[i - 1];
scrollViewItems[i].back = i == (scrollViewItems.Count - 1) ? scrollViewItems[0] : scrollViewItems[i + 1];
UITexture tmpTexture = scrollViewItems[i].gameObject.GetComponent<UITexture>();
textureTargets.Add(tmpTexture);
scrollViewItems[i].SetSelectColor(false);//设为选中状态 调用了enhanceitem中的方法将mtexture中的混合颜色设为白色以显示高亮
}
}
//_____________________________________________________________________________________________
void Update()
{
if (!isInChange)
{
touch();
return;
}
currentDuration += Time.deltaTime;
float percent = currentDuration / duration;
horizontalValue = Mathf.Lerp(originHorizontalValue, horizontalTargetValue, percent);
UpdateEnhanceScrollView(horizontalValue);
SortDepth();
if (currentDuration > duration)
{
centerItem = textureTargets[textureTargets.Count - 1].gameObject.GetComponent<EnhanceItem>();
centerItem.SetSelectColor(true);
isInChange = false;
}
}
/// <summary>
/// 更新水平滚动
/// </summary>
/// <param name="fValue"></param>
private void UpdateEnhanceScrollView(float fValue)
{
for (int i = 0; i < scrollViewItems.Count; i++)
{
EnhanceItem itemScript = scrollViewItems[i];
float xValue = GetXPosValue(fValue, itemScript.dValueTime);
float scaleValue = GetScaleValue(fValue, itemScript.dValueTime);
itemScript.UpdateScrollViewItems(xValue, yPositionValue, scaleValue);
}
}
//滑动X轴增量位置
float xMoved;
private void touch()
{
// 记录滑动位置
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
//获取手指自最后一帧的移动
float x = Input.GetTouch(0).deltaPosition.x;
xMoved = x;
}
// 滑动结束时判断故事翻页
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Ended)
{
if (centerItem == null || Mathf.Abs(xMoved) < touchStartPower)
return;
int count = (int)(Mathf.Abs(xMoved * scrollViewItems.Count/ touchForce)) + 1;
int minHalfCount = Mathf.CeilToInt((float)scrollViewItems.Count / 2) - 1;
if (count > minHalfCount)
{
count = minHalfCount;
}
if(xMoved > 0)
{
SetHorizontalTargetItemIndex(GetMoveIndex(centerItem, -count));
}
else if (xMoved < 0)
{
SetHorizontalTargetItemIndex(GetMoveIndex(centerItem, count));
}
xMoved = 0;
}
}
/// <summary>
/// 缩放曲线模拟当前缩放值
/// </summary>
private float GetScaleValue(float sliderValue, float added)
{
float scaleValue = scaleCurve.Evaluate(positionCenterTime + sliderValue + added);
return scaleValue;
}
/// <summary>
/// 位置曲线模拟当前x轴位置
/// </summary>
private float GetXPosValue(float sliderValue, float added)
{
float evaluateValue = startXPos + positionCurve.Evaluate(positionCenterTime + sliderValue + added) * width;
return evaluateValue;
}
/// <summary>
/// 计算位置动画中的时间点
/// </summary>
/// <param name="anga">角度值,360度=1</param>
/// <returns></returns>
private float GetCurveTimePos(float anga)
{
// 设定0.5为位置中间
return Round(anga / 360f, curACC);
}
// 获取项目A到项目B之间最小的时间差值(圆形角度计算,1=360度)
private float GetCurveTimeDValue(EnhanceItem itemA, EnhanceItem itemB)
{
return Round((Mathf.DeltaAngle(itemA.angla, itemB.angla)) / 360f, curACC);
}
private void SortDepth()
{
textureTargets.Sort(new CompareDepthMethod());
for (int i = 0; i < textureTargets.Count; i++)
textureTargets[i].depth = i;
}
/// <summary>
/// 用于层级对比接口
/// </summary>
private class CompareDepthMethod : IComparer<UITexture>
{
public int Compare(UITexture left, UITexture right)
{
if (left.transform.localScale.x > right.transform.localScale.x)
return 1;
else if (left.transform.localScale.x < right.transform.localScale.x)
return -1;
else
return 0;
}
}
//核心滚动函数
/// <summary>
/// 设置横向轴参数,根据缩放曲线和位移曲线更新缩放和位置
/// </summary>
internal void SetHorizontalTargetItemIndex(int itemIndex)
{
if (isInChange)
return;
EnhanceItem item = scrollViewItems[itemIndex];//_____________新场景中根据这个来判断
if (centerItem == item)
return;
Debug.Log ("item = " + item.name);
if (item.name == "Texture01") {
//Debug.Log("YES");
}
// _________________________________________________________________________________________
float dvalue = centerItem == null ? 0 : GetCurveTimeDValue(centerItem, item);
// 更改target数值,平滑移动,设负数倒着转
horizontalTargetValue += -dvalue;
beginScroll(horizontalValue, horizontalTargetValue);
}
void FixedUpdate()
{
// ___________________________________________________________________________________手势swipe模块
this.currentFrame = hc.GetFrame ();
Frame frame = hc.GetFrame ();
Frame lastframe = hc.getlastframe ();
GestureList gestures = this.currentFrame.Gestures ();
Vector swipedirection=null;
foreach (Gesture g in gestures) {
if(g.Type==Gesture.GestureType.TYPE_SWIPE)
{
SwipeGesture swipe=new SwipeGesture(g);
swipedirection=swipe.Direction;
//Debug.Log("direction is "+swipedirection);
}
}
if (swipedirection.x > 0) {//判断手势向左还是向右参数向左则小于0向右则大于0
Debug.Log("right");
mark=1;
}
if (swipedirection.x < 0) {
Debug.Log("left");
mark=-1;
}
checkmark (mark);//检查参数以完成UI的旋转
// ————————————————————————————————————————————————————————————————————————————————————————————————————————————
}
/// <summary>
/// 开始滚动
/// </summary>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
private void beginScroll(float startTime, float endTime)
{
if (isInChange)
return;
foreach (EnhanceItem item in scrollViewItems)
{
item.SetSelectColor(false);
}
originHorizontalValue = Round(startTime, curACC);
horizontalTargetValue = Round(endTime, curACC);
currentDuration = 0.0f;
isInChange = true;
}
/// <summary>
/// 向右选择角色按钮
/// </summary>
public void OnBtnRightClick()
{
if (isInChange)
return;
SetHorizontalTargetItemIndex(centerItem.back.scrollViewItemIndex);
}
/// <summary>
/// 向左选择按钮
/// </summary>
public void OnBtnLeftClick()
{
if (isInChange)
return;
SetHorizontalTargetItemIndex(centerItem.front.scrollViewItemIndex);
}
/// <summary>
/// 获取移动后的项目索引
/// </summary>
/// <param name="item">当前项目</param>
/// <param name="count">移动位数,负数表示倒移</param>
/// <returns></returns>
private int GetMoveIndex(EnhanceItem item, int count)
{
EnhanceItem curItem = item;
for (int i = 0; i < Mathf.Abs(count); i++)
{
curItem = count > 0 ? curItem.back : curItem.front;
}
return curItem.scrollViewItemIndex;
}
/// <summary>
/// 按指定小数位舍入
/// </summary>
/// <param name="f"></param>
/// <param name="acc"></param>
/// <returns></returns>
private float Round(float f, int acc)
{
float temp = f * Mathf.Pow(10, acc);
return Mathf.Round(temp) / Mathf.Pow(10, acc);
}
/// <summary>
/// 截取小数
/// </summary>
/// <param name="f"></param>
/// <returns></returns>
private float CutDecimal(float f) {
return f - (int)f;
}
}
以缩放曲线来决定 元素在位置发生转动后 大小的变化规则
以位移曲线来规定 元素的运动位置轨迹
两条曲线分别为:
可见一个小功能的实现竟然用了500+line
虽然不难 但是这个排除bug 保证逻辑正确的过程是在太令人头疼 构造环形链的代码也比较抽象
scrollViewItems = new List<EnhanceItem>();
scrollViewItems.AddRange(enhanceScrollView.GetComponentsInChildren<EnhanceItem>());
而之后的代码将手势识别的代码也包括进去了 主要思路就是判断挥动手势的方向 根据这个参数来确定UItexture整体转动的方向
至此我们的这个类型的UI代码讲解也结束了
总结一下手势控制类UI:
1,此类UI构建方式和普通UI区别较小(几乎没区别)无论是平面,立体。
2,此类UI的控制方式最好生成一个统一的操作函数例如turnleft(),turnright();之后直接用手势触发就好
3,如果使用单一手势触发的话最好设置一定的时延 否则 误操作将会非常多
最后
也就是这个系列的最后一些内容
我来讲讲印射式UI(或者说印射操作)
这里用到的就是
射线
leap和射线可以说是非常蛋疼的组合 我本身对这个功能的探索时间长达一个月
我就细细说一下这个奇葩的功能的由来:
首先
面对一个模型我们想把它散开 再在每个子物体上加上盒触发器 这种算法在unity里简直好像一坨屎
结果就如下图:
boxcollider不能很好的契合模型
Meshcollider又因为面数限制而不能使用
那只能用box凑活了 这就导致一个问题用手来触发这一坨屎一样的东西自然是很不精确的
并且一旦触及百万面的工业模型 性能就迅速下降到崩溃边缘:
像这样 于是我们怎么才能解决这个精确触发的问题呢?这是问题一
其二
leapmotion的手的大小和识别区域是绑定的 所以放大识别区域的结果是必然要放大手的模型
比如我想增大在空间内控制器的操作范围就会产生这种画面
手的模型变大严重阻碍了软件的使用,所以又要保证手的大小适中作指示
而手的操作范围被限制在控制器识别范围之内。这就诞生了一个设计上的矛盾。
画面上要求手变小 操作范围上要求手变大。
于是
就有了射线
初次写射线功能其实和leap并不能很好的一起工作,因为有太多的隐含参数数值需要去探索
用这种方法可以进行精确化的触发,选择。在这个基础上进行设计可以很多leap官方demo完成不了的操作
那下面我们先来看看代码 这个脚本挂camera上
using UnityEngine;
using System.Collections;
using Leap;
public class ray : MonoBehaviour {
public hand_script2 control_script;//控制脚本定义
public HandController hc=null;
public RaycastHit hit;
public Vector3 handdir;
public Vector3 handpos;
//public GameObject cube = null;
public GameObject highlight=null;
float hx, hy, hz=0;
float dx, dy, dz = 0;
public GameObject particle_light=null;
float xr,yr,zr;
private float index=0;
Hand lefthand=null;
Hand righthand=null;
public GameObject color_apply=null;
Color origin=Color.black;
public Color apply_color;
// ______________________________________________________________
bool squize(float radius)//判断手势是否为握持 阀值为35
{
if (radius < 37)
return true;
else
return false;
}
// ---------------------------------------------------------------------------
private bool IsHand(Collider collider)
{
return collider.transform.parent && collider.transform.parent.parent && collider.transform.parent.parent.GetComponent<HandModel>();
}//判断触发器是否是手
void Awake()
{
}
// Use this for initialization
void Start () {
Debug.Log ("Start");
//空间两点距离计算:sqrt(pow(x1-x2,2)+pow(y1-y2,2)+pow(z1-z2,2))
index = hc.transform.localScale.x / 1000;//计算后面的缩放指数
}
// Update is called once per frame
void Update () {
bool lefthandexit = false;
bool righthandexit = false;
//Debug.Log ("Update");
//Vector3 fwd = new Vector3 (0, 0,10 );
Frame frame = hc.GetFrame ();
Frame lastframe = hc.getlastframe ();
apply_color = color_apply.gameObject.GetComponent<RGB> ().rgb;//获取color混合界面的颜色值可去掉
foreach (Hand hand in frame.Hands) {
if (hand.IsLeft) {
lefthandexit = true;
lefthand = hand;
}
if (hand.IsRight) {
righthandexit = true;
righthand = hand;
foreach (Finger finger in righthand.Fingers) {
Finger.FingerType type=finger.Type();
if (type == Finger.FingerType.TYPE_INDEX) {
dx = finger.Direction.x;
dy = finger.Direction.y;
dz = finger.Direction.z;
hx = finger.TipPosition.x;
hy = finger.TipPosition.y;
hz = finger.TipPosition.z;
}
//获取中指的信息finger_INDEX
}
//dx = hand.Direction.x;
//dy = hand.Direction.y;
//dz = hand.Direction.z;
handdir = new Vector3 (dx*index,dy*index,-dz*index);//必须乘指数
// ----------------------------------------------------------------------
//hx = hand.PalmPosition.x;
//hy = hand.PalmPosition.y;
//hz = hand.PalmPosition.z;
handpos = new Vector3 (hx*index, hy*index,-hz*index);//同样乘指数
// ----------------------------------------------------------
//Debug.Log ("righthand_position is " + handpos + "righthand dir is " + handdir);
//Debug.Log ("reall date is" + hand.PalmPosition + " /// " + hand.Direction);
xr = hand.RotationAngle (lastframe, Vector.XAxis);
yr = hand.RotationAngle (lastframe, Vector.YAxis);
zr = hand.RotationAngle (lastframe, Vector.ZAxis);
}
}
//cube.transform.Rotate (new Vector3(xr*index,yr*index,-zr*index),Space.World);
//原先用cube表示手的信息与射线做对照
/*
float x = this.transform.rotation.x;
float y = this.transform.rotation.y;
float z = this.transform.rotation.z;
Vector3 rota = new Vector3 (x, y, z);
Debug.Log ("this.position=" + this.transform.position);
dx = this.transform.position.x;
dy = this.transform.position.y;
dz = this.transform.position.z;
Vector3 position = new Vector3 (dx,dy+100,dz);
*/
if (squize(lefthand.SphereRadius) && (!squize (righthand.SphereRadius))) {
//如果左手握持且右手不握持
//bool Physics.Raycast(Ray ray, Vector3 direction, RaycastHit out hit, float distance, int layerMask)
if (Physics.Raycast(handpos,handdir,out hit,10000,1)) {
//(Physics.Raycast(射出点,射出方向向量,输出触发信息存储点,长度,1))
particle_light.transform.position =new Vector3(hit.point.x,hit.point.y,hit.point.z-2);//此处为粒子系统 用粒子系统跟随触发点以显示触发位置
if ((highlight.gameObject.name.ToString() != hit.collider.gameObject.name.ToString()) &&
origin == Color.black&&
(!IsHand(hit.collider))&&
(hit.collider.gameObject.GetComponent<Renderer>()!=null)) {
//判断是否第一次触发
Debug.Log ("first");
highlight = hit.collider.gameObject;
control_script.main_son = hit.collider.gameObject;
origin = highlight.gameObject.GetComponent<Renderer> ().material.color;
highlight.gameObject.GetComponent<Renderer> ().material.color = Color.gray;
}
if ((highlight.gameObject.name.ToString () != hit.collider.gameObject.name.ToString ()) &&
origin != Color.black&&
(!IsHand(hit.collider))&&
((hit.collider.gameObject.GetComponent<Renderer>()!=null))) {
//判断是否变更触发物体
Debug.Log ("change");
control_script.main_son = hit.collider.gameObject;
highlight.gameObject.GetComponent<Renderer> ().material.color = origin;
highlight = hit.collider.gameObject;
origin = highlight.gameObject.GetComponent<Renderer> ().material.color;
highlight.gameObject.GetComponent<Renderer> ().material.color = Color.gray;
}
//以上用来交换信息 坐到触发高亮
/*
if((highlight.gameObject.name.ToString()==hit.collider.gameObject.name.ToString())&&origin!=Color.black)
{
highlight = hit.collider.gameObject;
highlight.gameObject.GetComponent<Renderer> ().material.color = Color.red;
}
*/
//hit.collider.gameObject.GetComponent<Renderer> ().material.color = Color.red;
Debug.Log ("OBJ-name is " + hit.collider.gameObject);
//Debug.Log ("hit point is " + hit.point + " distance = " + hit.distance);
//Debug.Log ("Success");
Debug.DrawLine (handpos, hit.point, Color.red);
//在sence中显示射线并设置为红色
//Debug.Break ();
}
if (lefthandexit==true) {
Debug.Log ("________________");
//Debug.Break();
}
}
}
}
//射线脚本bug汇总:
/*
* 1.发射点以世界坐标为准则:
* 如handcontroller的放大倍率为100则数乘index指数为0.1
* 2.必须将控制器的位置考虑进去
* 3.射线距离尽可能大
*/
脚本毕来说一些特殊的地方
那些隐含的值最后确定为
index = hc.transform.localScale.x / 1000;//计算后面的缩放指数
而以上都基于 控制器在原点也就是(0,0,0)
而针对 非原点的情况
要把控制器的坐标也算进去才能保证你的射线是从指尖发出去的
如果想更改射线发射的位置 那么判断进其他的手指类型就好了 代码里面非常清楚
同时 在射线的位置用一个粒子特效做一个触发位置提示 效果像这样
在这个体系之下 你就可以把手指当做一个三维环境里的鼠标 对任意一个点进行触发操作以进一步的执行其他部分
所以我们的触发UI可以不用再设计在控制器的范围之内 我们可以用射线功能触发几乎无限远的元素
比如在一个环境中我们通过一定映射关系去触发区域之外的按钮
如图所示
至此
所有LeapUI设计专题所有内容都讲完了
洋洋洒洒的三万多字
再次:
本人水平有限,有错误请联系qq:785929784
也欢迎有兴趣的开发者加入qq群:343074971
共同交流共同进步。
感谢群友对本博客的支持
后续将更新
1.open cv专题系列
2.c++与算法导论的爱情故事系列
3.计算几何常用算法专题
4.unity shader专题系列
5.unity AI专题系列
6.cuda并行运算入门
欢迎支持