Processing:Библиотеки/Processing for Android/Руководства/Использование датчиков

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску


Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.



Использование датчиков [1]

Описание

Это руководство дает базовое понимание того, как считывать данные с датчиков в Processing.

API для датчиков в SDK Android

Смартфоны и планшеты оснащены множеством различных датчиков – к примеру, акселерометрами, гироскопами, магнетометрами и термометрами. Эти датчики позволяют нам получать информацию о движениях, окружении и местоположении устройства. Хотя в языке Processing нет специальных функций для считывания данных этих датчиков, вы можете импортировать в Android SDK ПО-пакеты, отправляющие данные датчиков в ваш скетч.

Создание менеджера датчиков

В первом примере мы создадим скетч, реагирующий на изменения в ускорении, и нам понадобятся для этого данные акселерометра. Первый шаг в использовании API датчика – это получение контекста активности, в которой будет выполняться этот скетч, а затем получение менеджера датчиков из этого контекста. Затем мы можем инициализировать датчик, который понадобится в нашем скетче, и в данном случае это акселерометр. Делаем все это в блоке setup().

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;

Context context;
SensorManager manager;
Sensor sensor;

void setup() {
  fullScreen();
  
  context = getActivity();
  manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
  sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}

void draw() {
}

Добавление «прослушки» датчика

Теперь нам нужно добавить класс для «прослушки», который будет уведомлять скетч о том, что у датчика доступы новые данные. «Слушатель» для каждого датчика наследует у базового класса SensorEventListener из SDK Android. Создав в скетче экземпляр «класса-слушателя», мы можем зарегистрировать слушателя при помощи менеджера:

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;

Context context;
SensorManager manager;
Sensor sensor;
AccelerometerListener listener;

void setup() {
  fullScreen();
  
  context = getActivity();
  manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
  sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  listener = new AccelerometerListener();
  manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
}

void draw() {
}

class AccelerometerListener implements SensorEventListener {
  public void onSensorChanged(SensorEvent event) {
  }
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
  }
}

Вы, возможно, заметили, что при регистрации «слушателя» используется аргумент SensorManager.SENSOR_DELAY_GAME. Этот аргумент задает скорость, с которой датчик будет считывать новые данные. Чем выше эта скорость, тем выше чувствительность датчика, но и тем быстрее расходуется заряд батареи. Помимо SENSOR_DELAY_GAME есть и другие предопределенные константы для установления скорости считывания данных – SENSOR_DELAY_FASTEST, SENSOR_DELAY_NORMAL и SENSOR_DELAY_UI.

Считывание данных датчика

Слушатель событий имеет два метода – onSensorChanged() и onAccuracyChanged(). Для считывания данных датчика нам понадобится только onSensorChanged(). Если речь об акселерометре, то данные от этого датчика представляют собой три значения с плавающей точкой, отвечающие за ускорение устройства по осям X, Y и Z. На устройстве эти оси выглядят следующим образом:

Для первого теста мы можем просто напечатать эти значения на экране, а также проверить, что если мы положим телефон на стол горизонтально и экраном вверх, то ускорение по оси Z составит 9.81 м/с2, что соответствует силе притяжения.

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;

Context context;
SensorManager manager;
Sensor sensor;
AccelerometerListener listener;
float ax, ay, az;

void setup() {
  fullScreen();
  
  context = getActivity();
  manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
  sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  listener = new AccelerometerListener();
  manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
  
  textFont(createFont("SansSerif", 30 * displayDensity));
}

void draw() {
  background(0);
  text("X: " + ax + "\nY: " + ay + "\nZ: " + az, 0, 0, width, height);
}

class AccelerometerListener implements SensorEventListener {
  public void onSensorChanged(SensorEvent event) {
    ax = event.values[0];
    ay = event.values[1];
    az = event.values[2];    
  }
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
  }
}

Примечание: Обратите внимание на аргумент displayDensity, который используется для масштабирования размера текста. Это значение берется из поля DisplayMetrics.density в SDK Android. На экране с 160 dpi (например, с разрешением 240 х 320 и размером 1.5 на 2 дюйма) это значение будет «1». Это поле позволяет настроить размер графических элементов в скетче (вроде текста) пропорционально dpi.

При использовании датчиков рекомендуется отменить регистрацию слушателя, когда активность скетча поставлена на паузу, чтобы сэкономить заряд батареи, а затем, когда активность возобновится, снова зарегистрировать его. Это можно сделать, добавив в скетч такой фрагмент:

public void onResume() {
  super.onResume();
  if (manager != null) {
    manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
  }
}

public void onPause() {
  super.onPause();
  if (manager != null) {
    manager.unregisterListener(listener);
  }
}

В конце выполняется проверка экземпляра класса менеджера на null, потому что функции onPause() и onResume() могут быть вызваны в вашем скетче до блока setup().

