LibGDX Tutorial 5: Xử lý đầu vào phần 2 - Xử lý chạm Đa Điểm Và các cử chỉ
Trong bài viết trước, chúng ta đã biết làm thế nào để xử lý thao tác chuột và bàn phím. Bây giờ chúng ta sẽ xem xét làm thế nào để có thể xử lý thao tác chạm. Để xem xét việc này, chúng ta cần một thiết bị có cảm ứng (chạm đa điểm bằng cách sử dụng chuột vô cùng khó khăn). Chúng ta sẽ bắt đầu với ...
Trong bài viết trước, chúng ta đã biết làm thế nào để xử lý thao tác chuột và bàn phím. Bây giờ chúng ta sẽ xem xét làm thế nào để có thể xử lý thao tác chạm. Để xem xét việc này, chúng ta cần một thiết bị có cảm ứng (chạm đa điểm bằng cách sử dụng chuột vô cùng khó khăn). Chúng ta sẽ bắt đầu với ví dụ đầu tiên về chạm đa điểm:
package com.handlinginputdemo.game; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import java.util.HashMap; import java.util.Map; public class HandlingInputDemo implements ApplicationListener, InputProcessor { private SpriteBatch batch; private BitmapFont font; private String message = "Bắt đầu chạm!"; private int w,h; class TouchInfo { public float touchX = 0; public float touchY = 0; public boolean touched = false; } private Map<Integer,TouchInfo> touches = new HashMap<Integer,TouchInfo>(); @Override public void create () { batch = new SpriteBatch(); font = new BitmapFont(Gdx.files.internal("arial-15.fnt"),false); font.setColor(Color.RED); w = Gdx.graphics.getWidth(); h = Gdx.graphics.getHeight(); Gdx.input.setInputProcessor(this); for(int i = 0; i < 5; i++){ touches.put(i, new TouchInfo()); } } @Override public void resize(int awidth, int height) { } @Override public void dispose() { batch.dispose(); font.dispose(); } @Override public void render () { Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); batch.begin(); message = ""; for(int i = 0; i < 5; i++){ if(touches.get(i).touched) message += "Ngón tay[" + Integer.toString(i) + "] chạm vào tọa độ:" + Float.toString(touches.get(i).touchX) + "," + Float.toString(touches.get(i).touchY) + " "; } BitmapFont.TextBounds tb = font.getBounds(message); float x = w/2 - tb.awidth/2; float y = h/2 + tb.height/2; font.drawMultiLine(batch, message, x, y); batch.end(); } @Override public void pause() { } @Override public void resume() { } @Override public boolean keyDown(int keycode) { return false; } @Override public boolean keyUp(int keycode) { return false; } @Override public boolean keyTyped(char character) { return false; } @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { if(pointer < 5){ touches.get(pointer).touchX = screenX; touches.get(pointer).touchY = screenY; touches.get(pointer).touched = true; } return true; } @Override public boolean touchUp(int screenX, int screenY, int pointer, int button) { if(pointer < 5){ touches.get(pointer).touchX = 0; touches.get(pointer).touchY = 0; touches.get(pointer).touched = false; } return true; } @Override public boolean touchDragged(int screenX, int screenY, int pointer) { return false; } @Override public boolean mouseMoved(int screenX, int screenY) { return false; } @Override public boolean scrolled(int amount) { return false; } }
Đối với mỗi ngón tay chạm vào, nó sẽ hiển thị tọa độ, các bạn có thể nhìn thấy nó có thể lên tới 5 ngón tay cùng lúc.
Ở ví dụ trên, chúng ta đã táo lớp TouchInfo để lưu trữ các tọa độ chạm. Sau đó chúng ta tạo ra một HashMap touches với key là một số nguyên và lớp TouchInfo là value. Vấn đề then chốt là index của ngón tay được chạm vào. Logic xử lý được viết trong sự kiện touchDown và touchUp. Trong touchDown, chúng ta cập nhật touches map index những điểm chạm hiện tại. Khi tay rời khỏi màn hình, sự kiện touchUp đơn giản là xóa bỏ tọa độ được lưu trữ. Cuối cùng render() sẽ lặp touches map và hiển thị thông tin của tất cả những vị trí được chạm.
Ở ví dụ, chúng ta xử lý tối đa 5 ngón chạm cùng 1 lúc, tuy nhiên LibGDX có thể xử lý lên tới 20 chạm mặc dù không có thiết bị nào làm được điều này.
Có một số cử chỉ đã trở nên phổ biến trong thế giới di động. Như là pinch để phóng to, hoặc flick/fling và long press từ lâu đã trở thành tiêu chuẩn. May mắn thay LibGDX hỗ trợ tất cả những cử chỉ này. Chúng ta hãy bắt đầu luôn với ví dụ đơn giản:
package com.handlinginputdemo.game; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.math.Vector2; import java.util.HashMap; import java.util.Map; public class HandlingInputDemo implements ApplicationListener, GestureDetector.GestureListener { private SpriteBatch batch; private BitmapFont font; private String message = "Không có cử chỉ nào!"; private int w,h; @Override public void create () { batch = new SpriteBatch(); font = new BitmapFont(Gdx.files.internal("arial-15.fnt"),false); font.setColor(Color.RED); w = Gdx.graphics.getWidth(); h = Gdx.graphics.getHeight(); Gdx.input.setInputProcessor(new GestureDetector(this)); } @Override public void resize(int awidth, int height) { } @Override public void render() { Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); batch.begin(); BitmapFont.TextBounds tb = font.getBounds(message); float x = w/2 - tb.awidth/2; float y = h/2 + tb.height/2; font.drawMultiLine(batch, message, x, y); batch.end(); } @Override public void pause() { } @Override public void resume() { } @Override public void dispose() { } @Override public boolean touchDown(float x, float y, int pointer, int button) { return false; } @Override public boolean tap(float x, float y, int count, int button) { message = "Tap được thực hiện, ngón tay " + Integer.toString(button); return true; } @Override public boolean longPress(float x, float y) { message = "Long press được thực hiện"; return true; } @Override public boolean fling(float velocityX, float velocityY, int button) { message = "Fling được thực hiện, velocity:" + Float.toString(velocityX) + "," + Float.toString(velocityY); return true; } @Override public boolean pan(float x, float y, float deltaX, float deltaY) { message = "Pan được thực hiện, delta:" + Float.toString(deltaX) + "," + Float.toString(deltaY); return true; } @Override public boolean panStop(float x, float y, int pointer, int button) { return false; } @Override public boolean zoom(float initialDistance, float distance) { message = "Zoom được thực hiện, initial Distance:" + Float.toString(initialDistance) + " Distance: " + Float.toString(distance); return true; } @Override public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) { message = "Pinch được thực hiện"; return true; } }
Nếu bạn chạy chương trình, các cử chỉ chạm sẽ được hiển thị. Các cử chỉ hỗ trợ bao gồm tab, fling (flick), pinch (hai ngón tay di chuyển lại gần nhau), zoom (hai ngón tay di chuyển ra ngoài), pan (một ngón tay giữ và trượt) và long press (bấm và giữ).
Cũng giống như việc chúng ta cài đặt interface InputProcessor để xử lý thao tác chạm, chuột và bán phím, ở ví dụ này chúng ta sử dụng GestureListener để chấp nhận các cử chỉ. Trong hàm create() bạn tạo ra một GestureDetector sử dụng GestureListener của bạn và một lần nữa bạn đăng ký nó sử dụng Gdx.input.setInputProcessor (). Mỗi cử chỉ khác nhau sẽ được GestureListener nhận diện. Trong mỗi cử chỉ này, chúng ta lại hiển thị ra màn hình thông qua hàm render.
Nếu bạn muốn xử lý cử chỉ và xử lý cả bàn phím thì sẽ phải làm gì nhỉ? May mắn là câu trả lời khá đơn giản, thay vì thông qua setInputProcessor một InputProcessor hay GestureDetector, bạn thay thế bằng một InputMultiplexer. Giống như thế này:
package com.handlinginputdemo.game; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputMultiplexer; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.math.Vector2; import java.util.HashMap; import java.util.Map; public class HandlingInputDemo implements ApplicationListener, GestureDetector.GestureListener, InputProcessor { private SpriteBatch batch; private BitmapFont font; private String message = "Không có cử chỉ nào!"; private int w,h; @Override public void create() { batch = new SpriteBatch(); font = new BitmapFont(Gdx.files.internal("arial-15.fnt"),false); font.setColor(Color.RED); w = Gdx.graphics.getWidth(); h = Gdx.graphics.getHeight(); InputMultiplexer inputMultiplexer = new InputMultiplexer(); inputMultiplexer.addProcessor(new GestureDetector(this)); inputMultiplexer.addProcessor(this); Gdx.input.setInputProcessor(inputMultiplexer); } @Override public void resize(int awidth, int height) { } @Override public void render() { } @Override public void pause() { } @Override public void resume() { } @Override public void dispose() { } @Override public boolean touchDown(float x, float y, int pointer, int button) { message = "Touch down!"; Gdx.app.log("INFO", message); return true; } @Override public boolean tap(float x, float y, int count, int button) { message = "Tap được thực hiện, ngón tay " + Integer.toString(button); Gdx.app.log("INFO", message); return true; } @Override public boolean longPress(float x, float y) { message = "Long press được thực hiện"; Gdx.app.log("INFO", message); return true; } @Override public boolean fling(float velocityX, float velocityY, int button) { message = "Fling được thực hiện, velocity:" + Float.toString(velocityX) + "," + Float.toString(velocityY); Gdx.app.log("INFO", message); return true; } @Override public boolean pan(float x, float y, float deltaX, float deltaY) { message = "Pan được thực hiện, delta:" + Float.toString(deltaX) + "," + Float.toString(deltaY); Gdx.app.log("INFO", message); return true; } @Override public boolean panStop(float x, float y, int pointer, int button) { return false; } @Override public boolean zoom(float initialDistance, float distance) { message = "Zoom được thực hiện, initial Distance:" + Float.toString(initialDistance) + " Distance: " + Float.toString(distance); Gdx.app.