Skip to content

简介

来自摄像机的射线最常见的用途是将 射线投射(Raycast)到场景中,此时射线为从近裁剪面沿着射线方向发射的不可见激光束,直至命中场景中的碰撞体并获取被击中的 游戏对象(Game Object)和击中点位置等信息。

摄像机射线作用

这是一种基于 游戏对象(Game Object)在屏幕上的图像来定位该游戏对象的方法。通常在游戏中点击场景的对象所进行的游戏交互方式就是使用摄像机射线实现。

【Camera_Rays001.mp4】

例如游戏中被点中的树木变成红色,就是使用摄像机射线检测被点中的树木游戏对象。

摄像机射线原理

一般在透视模式下的摄像机,能观察到的内容都在视椎体内,而且根据视椎体的锥形特性,摄像机图像中的任何一点实际上都对应于世界空间中的一条线,在图像中只能看到这条线上的一个点。这条线上该位置背后的一切都会被遮挡。

假设将一根细长的圆柱体正对着摄像机:

  • 如果杆垂直于摄像机镜头保持在图片中心位置,那么只有其一端可在图片上显示为圆圈。所有其他部分都会被遮挡。

【Camera_Rays002.png】

  • 此时如果将杆向上移动,圆柱体下侧将开始变得可见。

【Camera_Rays003.png】

  • 但可通过向上倾斜杆再次将其隐藏。如果继续上倾斜,直至圆柱体与视椎体的斜面平行,圆柱体会在屏幕中显示为一个圆形。

【Camera_Rays004.png】

因此可以得知,摄像机图像中的任何一点都对应于世界空间中的一条从近剪裁面出发的射线。

摄像机射线检测方法

来自摄像机的射线为从沿着射线方向发射,直至命中场景中的碰撞体。随后会返回用于储存被命中 游戏对象(Game Object)变换(Transform)和命中点位置等信息的 RaycastHit 类型变量。

摄像机检测前,请确认以下信息:

  • 被检测的游戏对象需要具有 碰撞体(Controller)才能被射线检测,具体添加碰撞体的方法请参阅 碰撞器 章节。
  • 确定被检测的游戏对象的 层次(Layer),具体查看和设置方法请参阅 游戏对象层次 章节。

代码示例如下:

typescript
class TouchEvent extends Component
{
    //定义公共变量保存摄像机的 Camera 组件;
    public CameraCom:Camera;
    //定义公共变量保存摄像机的最大检测距离;
    public RayMaxDistance:number = 100;

    OnStart(): void {
    }

    /*
    每帧检测鼠标点击屏幕和屏幕触碰事件;
    屏幕被触碰或鼠标点击时,将鼠标或触碰的位置同步至私有事件 ClickScreen()中;
        * 当多根手指触碰屏幕时:
            - 获取第一根手指点击屏幕的坐标;
            - 将该坐标传入私有事件 ClickScreen()中;
            - 同时获取摄像机射线的最大检测距离传入私有事件 ClickScreen()中;
        * 或者当鼠标左键被点击时:
            - 获取此时鼠标在屏幕的坐标;
            - 将该坐标传入私有事件 ClickScreen()中;
            - 同时获取摄像机射线的最大检测距离传入私有事件 ClickScreen()中;
    */
    OnUpdate(): void {
        if(Input.touchCount > 0){
            const touch:Touch = Input.GetTouch(0);    
            const ScrPos:Vector2 = touch.position;
            this.ClickScreen(ScrPos,this.RayMaxDistance);
        }
        else if(Input.GetMouseButtonDown(MouseButton.LeftButton)){
            const ScrPos:Vector2 = Input.mousePosition;
            this.ClickScreen(ScrPos,this.RayMaxDistance);
        }
    }

    /*
    私有事件,用于封装屏幕被点击或屏幕触碰的逻辑;
    */
    private ClickScreen(ScrPos:Vector2,rayMaxDistance:number):void{
        //定义射线,射线的发射点位于近剪裁面上;
        let theRay:Ray = this.CameraCom.ScreenPointToRay(
            new Vector3(
                ScrPos.x,
                ScrPos.y,
                0
            )
        );
        
        //定义保存射线检测结果的变量;
            //- Physics.Raycast(ray, maxDistance, mask, cast_trigger) 是进行射线检测的代码;
            //- 其中传入参数 mask 的值为二进制数值,-1 表示所有位数都为 1 即所有层次的游戏对象都可被射线检测;
        let isHit:RaycastHit= Physics.Raycast(theRay,rayMaxDistance,-1);

        //判断射线是否检测到物体;
        if(isHit){
            //编辑射线击中物体后的事件,其中 isHit.transform 代表射线击中物体的游戏对象变换;
        }
    }

}

值得注意的是,如果您采用 UI 的触碰事件发射摄像机射线,UI 的 Y 轴坐标是顶部为 0,底部为屏幕纵向像素值,而屏幕的 Y 轴坐标是底部为 0,顶部为屏幕纵向像素值,因此射线发射的位置需要手动换算。