如何检测android中某个Renderer区域内的触摸事件?

问题描述:

在android我已经采取了旋转球体的例子given here。它创建了一个显示旋转球体(地球)的简单应用程序。如何检测android中某个Renderer区域内的触摸事件?

现在,我想要做的事情,如果在手机显示屏上按下旋转的球体。我不想对旋转球体的以外的触摸事件作出反应。以下是我迄今为止尝试,通过使用GLSurfaceView的衍生版本:

public class MyGLSurfaceView extends GLSurfaceView { 



    MyGLSurfaceView(Context context) { 
     super(context); 
    } 

    public boolean onTouchEvent(MotionEvent event) { 
     int x = (int)event.getX(); 
     int y = (int)event.getY(); 
     Log.d("onTouchEvent",String.format("keyCode: %d coords: %d %d", event.getActionMasked(), x, y)); 

     Renderer renderer = this.Renderer.getRenderer(); // ERROR 
     return super.onTouchEvent(event); 
    } 

} 

没有错误行此代码的工作,当我按下显示我得到的X/Y坐标。

然而,为了评估该事件是球,我的做法是让它具有Sphere对象,以确定是否X/Y坐标在范围内的渲染器。

但错误行不行,我得到一个错误说

Error:(26, 56) error: cannot find symbol method getRenderer() 

我怎样才能解决这个错误;或者如果事件是在Sphere -graphics-object中,是否有更自理的方法来自动找出?

也许这是一个好主意,更改构造函数并将渲染器实例传递给MyGLSurfaceView以及?附录:这个想法似乎不起作用。我得到一个构造函数的错误,我不明白...

我认为,如你所说,在构造函数中传递渲染器是个好主意。然后传递事件由游戏循环中的渲染器处理。 GLSurfaceView有一个方法来向渲染线程添加一个名为queueEvent的runnable。

然后在渲染器中,您拥有所有渲染对象,您可以使用光线投射来检查是否有对象被点击。

public SurfaceView(Context context, GLRenderer renderer) { 
    super(context); 

    setEGLContextClientVersion(2); 
    setRenderer(renderer); 

    setOnTouchListener(new OnTouchListener() { 
     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      if(event == null) 
       return false; 

      // set touch coordinates to be between -1 and 1 
      float normalizedX = ((event.getX()/v.getWidth())*2 - 1; 
      float normalizedY = -((event.getY()/v.getHeight())*2 - 1); 

      if (event.getAction() == MotionEvent.ACTION_UP){ 
       queueEvent(new Runnable() { 
        @Override 
        public void run() { 
         renderer.handleTouchUP(normalizedX, 
               normalizedY); 
        } 
       }); 
       return true;       
      } 
      return false; 
     } 
    }); 
} 
+0

感谢您的建议,但有一个小代码示例,我认为我完全失去了。 “射线投射”是什么意思? – Alex

+0

我添加了SurfaceView代码。 光线投射是一种检查是否通过使用视图矩阵创建从某个点(在此情况下从具有帐户的相机到发生触摸事件的位置)发出的光线来“击中”对象的方法。然后检查光线是否与任何物体相交。网上有很多在线教程。 – satm12

你的问题比检测触摸事件复杂得多。渲染器不仅具有球体对象,还包含整个OpenGL ES内容。因此,即使在检测到触摸事件后,您仍然需要将屏幕2D坐标转换为3D射线,并检查它是否与球体相交。这被称为射线铸造Here是光线投射算法的一个很好的解释。另外还有一个good video tutorial用于实现光线投射Java。

您也可以按照您和@ satm12的建议,在GLSurfaceView的构造函数中传递渲染器。

你想要做的是检查你的触摸是否在球体上。最直接的答案是制作穿过触摸位置的3D光线,看看光线是否与球体发生碰撞,即Ray-Sphere碰撞。要达到此目的,您可以使用gluUnProject()。请记住,您需要在近平面和远平面上使用此功能两次才能获得接近远向量的3D。这个矢量是用于检查碰撞的3D射线。

但在你的情况下,你有一个简单的旋转球体。中心是固定的,半径是已知的。因此,更简单的方法是找到适合球体的屏幕上的圆圈,并检查该圆圈内是否有触摸。

首先从您的问题中链接的代码示例中获取球体的中心。由于你做了一个转换为(0,0,-10)这是3D中心。 center3d(0.0f, 0.0f, -10.0f)。将它投影到屏幕上并获得一个屏幕中心。您可以使用gluProject()。可以说这导致center2d。现在在你的球体表面上再加一点,因为球体半径是2(从你的代码链接),一个点可能是point3d(2.0f, 0.0f, -10.0f)。现在将其投影到屏幕上,并假定您获得point2d。现在屏幕上球体的二维圆弧半径为length(point2D - center2D)

现在检查输入触摸是否在圆圈内。只需从圆的中心获取触摸位置的距离,并检查其2D半径。这对于你的情况来说是一个简单的方法,但如果你有更复杂的形状,光线投射就是要走的路。