LibGDX Tutorial 6: Điều khiển chuyển động

Điều khiển chuyển động

Trong bài viết trước, chúng ta đã biết làm thế nào để xử lý chạm đa điểm và các cử chỉ trên màn hình càm ứng. Ngày nay, hầu hết các thiết bị di động có khả năng phát hiện chuyển động rất chính xác, mà LibGDX hoàn toàn hỗ trợ. Trong ví dụ này, chúng ta sẽ xem xét làm thế nào để xử lý các chuyển động, và kiểm tra xem một thiết bị hỗ trợ những chuyển động nào.

Bài viết này xoay quanh một ví dụ duy nhất, nhưng có một số bước cầu hình bạn cần phải nắm được.

Trước hết, để ứng dụng LibGDX có thể sử dụng được chức năng định hướng và gia tốc, bạn cần phải cấu hình tại AndroidLauncher class trong Android project. Trong android project, bạn sửa file AndroidLauncher.java như sau:

package com.motioncontrols.game.android;

import android.os.Bundle;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.motioncontrols.game.MotionControlsDemo;

public class AndroidLauncher extends AndroidApplication {
	@Override
	protected void onCreate (Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
        config.useAccelerometer = true;
        config.useCompass = true;
		initialize(new MotionControlsDemo(), config);
	}
}

Đoạn code này:

config.useAccelerometer = true;
config.useCompass = true;

cho phép ứng dụng sử dụng cả định hướng và gia tốc.

Tiếp theo chúng ta chỉnh sửa một chút trong file AndroidManifest.xml. Có 2 điểm chúng ta cần thay đổi. Đầu tiên, chúng ta cần một thiết bị hỗ trợ xoay, khi người dùng xoay thiết bị, ứng dụng sẽ xoay thay theo. Điều này được thực hiện bằng cách xét thuộc tính android:screenOrientation thành fullsensor. Tiếp theo chúng ta cần thêm permission android.permission.VIBRATE. Nếu bạn không thêm permission này, khi chúng ta yêu cầu thiết bị rung sẽ bị crash.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.motioncontrols.game.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="22" />
    <uses-permission android:name="android.permission.VIBRATE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/GdxTheme" >
        <activity
            android:name="com.motioncontrols.game.android.AndroidLauncher"
            android:label="@string/app_name"
            android:screenOrientation="fullSensor"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Như bạn thấy, tôi đã thêm trong file AndroidManifest.xml:

<uses-permission android:name="android.permission.VIBRATE"/>

android:screenOrientation="fullSensor"

Bạn cần phải chú ý khi thêm các permissions, vì chúng sẽ được hiển thị khi người dùng cài đặt ứng dụng của bạn. Quá nhiều permissions sẽ khiến người dùng lo lắng và không dám sử dụng ứng dụng. Tất nhiên, nếu bạn cần phải làm điều gì đó mà cần thiết permissions không quá nhiều bạn có thể thực hiện! Với những giá trị screenOrientation, cho biết ứng dụng của bạn hiển thị theo chiều nào. Có một số tùy chọn, Landscape and Portrait là phổ biến nhất. fullSensor về cơ bản có nghĩa là hỗ trợ tất cả các chiều. Điều này có nghĩa là bạn có thể xoay thiết bị 360 và nó sẽ được xoay chuyển cho phù hợp. Mặt khác, nếu bạn chọn user, bạn không thể xoay điện thoại 180 độ, có nghĩa là bạn không thể sử dụng nó khi xoay ngược.

Chúng ta đã cấu hình xong toàn bộ, bây giờ hãy nhìn vào ví dụ sau đây:

package com.motioncontrols.game;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class MotionControlsDemo extends ApplicationAdapter {
    private SpriteBatch spriteBatch;
    private BitmapFont bitmapFont;
    private String message = "Do something already!";
    private float highestY = 0.0f;

	@Override
	public void create () {
        spriteBatch = new SpriteBatch();
        bitmapFont = new BitmapFont(Gdx.files.internal("arial-15.fnt"),false);
        bitmapFont.setColor(Color.RED);
	}

    @Override

    public void dispose() {
        spriteBatch.dispose();
        bitmapFont.dispose();
    }

	@Override
	public void render () {
        int w = Gdx.graphics.getWidth();
        int h = Gdx.graphics.getHeight();
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        spriteBatch.begin();
        int deviceAngle = Gdx.input.getRotation();
        Input.Orientation orientation = Gdx.input.getNativeOrientation();
        float accelerometerY = Gdx.input.getAccelerometerY();
        if(accelerometerY > highestY) {
            highestY = accelerometerY;
        }
        message = "Rotated to:" + Integer.toString(deviceAngle) + " degrees\n";
        message += "Orientation: ";
        switch(orientation) {
            case Landscape:
                message += " landscape.\n";
                break;
            case Portrait:
                message += " portrait. \n";
                break;
            default:
                message += " complete crap!\n";
                break;
        }
        message += "Resolution: " + Integer.toString(w) + "," + Integer.toString(h) + "\n";
        message += "Y axis accelerometer: " + Float.toString(accelerometerY) + " \n";
        message += "Highest Y value: " + Float.toString(highestY) + " \n";
        if(Gdx.input.isPeripheralAvailable(Input.Peripheral.Vibrator)){
            if(accelerometerY > 7){
                Gdx.input.vibrate(100);
            }
        }
        if(Gdx.input.isPeripheralAvailable(Input.Peripheral.Compass)){
            message += "Azimuth:" + Float.toString(Gdx.input.getAzimuth()) + "\n";
            message += "Pitch:" + Float.toString(Gdx.input.getPitch()) + "\n";
            message += "Roll:" + Float.toString(Gdx.input.getRoll()) + "\n";
        }
        else{
            message += "No compass available\n";
        }

        bitmapFont.drawMultiLine(spriteBatch, message, 0, h);
        spriteBatch.end();
    }

    @Override

    public void resize(int width, int height) {
        spriteBatch.dispose();
        spriteBatch = new SpriteBatch();
        String resolution = Integer.toString(width) + "," + Integer.toString(height);
        Gdx.app.log("LOG", "Resolution change " + resolution);
    }
}

