您的位置:首页 > 移动开发 > Unity3D

实现一个简单的Unity3D三维拾取——3D Picking (1)

2014-05-20 21:21 489 查看
3D Picking 原理就是从相机位置向空间中发射一条射线,根据射线击中的物体来进行拾取。

 

这里我们使用触摸屏触摸来进行拾取,鼠标的拾取原理一样,只不过选用的API不同。

从unity3D官网Manual里找到以下Input内容:

http://docs.unity3d.com/Documentation/Manual/Input.html

其中有段例子程序:

Following is an example script which will shoot a ray whenever the user taps on the screen:

var particle : GameObject;
function Update () {
for (var touch : Touch in Input.touches) {
if (touch.phase == TouchPhase.Began) {
// Construct a ray from the current touch coordinates
var ray = Camera.main.ScreenPointToRay (touch.position);
if (Physics.Raycast (ray)) {
// Create a particle if hit
Instantiate (particle, transform.position, transform.rotation);
}
}
  }
}


var ray = Camera.main.ScreenPointToRay (touch.position);
if (Physics.Raycast (ray))
这两句代码是关键代码,我们从这里入手。

查找ScreenPointToRay文档:

http://docs.unity3d.com/Documentation/ScriptReference/Camera.ScreenPointToRay.html

节选文档中主要描述和一段例子

Ray
ScreenPointToRay(Vector3
position);

Returns a ray going from camera through a screen point.

using UnityEngine;
using System.Collections;

public class Example : MonoBehaviour {
void Update() {
Ray ray = camera.ScreenPointToRay(new Vector3(200, 200, 0));
Debug.DrawRay(ray.origin, ray.direction * 10, Color.yellow);
}
}

上面的Debug.DrawRay留着后面调试时使用,可以看到发射的射线。

 

接下来Camera.main.ScreenPointToRay中的Camera.main是什么意思呢,查找Camera.main

static Camera main; 

The first enabled camera tagged "MainCamera" (Read Only).

 


即unity新建一个工程后,默认的一个main Camera

查找Physics.Raycast文档,

http://docs.unity3d.com/Documentation/ScriptReference/Physics.Raycast.html

文档内容很多,其中有一个

static bool Raycast(Vector3origin, Vector3direction, RaycastHithitInfo, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers);

红色的两个参数我们后面将会用到,在查看RaycastHithitInfo,
http://docs.unity3d.com/Documentation/ScriptReference/RaycastHit.html
 

至此,我们可以实现一个简单的Picking了,创建sphere, plane, Direction Light,和一个Empty GameObject取名GameController,并新建一个脚本与其绑定。



打开GameController.cs,输入以下代码:

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
void Update ()
{
if(Input.touchCount == 1)
{
Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch(0).position);

Debug.DrawRay (ray.origin, ray.direction * 10, Color.yellow);  // ray needs a origin, and a dir

if(Physics.Raycast(ray, 10))
Debug.Log("Hit something");
}
}
}


在移动设备上使用Unity remote,用手触摸屏幕,将会看到一道黄线,若触摸到小球Sphere或地面Plane后,可看到在Console中有Hit something信息,即射线击中了物体。





下面我们想分别选中物体,即只picking小球而忽略地面plane,查看Layer文档:

http://docs.unity3d.com/Documentation/Components/Layers.html

其中讲的很细致,关键就是要新建Tags和设定layerMask

 

打开Edit,选择Project Settings->Tags and Layers

在Layer 8输入Player,Layer 9输入Background



将Sphere 和 Plane的Layer 设为相应的层





修改GameController.cs,

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
void Update ()
{
if(Input.touchCount == 1)
{

Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch(0).position);

int playersLayerMask = 1 << 8;

RaycastHit hit;
if(Physics.Raycast(ray, out hit, Mathf.Infinity, playersLayerMask))
{
Debug.DrawLine(ray.origin, hit.point, Color.yellow);			// line needs two points
}
}
}
}

此时,只有当我们触摸到Sphere时才绘制黄色射线提示,由于设置了LayerMask射线将忽略Plane。



下面我们尝试当射线击中Spheres时绘制黄色提示线,先击中Sphere后击中Plane时绘制红色提示线。

修改GameController.cs,

using UnityEngine;
using System.Collections;

public class GameController : MonoBehaviour
{
void Update ()
{
if(Input.touchCount == 1)
{
Ray ray = Camera.main.ScreenPointToRay (Input.GetTouch(0).position);

int playersLayerMask = 1 << 8;
int backgroundLayerMask = 1 << 9;

RaycastHit hit;
if(Physics.Raycast(ray, out hit, Mathf.Infinity, playersLayerMask))
{
Debug.DrawLine(ray.origin, hit.point, Color.yellow);			// line needs two points

RaycastHit groundHit;
if(Physics.Raycast(ray, out groundHit, Mathf.Infinity, backgroundLayerMask))
{
Debug.DrawLine(ray.origin, groundHit.point, Color.red);

}

}
}
}
}

点击Play,效果如下,



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: