Skip to main content
 首页 » 资源教程

Steam VR的使用(二)

2016年08月24日 16:08:196580

SteamVR 抛物线移动

其实实现抛物线很简单,生成一组抛物点,然后将点渲染成线就好。渲染成线有很多方式,你可以用模型,也可以用GL的绘制线段,也可以用LineRender。重点是优化点的生成计算。

楼主上班比较忙,很少写demo。我做项目一般有严密的框架,但是为了更加简明的为大家展示功能的实现剔除了很多代码,并且写了不符合我风格的代码,就是为了让大家能看清楚功能的实现。

我不是个人开发者,家中无测试环境,如果有问题可以在下面回复。本教程中的代码已经用在实际的案例上,因为工作保密我不能截图或者共享项目源码,只是把开发过程的部分代码功能以个人的名义分享,欢迎交流。

如何生成抛物点,一个很简单的公式:

nextPos = lastPos + 水平位移 + 垂直位移。

稍后的代码有详尽的注释我就不多赘述了。

重点是对点集的优化,优化抛物点分两个部分,一是如何计算碰撞点,二是内存开销的优化。考虑到一般的VR项目只在水平面上进行移动,所以通过判断抛物点的y轴来判定碰撞。关于内存开销,我们可以限定生成的抛物点个数,同时优化计算。我们使用一个List来保存点集,动态的根据点的情况来生成点,或者改变点的位置。例如当手柄角度不变时,只需要将List集合中的点改变Y轴就行了。

现在思路已经明了了。以下是伪代码:

 int i = 0;

while( nextPos.y>0 && maxPoint>0 ){

if(list.count<=i){

list.add(nextPos);

}

else{

list[i] = nextPos;

}

///生成,优化,计算下一个抛物点

i++;

}

list.remove(i,list.count-i);//移除上一个点集的多余数据

生成抛物线点集后,接下来就是绘制曲线,LineRender,GL都可以很方便的绘制。但是就性能来说,GL更加快一点。建议大家用GL。当然大家也可以使用自己的模型绘制,原理就是,在对应的点生成你的模型,然后计算对应的角度即可。这几种实现方案我都会写到代码中,效果大家自己调用查看。

最后就是生成抛物线的终点,因为我是按照y轴来判断抛物线的落点,但是落点可能有障碍物等等,或者落点在墙角,这些位置显然是不能跳跃的,所以我们需要对落点进行判断。在Physics中有一个方法可以检测一个球形范围是否会碰撞,我们可以给地面添加一个层,以便忽略地面的检测,这样就可以安全的着陆了。