Khi bạn chạy ứng dụng, bạn sẽ thấy:

Screenshot_2015-09-26-01-34-31.png

Khi bạn di chuyển thiết bị, các giá trị khác nhau sẽ cập nhật. Nếu bạn dựng đứng điện thoại khoảng quá 30 độ, nó sẽ rung động. Tất nhiên, điều này giả định rằng thiết bị của bạn hỗ trợ tất cả các cảm biến này được!

Phần lớn các logic là trong phương thức render (). Đầu tiên chúng tôi nhận được góc xoay của thiết bị. Giá trị này là tính từ 0 độ khi đặt thiết bị đứng thẳng hướng về phía mình. Một trong những điều quan trọng để nhận ra giá trị này sẽ luôn luôn lớn hơn, kể cả khi bạn để ở chế độ portrait hay landscape. Đây là một cái gì đó LibGDX làm cho mọi việc dễ dàng hơn, nhưng lại khác so với chuẩn của Android.

Tiếp theo chúng ta lấy được hướng của các thiết bị. Hướng có thể là landscape hoặc portrait (hoặc màn hình ngang). Tiếp theo chúng ta kiểm tra giá trị của gia tốc dọc theo trục Y sử dụng getAccelerometerY (). Bạn cũng có thể kiểm tra gia tốc chuyển động với trục X và Z bằng cách sử dụng getAcceleromterX () và getAcceleromterZ (). Một lần nữa, LibGDX chuẩn hóa các hướng trục, bất kể đến sự định hướng các thiết bị. Trục Y có nghĩa là di chuyển lên hoặc xuống. Trục Z là di chuyển xa gần. Còn trục X là di chuyển qua trái hay qua phải.

Trở lại đoạn code. Chúng tôi kiểm tra nếu giá trị accelerometerY là cao nhất thì nó sẽ được hiển thị. Chúng ta cũng kiểm tra nếu giá trị định hướng trở lại thì hiển thị thông báo thích hợp. Tiếp theo, rất quan trọng là chúng ta cần phải gọi phương thức Gdx.input.isPeripheralAvailable () để kiểm tra xem thiết bị có hỗ trợ chức năng được yêu cầu hay không. Đầu tiên chúng ta kiểm tra xem điện thoại có hỗ trợ rung không, nếu có chúng ta kiểm tra xem giá trị trục Y có lớn hơn 7 không. Chúng ta cần nhớ giá trị 9,8 đại diện cho điện thoại hướng thẳng lên hoặc xuống, vì vậy nếu nó là 7 hoặc cao hơn nghĩa là điện thoại đang nghiêng 35 độ theo chiều dọc. Nếu đúng chúng ta sẽ làm cho điện thoại rung bằng cách gọi hàm vibrate() và truyển vào giá với đơn vị miliseconds cho biết thiết bị sẽ rung trong bao lâu.

.. Tiếp theo chúng ta kiểm tra xem thiết bị có hỗ trợ định hướng không. Nếu có, thêm vào message các gía trị.

Cuối cùng hiển thị chúng ra màn hình.

Có một việc khác cũng rất quan trọng trong ví dụ này:

public void resize(int width, int height) {
        spriteBatch.dispose();
        spriteBatch = new SpriteBatch();
        String resolution = Integer.toString(width) + "," + Integer.toString(height);
        Gdx.app.log("LOG", "Resolution change " + resolution);
    }

Trong phương thức resize(), chúng ta hủy biến spriteBatch và tạo lại. Điều này là bởi vì khi bạn thay đổi định hướng của các thiết bị từ landscape thành portrait hoặc ngược lại SpriteBatch sẽ bị lỗi vì không đúng kích thướng thiết bị. Vì vậy khi phương thức resize() được gọi, chúng ta cần tạo lại SpriteBatch.

Source code

Bạn có có thể tham khảo source code của project này tại đây.


All Rights Reserved