2016-05-16 32 views
0

我一直在試圖讓我的遊戲能夠處理OpenGl中的用戶交互。我有一個圈子,我希望能夠檢測到用戶的新聞。我知道我需要使用Ray採摘,我跟着這本書OpenGl 2 for Android,並按照教程在3D空間中創建一個空氣曲棍球遊戲,並檢測用戶點擊事件。Ray爲用戶選擇Open G1 2D中的觸摸對象Android

現在我想用我的知識從2D遊戲開始,但是按照教程和應用我當前的知識並沒有幫助我實現能夠檢測到用戶單擊我的對象。

我假設我做錯了什麼,併爲3D和2D做了一點點區別。

我有一個幾何類,如下所示:

public class Geometry { 
    public static class Point { 
     public final float x, y, z; 

     public Point(float x, float y, float z) { 
      this.x = x; 
      this.y = y; 
      this.z = z; 
     } 

     public Point translateY(float distance) { 
      return new Point(x, y + distance, z); 
     } 

     public Point translate(Vector vector) { 
      return new Point(
       x + vector.x, 
       y + vector.y, 
       z + vector.z); 
     } 
    } 

    public static class Vector { 
     public final float x, y, z; 

     public Vector(float x, float y, float z) { 
      this.x = x; 
      this.y = y; 
      this.z = z; 
     } 

     public float length() { 
      return (float)Math.sqrt(
       x * x 
       + y * y 
       + z * z); 
     } 

     public Vector crossProduct(Vector other) { 
      return new Vector(
       (y * other.z) - (z * other.y), 
       (z * other.x) - (x * other.z), 
       (x * other.y) - (y * other.x)); 
     } 

     public float dotProduct(Vector other) { 
      return x * other.x 
       + y * other.y 
       + z * other.z; 
     } 

     public Vector scale(float f) { 
      return new Vector(
       x * f, 
       y * f, 
       z * f); 
     } 
    } 

    public static class Ray { 
     public final Point point; 
     public final Vector vector; 

     public Ray(Point point, Vector vector) { 
      this.point = point; 
      this.vector = vector; 
     } 
    } 

    public static class Circle { 
     public final Point center; 
     public final float radius; 

     public Circle(Point center, float radius) { 
      this.center = center; 
      this.radius = radius; 
     } 

     public Circle scale(float scale) { 
      return new Circle(center, radius * scale); 
     } 
    } 

    public static class Cylinder { 
     public final Point center; 
     public final float radius; 
     public final float height; 

     public Cylinder(Point center, float radius, float height) { 
      this.center = center; 
      this.radius = radius; 
      this.height = height; 
     } 
    } 

    public static class Sphere { 
     public final Point center; 
     public final float radius; 

     public Sphere(Point center, float radius) { 
      this.center = center; 
      this.radius = radius; 
     } 
    } 

    public static class Plane { 
     public final Point point; 
     public final Vector normal; 

     public Plane(Point point, Vector normal) { 
      this.point = point; 
      this.normal = normal; 
     } 
    } 

    public static Vector vectorBetween(Point from, Point to) { 
     return new Vector(
      to.x - from.x, 
      to.y - from.y, 
      to.z - from.z); 
    } 

    public static boolean intersects(Sphere sphere, Ray ray) { 
     return distanceBetween(sphere.center, ray) < sphere.radius; 
    } 

    public static float distanceBetween(Point point, Ray ray) { 
     Vector p1ToPoint = vectorBetween(ray.point, point); 
     Vector p2ToPoint = vectorBetween(ray.point.translate(ray.vector), point); 

     float areaOfTriangleTimesTwo = p1ToPoint.crossProduct(p2ToPoint).length(); 
     float lengthOfBase = ray.vector.length(); 

     float distanceFromPointToRay = areaOfTriangleTimesTwo/lengthOfBase; 
     return distanceFromPointToRay; 
    } 

    public static Point intersectionPoint(Ray ray, Plane plane) { 
     Vector rayToPlaneVector = vectorBetween(ray.point, plane.point); 

     float scaleFactor = rayToPlaneVector.dotProduct(plane.normal) 
      /ray.vector.dotProduct(plane.normal); 

     Point intersectionPoint = ray.point.translate(ray.vector.scale(scaleFactor)); 
     return intersectionPoint; 
    } 
} 

我有一個表面查看的onTouchEvent重寫如下:

if(event.getAction() == MotionEvent.ACTION_DOWN){ 

    if (event != null) { 

     final float normalizedX = 
         (event.getX()/(float) getWidth()) * 2 - 1; 
     final float normalizedY = 
         -((event.getY()/(float) getHeight()) * 2 - 1); 

     if (event.getAction() == MotionEvent.ACTION_DOWN) { 
      queueEvent(new Runnable() { 
       @Override 
        public void run() { 
         mRenderer.handleTouchPress(
          normalizedX, normalizedY); 
         } 
        }); 

       } 

       return true; 
      } else { 
       return false; 
     } 
} 

最後我有一個渲染如下:

public class ImpulseRushRenderer implements Renderer { 
    private Geometry.Point circlePosition; 
    private boolean circlePressed= false; 
    private final Context context; 
    public Circle circle; 
    private float mMatrix[] = new float[16]; 
    private float[] mTempMatrix = new float[16]; 
    private final float[] mProjectionMatrix = new float[16]; 
    private final float[] mViewMatrix = new float[16]; 
    private final float[] mRotationMatrix = new float[16]; 
    private final float[] mMVPMatrix = new float[16]; 
    private final float[] mProjMatrix = new float[16]; 
    private final float[] mVMatrix = new float[16]; 
    private final float[] mModelMatrix = new float[16]; 
    private final float[] tempMatrix = new float[16]; 
    private final float[] invertedViewProjectionMatrix = new float[16]; 