好了,也许你没听懂,没关系,接下来就是代码,代码中也有详尽的注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
using UnityEngine;
using System.Collections.Generic;
using System;
/// <summary>
/// 抛物线脚本
/// </summary>
public class HandRay : MonoBehaviour
{
    private Transform CurrentHand; //当前触发的手
    private List<Vector3> pointList; //曲线点集合
    private Quaternion lastRotation; //上一个移动的角度
    private Vector3 lastPostion; //上一个位置
    private Vector3 lastPos; //上一个点,为了优化将一个临时变量做成全局的,节省内存开销
    private Vector3 nextPos;//下一个点,理由同上
    private event Action OnChangeTransform;//一个事件,用来检测手柄位置和角度变化的
    private Material material;//渲染射线的材质球
    private Vector3 HitPoint;//抛物线的碰撞点
    private Ray ray;
    private bool canJump = false;//
    public GameObject PointEffect;//一个特效,就是在射线的终点放置一个光柱什么的,大家可以自己做这个特效
    public int MaxPoint;  //生成曲线的点最大数量
    public float Distence;//水平位移
    public float Grity;//垂直位移
    public float CheckRange;//检测位置是否存在障碍物
    public void Awake()
    {
        SetData();
    }
    public void Start()
    {
        pointList = new List<Vector3>();
        OnChangeTransform += OnChangeTransformCallBack;
        HitPoint = -Vector3.one;
        ray = new Ray();
    }
    public void Update()
    {
        //当手柄按下触摸键同时角度合适时触发事件开始计算点
        if (CurrentHand != null && ((CurrentHand.eulerAngles.x > 275 && CurrentHand.eulerAngles.x <= 360) || (CurrentHand.eulerAngles.x >= -0.01f && CurrentHand.eulerAngles.x < 85)))
        {
            if (OnChangeTransform != null) OnChangeTransform();
        }
        else
        {
            pointList.Clear();
            PointEffect.SetActive(false);
        }
    }
    /// <summary>
    /// 计算抛物线的点
    /// 此方法已经优化过性能
    ///
    /// </summary>
    private void OnChangeTransformCallBack()
    {
        if (lastRotation != CurrentHand.rotation || lastPostion != CurrentHand.position)
        {
            lastPos = nextPos = CurrentHand.position;
            int i = 0;
            while (nextPos.y > 0 && (i < MaxPoint))
            {
                if (pointList.Count <= i)
                {
                    pointList.Add(nextPos);
                }
                else
                {
                    pointList[i] = nextPos;
                }
                if (lastRotation == CurrentHand.rotation && lastPostion != CurrentHand.position && i < pointList.Count - 1)
                {
                    nextPos = pointList[i + 1] + CurrentHand.position - lastPostion;
                }
                else
                {
                    nextPos = lastPos + CurrentHand.rotation * Vector3.forward * Distence + Vector3.up * Grity * 0.1f * i * Time.fixedDeltaTime;
                }
                lastPos = nextPos;
                i++;
            }
            if (pointList.Count > i)
            {
                pointList.RemoveRange(i, pointList.Count - i);
            }
            lastRotation = CurrentHand.rotation;
            lastPostion = CurrentHand.position;
            if (pointList.Count > 1)
            {
                HitPoint = pointList[pointList.Count - 1];
                PointEffect.SetActive(true);
                PointEffect.transform.position = HitPoint;
            }
            else
            {
                HitPoint = -Vector3.one;
                PointEffect.SetActive(false);
            }
        }
    }
    public void Enable()
    {
        SteamVR_InitManager.Instance.OnLeftDeviceActive += OnHandActive;
        SteamVR_InitManager.Instance.OnRightDeviceActive += OnHandActive;
        OnChangeTransform += OnChangeTransformCallBack;
    }
    public void OnHandActive(SteamVR_TrackedObject obj)
    {
        DeviceInput device = obj.GetComponent<DeviceInput>();
        device.OnPressDownPadV3 += OnPressDownPad;
        device.OnPressUpPad += OnPressUpPadAction;
    }
    public void OnHandDis(SteamVR_TrackedObject obj)
    {
        if (obj && obj.gameObject.activeSelf)
        {
            DeviceInput device = obj.GetComponent<DeviceInput>();
            device.OnPressDownPadV3 -= OnPressDownPad;
            device.OnPressUpPad -= OnPressUpPadAction;
        }
    }
    public void Disable()
    {
        SteamVR_InitManager.Instance.OnLeftDeviceActive -= OnHandActive;
        SteamVR_InitManager.Instance.OnRightDeviceActive -= OnHandActive;
        OnHandDis(SteamVR_InitManager.Instance.LeftObject);
        OnHandDis(SteamVR_InitManager.Instance.LeftObject);
        OnChangeTransform -= OnChangeTransformCallBack;
    }
    public void SetData()
    {
        if (PointEffect)
            PointEffect.SetActive(false);
    }
    /// <summary>
    /// 抬起触摸板时,计算落脚点
    /// </summary>
    private void OnPressUpPadAction()
    {
        if (CurrentHand == nullreturn;
        canJump = true;
        ray.origin = CurrentHand.position;
        Vector3 dir = HitPoint - CurrentHand.position;
        ray.direction = dir;
        if (Physics.CheckSphere(HitPoint, CheckRange, ~(1 << 8)))
        {
            canJump = false;
        }
        if (canJump)
        {
            JumpPoint(HitPoint);
        }
        CurrentHand = null;
    }
    /// <summary>
    /// 跳到指定的点
    /// </summary>
    /// <param name="point"></param>
    public void JumpPoint(Vector3 point)
    {
        point.y = transform.position.y;
        transform.position = point;
    }
    private void OnPressDownPad(Transform parent)
    {
        CurrentHand = parent;
    }
    /// <summary>
    /// 使用GL来绘制曲线
    /// 将点绘制出来
    /// </summary>
    void OnRenderObject()
    {
        material.SetPass(0);
        if (pointList == nullreturn;
        GL.Begin(GL.LINES);
        for (int i = 0; i < pointList.Count; i++)
        {
            GL.Vertex(pointList[i]);
        }
        GL.End();
    }
    /// <summary>
    /// 一个额外的附加方法,即用一个曲线来绘制抛物线,性能较低,因为点数比较多
    /// 感兴趣的可以把此方法添加到Update中更新
    /// </summary>
    public void ShowLineByRender()
    {
        LineRenderer line = GetComponent<LineRenderer>();
        if (line)
        {
            line.SetVertexCount(pointList.Count);
            for (int i = 0; i < pointList.Count; i++)
            {
                line.SetPosition(i, pointList[i]);
            }
        }
    }
}

更多AR新闻就在中国AR网(http://www.chinaar.com/)


评论列表暂无评论
发表评论