Реагировать на сенсорные события

Заставить объекты перемещаться в соответствии с заранее заданной программой, например вращающимся треугольником, полезно для привлечения некоторого внимания, но что, если вы хотите, чтобы пользователи взаимодействовали с вашей графикой OpenGL ES? Ключом к тому, чтобы сделать ваше приложение OpenGL ES интерактивным с помощью сенсорного управления, является расширение реализации GLSurfaceView для переопределения onTouchEvent() для прослушивания событий касания.

В этом уроке показано, как прослушивать события касания, чтобы пользователи могли вращать объект OpenGL ES.

Настройка сенсорного прослушивателя

Чтобы ваше приложение OpenGL ES реагировало на события касания, вы должны реализовать метод onTouchEvent() в своем классе GLSurfaceView . В приведенном ниже примере реализации показано, как прослушивать события MotionEvent.ACTION_MOVE и преобразовывать их в угол поворота фигуры.

Котлин

private const val TOUCH_SCALE_FACTOR: Float = 180.0f / 320f
...
private var previousX: Float = 0f
private var previousY: Float = 0f

override fun onTouchEvent(e: MotionEvent): Boolean {
    // MotionEvent reports input details from the touch screen
    // and other input controls. In this case, you are only
    // interested in events where the touch position changed.

    val x: Float = e.x
    val y: Float = e.y

    when (e.action) {
        MotionEvent.ACTION_MOVE -> {

            var dx: Float = x - previousX
            var dy: Float = y - previousY

            // reverse direction of rotation above the mid-line
            if (y > height / 2) {
                dx *= -1
            }

            // reverse direction of rotation to left of the mid-line
            if (x < width / 2) {
                dy *= -1
            }

            renderer.angle += (dx + dy) * TOUCH_SCALE_FACTOR
            requestRender()
        }
    }

    previousX = x
    previousY = y
    return true
}

Ява

private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
private float previousX;
private float previousY;

@Override
public boolean onTouchEvent(MotionEvent e) {
    // MotionEvent reports input details from the touch screen
    // and other input controls. In this case, you are only
    // interested in events where the touch position changed.

    float x = e.getX();
    float y = e.getY();

    switch (e.getAction()) {
        case MotionEvent.ACTION_MOVE:

            float dx = x - previousX;
            float dy = y - previousY;

            // reverse direction of rotation above the mid-line
            if (y > getHeight() / 2) {
              dx = dx * -1 ;
            }

            // reverse direction of rotation to left of the mid-line
            if (x < getWidth() / 2) {
              dy = dy * -1 ;
            }

            renderer.setAngle(
                    renderer.getAngle() +
                    ((dx + dy) * TOUCH_SCALE_FACTOR));
            requestRender();
    }

    previousX = x;
    previousY = y;
    return true;
}

Обратите внимание, что после расчета угла поворота этот метод вызывает requestRender() чтобы сообщить средству визуализации, что пришло время визуализировать кадр. Этот подход является наиболее эффективным в данном примере, поскольку кадр не нужно перерисовывать, если не произойдет изменение поворота. Однако это не оказывает никакого влияния на эффективность, если вы также не запрашиваете, чтобы средство рендеринга перерисовывалось только при изменении данных с помощью метода setRenderMode() , поэтому убедитесь, что эта строка не закомментирована в средстве рендеринга:

Котлин

class MyGlSurfaceView(context: Context) : GLSurfaceView(context) {

    init {
        // Render the view only when there is a change in the drawing data
        renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    }
}

Ява

public MyGLSurfaceView(Context context) {
    ...
    // Render the view only when there is a change in the drawing data
    setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}

Выставляем угол поворота

В приведенном выше примере кода требуется, чтобы вы предоставили угол поворота через средство визуализации, добавив открытый член. Поскольку код средства рендеринга выполняется в потоке, отдельном от основного потока пользовательского интерфейса вашего приложения, вы должны объявить эту общедоступную переменную как volatile . Вот код для объявления переменной и предоставления пары геттер-сеттер:

Котлин

class MyGLRenderer4 : GLSurfaceView.Renderer {

    @Volatile
    var angle: Float = 0f
}

Ява

public class MyGLRenderer implements GLSurfaceView.Renderer {
    ...

    public volatile float mAngle;

    public float getAngle() {
        return mAngle;
    }

    public void setAngle(float angle) {
        mAngle = angle;
    }
}

Применить вращение

Чтобы применить поворот, генерируемый сенсорным вводом, закомментируйте код, который генерирует угол, и добавьте переменную, содержащую угол, созданный сенсорным вводом:

Котлин

override fun onDrawFrame(gl: GL10) {
    ...
    val scratch = FloatArray(16)

    // Create a rotation for the triangle
    // long time = SystemClock.uptimeMillis() % 4000L;
    // float angle = 0.090f * ((int) time);
    Matrix.setRotateM(rotationMatrix, 0, angle, 0f, 0f, -1.0f)

    // Combine the rotation matrix with the projection and camera view
    // Note that the mvpMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    Matrix.multiplyMM(scratch, 0, mvpMatrix, 0, rotationMatrix, 0)

    // Draw triangle
    triangle.draw(scratch)
}

Ява

public void onDrawFrame(GL10 gl) {
    ...
    float[] scratch = new float[16];

    // Create a rotation for the triangle
    // long time = SystemClock.uptimeMillis() % 4000L;
    // float angle = 0.090f * ((int) time);
    Matrix.setRotateM(rotationMatrix, 0, mAngle, 0, 0, -1.0f);

    // Combine the rotation matrix with the projection and camera view
    // Note that the vPMatrix factor *must be first* in order
    // for the matrix multiplication product to be correct.
    Matrix.multiplyMM(scratch, 0, vPMatrix, 0, rotationMatrix, 0);

    // Draw triangle
    mTriangle.draw(scratch);
}

Выполнив описанные выше действия, запустите программу и проведите пальцем по экрану, чтобы повернуть треугольник:

Рис. 1. Треугольник вращается с помощью сенсорного ввода (кругом показано местоположение касания).