    public ImpulseRushRenderer(Context context) { 
     this.context = context; 
    } 

    LayoutInflater mInflater; 

    @Override 
    public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { 
     glClearColor(0.1725490196078431f, 0.2431372549019608f, 0.3137254901960784f, 1.0f); 

     circle= new Circle(); 
     mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    @Override 
    public void onSurfaceChanged(GL10 glUnused, int width, int height) { 
     glViewport(0, 0, width, height); 
    } 

    @Override 
    public void onDrawFrame(GL10 glUnused) { 

     glClear(GL_COLOR_BUFFER_BIT); 

     Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 

     Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); 

     mTempMatrix = mModelMatrix.clone(); 
     Matrix.multiplyMM(mModelMatrix, 0, mTempMatrix, 0, mRotationMatrix, 0); 

     mTempMatrix = mMVPMatrix.clone(); 
     Matrix.multiplyMM(mMVPMatrix, 0, mTempMatrix, 0, mModelMatrix, 0); 
     Matrix.orthoM(mMatrix, 0, -1, 1, -1, 1, -1, 1); 

     Matrix.orthoM(mMatrix, 0, -1, 1, -1, 1, -1, 1); 

     Matrix.setIdentityM(mModelMatrix, 0); 

     circle.draw(mModelMatrix); 

     circlePosition= new Geometry.Point(0f, 100/2f, 0.4f); 
    } 

    private Ray convertNormalized2DPointToRay(
     float normalizedX, float normalizedY) { 

     final float[] nearPointNdc = {normalizedX, normalizedY, -1, 1}; 
     final float[] farPointNdc = {normalizedX, normalizedY, 1, 1}; 

     final float[] nearPointWorld = new float[4]; 
     final float[] farPointWorld = new float[4]; 

     multiplyMV(
      nearPointWorld, 0, invertedViewProjectionMatrix, 0, nearPointNdc, 0); 
     multiplyMV(
      farPointWorld, 0, invertedViewProjectionMatrix, 0, farPointNdc, 0); 

     divideByW(nearPointWorld); 
     divideByW(farPointWorld); 

     Geometry.Point nearPointRay = 
      new Geometry.Point(nearPointWorld[0], nearPointWorld[1], nearPointWorld[2]); 

     Geometry.Point farPointRay = 
      new Geometry.Point(farPointWorld[0], farPointWorld[1], farPointWorld[2]); 

     return new Ray(nearPointRay, 
      Geometry.vectorBetween(nearPointRay, farPointRay)); 
    } 

    public static int loadShader(int type, String shaderCode) { 

     int shader = GLES20.glCreateShader(type); 

     GLES20.glShaderSource(shader, shaderCode); 
     GLES20.glCompileShader(shader); 

     return shader; 
    } 

    public void handleTouchPress(float normalizedX, float normalizedY) { 
     Geometry.Ray ray = convertNormalized2DPointToRay(normalizedX, normalizedY); 

     Geometry.Sphere circleBoundingSphere = new Geometry.Sphere(new Geometry.Point(
      circlePosition.x, 
      circlePosition.y, 
      circlePosition.z), 
      100/2f); 

     circlePressed= Geometry.intersects(circleBoundingSphere , ray); 

     if (circlePressed) { 
      Log.i("circlePressed", "circle was pressed"); 
     } 
    } 


    private void divideByW(float[] vector) { 
     vector[0] /= vector[3]; 
     vector[1] /= vector[3]; 
     vector[2] /= vector[3]; 
    } 

} 

到我目前的知識和從這本書中的教程我鏈接這應該會糾正年。

回答

1

在「private Ray convertNormalized2DPointToRay」中, 您可以嘗試將1放到nearPointWorld [3]和farPointWorld [3](1到W)中,否則會導致乘法運算符無法正常工作。

EDIT

方法1:距離函數

假設你已經有觸摸位置,所述物體的在相同的空間和其半徑的中心。

int touchX, touchY; 
int centerObjX, centerObjY; 
int rayon; 

int distX = centerObjX - touchX; // the sens don't matter 
int distY = centerObjY - touchY; 
if (distX*distX + distY*distY<rayon*rayon) // squared distance 
{ 
    // touch in the circle 
} 

注意:你可以通過乘法中心相機舉動模型視圖中心。 這種方法很好,因爲它是數學的,不需要使用GPU。 你可以有一些3D距離函數,以及如何使用here

了Methode 2:顏色選擇(算法)

  1. 清除背景爲黑色(RGB =(0,0,0))
  2. 設置顏色對象爲RGB =(1,0,0)(它現在的對象的顏色ID)使用glReadPixels()(例如)
  3. 拾取顏色
  4. 然後比較所檢索的顏色值以瞭解如果它是你的ob或不是。

當場景中有很多物體或物體很複雜時,此方法非常棒。

您的方法也很棒,但結果只是您點擊的位置(或方向相機 - >您點擊的位置)。那麼,3D使用會更好。

+0

您的意思是這樣的? ( 1,0,invertedViewProjectionMatrix,0,nearPointNdc,0); – L1ghtk3ira

+0

此外,爲什麼會使它適用於2D。下班後我會盡快回家。 – L1ghtk3ira

+0

哦,我沒有看到它是2D,道歉。 我曾經做過3D精確度的非投影方法。您可以選擇另一種方法來獲取像拾色這樣的對象(使用獨特的顏色爲您的對象着色以獲得「對象ID」)或者只需鼠標計算(對於圓圈,中心與鼠標之間的距離 GLCraft