پاسخ دادن به رویدادهای لمسی

حرکت دادن اشیا طبق برنامه‌ای ازپیش‌تنظیم‌شده، مثل مثلث چرخان، برای جلب توجه مفید است، اما اگر بخواهید کاربران با گرافیک 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);
}

وقتی مراحل شرح‌داده‌شده در بالا را تکمیل کردید، برنامه را اجرا کنید و انگشتتان را روی صفحه‌نمایش بکشید تا مثلث بچرخد:

شکل ۱. مثلثی که با ورودی لمسی چرخانده می‌شود (دایره مکان لمس را نشان می‌دهد).