UnityからBLEを使うためのネイティブプラグインを作る。
Android側
まず、Activityなしのプロジェクトを作って、New ModuleからAndroid Libraryを選択。 これらのパッケージ名がUnityで使うものと被らないようにする。
/Applications/Unity/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar
をModuleの方のlibsに置く。
import com.unity3d.player.UnityPlayer;
このjarは元々のやつとかぶってしまうので除外(build.gradleに追加)
android.libraryVariants.all { variant ->
variant.outputs.each { output ->
output.packageLibrary.exclude('libs/classes.jar')
}
}
ActiviyはUnityPlayer.currentActivity
で取得でき、
Unity側のメソッドを呼ぶのも
UnityPlayer.UnitySendMessage(mGameObjName, mCallBackName, new String(characteristic.getValue()));
のようにできる。
public class BLE {
private final static String TAG = BLE.class.getSimpleName();
private static final int REQUEST_ENABLE_BT = 1;
private static final int MY_PERMISSION_RESPONSE = 2;
private static final String PERIPHERAL_LOCAL_NAME = "my-ble";
private static final UUID PERIPHERAL_SERIVCE_UUID = UUID.fromString("BF9CB85F-620C-4A67-BDD2-1A64213F74CA");
private static final UUID PERIPHERAL_CHARACTERISTIC_UUID = UUID.fromString("5F83E23F-BCA1-42B3-B6F2-EA82BE46A93D");
private static final UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private String mGameObjName;
private String mCallBackName;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothGatt mBluetoothGatt;
private BluetoothGattCharacteristic mCharacteristic;
private Handler mHandler;
// Stops scanning after 30 seconds.
private static final long SCAN_PERIOD = 30000;
public BLE(String gameObjName, String callBackName) {
this.mGameObjName = gameObjName;
this.mCallBackName = callBackName;
mHandler = new Handler();
if (!UnityPlayer.currentActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(UnityPlayer.currentActivity, "BLEをサポートしていません", Toast.LENGTH_SHORT).show();
UnityPlayer.currentActivity.finish();
return;
}
final BluetoothManager bluetoothManager =
(BluetoothManager) UnityPlayer.currentActivity.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(UnityPlayer.currentActivity, "Bluetoothをサポートしていません", Toast.LENGTH_SHORT).show();
UnityPlayer.currentActivity.finish();
return;
}
onActive();
}
public void onActive() {
Log.d(TAG, "onActive");
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
UnityPlayer.currentActivity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
scanLeDevice(true);
}
public void onPause() {
Log.d(TAG, "onPause");
scanLeDevice(false);
if(mCharacteristic != null){
mBluetoothGatt.setCharacteristicNotification(
mCharacteristic,
false
);
}
if(mBluetoothGatt != null) {
mBluetoothGatt.close();
mBluetoothGatt = null;
}
}
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if(PERIPHERAL_LOCAL_NAME.equals(device.getName())){
scanLeDevice(false);
connect(device);
}
}
};
private boolean connect(BluetoothDevice device) {
if (mBluetoothAdapter == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
// Previously connected device. Try to reconnect.
if (mBluetoothGatt != null) {
Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
return true;
} else {
return false;
}
}
mBluetoothGatt = device.connectGatt(UnityPlayer.currentActivity, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
return true;
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
scanLeDevice(false);
Log.i(TAG, "Connected to GATT server.");
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
} else{
Log.i(TAG, "onConnectionStateChange:" + newState);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
mCharacteristic = gatt.getService(PERIPHERAL_SERIVCE_UUID).
getCharacteristic(PERIPHERAL_CHARACTERISTIC_UUID);
gatt.setCharacteristicNotification(
mCharacteristic,
true
);
BluetoothGattDescriptor descriptor = mCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
UnityPlayer.UnitySendMessage(mGameObjName, mCallBackName, new String(characteristic.getValue()));
}
};
}
Manifestに追加した分。
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
できたらaarを生成する。
$ ./gradlew assembleRelease
build/outputs/aar/*-release.aar
をUnityのAssets/Plugins/Android/libs
に置く。
あと、依存aarはこの中に含まれないようなのでそれもまとめてコピーする必要がある。
task copyLibs(type: Copy) {
from configurations.compile
into 'build/outputs/aar'
exclude { details -> details.file.name.endsWith(".jar") }
}
Manifetstのmergeに失敗したのでSDKVersionを合わせる。
compileSdkVersion 22
defaultConfig {
minSdkVersion 19
targetSdkVersion 22
...
Unity側
こんな感じでインスタンスを作り、
メソッドを呼べる。
ただし、unity editor上ではInit'd AndroidJavaClass with null ptr!
のエラーが出る。
plugin = new AndroidJavaObject("net.sambaiz.unity_ble.BLE", this.gameObject.name, "received");
void received(string message){
Debug.Log ("BLE:" + message);
}
void OnApplicationPause (bool pauseStatus)
{
if (pauseStatus) {
plugin.Call ("onPause");
} else {
plugin.Call ("onActive");
}
}