LibGDX Tutorial 5: Xử lý đầu vào phần 2 - Xử lý chạm Đa Điểm Và các cử chỉ
Bài đăng này đã không được cập nhật trong 9 năm
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:
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 width, 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) +
"\n";
}
BitmapFont.TextBounds tb = font.getBounds(message);
float x = w/2 - tb.width/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ử chỉ chạm vào
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 width, 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.width/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
.
Sử dụng nhiều InputProcessors
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 width, 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.log("INFO", message);
return true;
}
@Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
message = "Pinch được thực hiện";
Gdx.app.log("INFO", message);
return true;
}
@Override
public boolean keyDown(int keycode) {
message = "Key Down";
Gdx.app.log("INFO", message);
return true;
}
@Override
public boolean keyUp(int keycode) {
message = "Key Up";
Gdx.app.log("INFO", message);
return true;
}
@Override
public boolean keyTyped(char character) {
message = "Key Typed";
Gdx.app.log("INFO", message);
return true;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
message = "Touch Down";
Gdx.app.log("INFO", message);
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
message = "Touch up";
Gdx.app.log("INFO", message);
return false;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
message = "Touch Dragged";
Gdx.app.log("INFO", message);
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
message = "Mouse moved";
Gdx.app.log("INFO", message);
return false;
}
@Override
public boolean scrolled(int amount) {
message = "Scrolled";
Gdx.app.log("INFO", message);
return false;
}
}
Điều chúng ta cần quan tâm nhất là đoạn code này:
InputMultiplexer inputMultiplexer = new InputMultiplexer();
inputMultiplexer.addProcessor(new GestureDetector(this));
inputMultiplexer.addProcessor(this);
Gdx.input.setInputProcessor(inputMultiplexer);
Về cơ bản, chúng ta tạo ra Multiplexer
, sau đó thêm cả InputProcessor
và GestureDetector
thông qua addProcessor()
, sau đó để sử dụng nó chúng ta sử dụng hàm setInputProcessor()
. Có hai điều cực kỳ quan trọng ở đây. Thứ nhất, theo thứ tự mà các proccessors
được thêm vào Multiplexer
dường như để xác định thứ tự cử chỉ được xử lý. Tiếp theo, và điều này rất quan trọng, trong xử lý sự kiến, nếu bạn return true
có nghĩa là sự kiện đã được xử lý. Trong khi sự kiện touchUp
hoặc touchDown
được thực hiện, và sự kiện pinch
thì không. Trong thực tế, sự kiện pinch
bao gồm các sự kiện khác, gồm có các sự kiện chạm đa điểm. Tuy nhiên nếu bạn return true
tại sự kiên touchDown
, sự kiện này sẽ không đi qua gesture detector
. Vì vậy nếu bạn đang hỗ trợ đa cảm ứng, hãy chắc chắn return false
từ các sự kiên nguyên tử hơn như touchDown
và touchUp
.
Source code
Bạn có có thể tham khảo source code của project này tại đây.
All rights reserved