Более сложный пример с использованием Box2D

Один из вариантов использования данных от датчиков – это управление движением графических элементов в вашем скетче. Значения акселерометра, по сути, информируют о силах, которые действуют на ваше устройство, поэтому если мы воспользуемся ими для управления поведения, скажем, системы твердых тел, то сможем соединить поведение, заданное в коде, с поведением, задаваемым при помощи движений в реальности.

Чтобы реализовать это, мы воспользуемся библиотекой Box2D for Processing, разработанной Дэниэлом Шиффманом. Хотя эта библиотека создавалась не для Android, мы все равно сможем использовать ее в скетчах в режиме программирования Android, т.к. он написан на Java!

Мы объединим базовый код из предыдущего примера, чтобы прочесть данные акселерометра, с простой симуляцией Box2D, где границы экрана задают границы симуляции, и в ней также будет несколько коробок, болтающихся согласно силам ускорения, задаваемым перемещением устройства. Полный код этого скетча-примера можно найти тут.

Без данных от акселерометра код Box2D выглядит следующим образом (скетчи-примеры с использованием классов Box и Wall смотрите на GitHub – тут и тут):

import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;

Box2DProcessing box2d;
ArrayList<Box> boxes;
ArrayList<Wall> walls;

void setup() {
  fullScreen(P2D);
  orientation(PORTRAIT);
  
  box2d = new Box2DProcessing(this);
  box2d.createWorld();
  box2d.setGravity(0, -10);
  
  // группа коробок:
  boxes = new ArrayList<Box>();
  for (int i = 0; i < 20; i++) {
    Box p = new Box(random(200, width-200), random(200, height-200));
    boxes.add(p);
  }
  
  // невидимые стены:
  walls = new ArrayList<Wall>();
  walls.add(new Wall(width/2, -25, width, 50));
  walls.add(new Wall(width/2, height+25, width, 50));
  walls.add(new Wall(-25, height/2, 50, height));
  walls.add(new Wall(width+25, height/2, 50, height));
}

void draw() {
  background(255);  
  
  box2d.step();
  
  for (Box b: boxes) {
    b.display();
  }
}

void mousePressed() {
  for (Box b: boxes) {
    b.shake();
  }   
}

В этом коде мы используем постоянное ускорение по оси Y в виде (0, 10), но мы также можем привязать силу притяжения в Box2D к значениям ускорения от акселерометра:

box2d.setGravity(-ax, -ay);

Таким образом, в результате скетч, в котором Box2D будет работать вместе с акселерометром, будет выглядеть следующим образом:

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;

import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;

Context context;
SensorManager manager;
Sensor sensor;
AccelerometerListener listener;
float ax, ay, az;

Box2DProcessing box2d;
ArrayList<Box> boxes;
ArrayList<Wall> walls;

void setup() {
  fullScreen(P2D);
  orientation(PORTRAIT);
  
  context = getActivity();
  manager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
  sensor = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  listener = new AccelerometerListener();
  manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
  
  box2d = new Box2DProcessing(this);
  box2d.createWorld();
  
  // группа коробок:
  boxes = new ArrayList<Box>();
  for (int i = 0; i < 20; i++) {
    Box p = new Box(random(200, width-200), random(200, height-200));
    boxes.add(p);
  }
  
  // невидимые стены:
  walls = new ArrayList<Wall>();
  walls.add(new Wall(width/2, -25, width, 50));
  walls.add(new Wall(width/2, height+25, width, 50));
  walls.add(new Wall(-25, height/2, 50, height));
  walls.add(new Wall(width+25, height/2, 50, height));
}

void draw() {
  background(255);  
  
  // обновляем гравитационную информацию данными от акселерометра: 
  box2d.setGravity(-ax, -ay);
  
  box2d.step();
  
  for (Box b: boxes) {
    b.display();
  }
}

void mousePressed() {
  for (Box b: boxes) {
    b.shake();
  }   
}

public void onResume() {
  super.onResume();
  if (manager != null) {
    manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);
  }
}

public void onPause() {
  super.onPause();
  if (manager != null) {
    manager.unregisterListener(listener);
  }
}

class AccelerometerListener implements SensorEventListener {
  public void onSensorChanged(SensorEvent event) {
    ax = event.values[0];
    ay = event.values[1];
    az = event.values[2];    
  }
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
  }
}

Использование других датчиков

В документации Google для разработчиков есть подробная информация о том, как считывать данные с других датчиков движения. В качестве альтернативы можно воспользоваться библиотекой Ketai, разработанной Дэниэлом Сотером и Иисусом Дюраном. Она обертывает весь управляющий код и серьезно упрощает считывание данных от разных датчиков. Библиотеку Ketai можно установить из меню Contribution Manager.

См.также

Внешние ссылки