Дополнительные возможности контроллера

Игровые контроллеры оснащены дополнительными функциями, которые значительно улучшают взаимодействие игрока и погружение в игру. Тактильная обратная связь, датчики движения и световые эффекты Android-игровых контроллеров играют особенно важную роль в углублении и обогащении игрового процесса. Каждая функция уникальным образом стимулирует чувства игрока, способствуя более осмысленному и интуитивно понятному взаимодействию в игре.

Тактильные ощущения

Функция тактильной обратной связи в игровых контроллерах для Android — это важнейшая технология, обеспечивающая реалистичную тактильную отдачу во время игры.

Технология тактильной обратной связи передает пользователю физические ощущения посредством вибрации или движений. Например, при взрыве в игре контроллер вибрирует, позволяя игроку реалистично почувствовать удар. Кроме того, едва заметные вибрации могут быть синхронизированы со звуком ходьбы или бега персонажа, обеспечивая более реалистичные ощущения. Этот тип тактильной обратной связи позволяет игрокам физически ощущать различные события, происходящие в игре.

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

Котлин

fun triggerVibrationMultiChannel(
  deviceId: Int, leftIntensity: Int, leftDuration: Int,
  rightIntensity: Int, rightDuration: Int) {
  val inputDevice = InputDevice.getDevice(deviceId)
  val vibratorManager = inputDevice!!.vibratorManager
  if (vibratorManager != null) {
    val vibratorIds = vibratorManager.vibratorIds
    val vibratorCount = vibratorIds.size
    if (vibratorCount > 0) {
      // We have an assumption that game controllers have two vibrators
      // corresponding to a left motor and a right motor, and the left
      // motor will be first.
      updateVibrator(vibratorManager.getVibrator(vibratorIds  [0]), leftIntensity, leftDuration)
      if (vibratorCount > 1) {
        updateVibrator(vibratorManager.getVibrator(vibratorIds[1]), rightIntensity, rightDuration)
      }
    }
  }
}

fun updateVibrator(vibrator: Vibrator?, intensity: Int, duration: Int) {
  if (vibrator != null) {
    if (intensity == 0) {
      vibrator.cancel()
    } else if (duration > 0) {
      vibrator.vibrate(VibrationEffect.createOneShot(duration.toLong(), intensity))
    }
  }
}

Java

public void triggerVibrationMultiChannel(
    int deviceId, int leftIntensity, int leftDuration,
    int rightIntensity, int rightDuration) {

    InputDevice inputDevice = InputDevice.getDevice(deviceId);

    // Check if device exists to avoid NullPointerException
    if (inputDevice == null) {
      return;
    }

    VibratorManager vibratorManager = inputDevice.getVibratorManager();
    if (vibratorManager != null) {
        int[] vibratorIds = vibratorManager.getVibratorIds();
        int vibratorCount = vibratorIds.length;

        if (vibratorCount > 0) {
            // We have an assumption that game controllers have two vibrators
            // corresponding to a left motor and a right motor, and the left
            // motor will be first.
            updateVibrator(vibratorManager.getVibrator(vibratorIds[0]), leftIntensity, leftDuration);

            if (vibratorCount > 1) {
                updateVibrator(vibratorManager.getVibrator(vibratorIds[1]), rightIntensity, rightDuration);
            }
        }
    }
}

public void updateVibrator(Vibrator vibrator, int intensity, int duration) {
    if (vibrator != null) {
        if (intensity == 0) {
            vibrator.cancel();
        } else if (duration > 0) {
            vibrator.vibrate(VibrationEffect.createOneShot. ((long) duration, intensity));
        }
    }
}

Для использования вибрации необходимо установить функцию и права доступа.

<application ...>
  ...
  <uses-feature android:name="android.hardware.gamepad" android:required="true"/>
  <uses-permission android:name="android.permission.VIBRATE"/>
  ...
</application>

Для получения дополнительной информации о VibratorManager и манифесте приложения .

Датчики движения

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

Датчики движения обычно включают в себя гироскопы и акселерометры для определения движений и ориентации пользователей.

Необходимо реализовать классы для обработки сигналов ускорения и гироскопа и зарегистрировать эти обработчики в менеджере датчиков контроллера.

Котлин

fun setIntegratedAccelerometerActive(deviceId: Int) {
  val device = InputDevice.getDevice(deviceId)
  val sensorManager = device?.sensorManager
  val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
  if (accelerometer != null) {
    val accelerometerListener =
      GameControllerAccelerometerListener(accelerometer)
    sensorManager.registerListener(
      accelerometerListener, accelerometer,
      SensorManager.SENSOR_DELAY_GAME
    )
  }
}

fun setIntegratedGyroscopeActive(deviceId: Int) {
  val device = InputDevice.getDevice(deviceId)
  val sensorManager = device?.sensorManager
  val gyroscope = sensorManager?.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
  if (gyroscope != null) {
    val gyroscopeListener = GameControllerGyroscopeListener(gyroscope)
    sensorManager.registerListener(
      gyroscopeListener, gyroscope,
      SensorManager.SENSOR_DELAY_GAME
    )
  }
}

class GameControllerAccelerometerListener(private val listenerAccelerometer: Sensor?) :
  SensorEventListener {
  override fun onSensorChanged(event: SensorEvent) {
    if (listenerAccelerometer != null) {
      synchronized(listenerAccelerometer) {
        if (event.sensor == listenerAccelerometer) {
          Log.d("Accelerometer",
            "onSensorChanged " + event.values[0] + ", "
            + event.values[1] + ", " + event.values[2])
        }
      }
    }
  }

  override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
  }
}

class GameControllerGyroscopeListener(private val listenerGyroscope: Sensor?) :
  SensorEventListener {
  override fun onSensorChanged(event: SensorEvent) {
    if (listenerGyroscope != null) {
      synchronized(listenerGyroscope) {
        if (event.sensor == listenerGyroscope) {
          Log.d("Gyroscope",
            "onSensorChanged " + event.values[0] + ", " +
            event.values[1] + ", " + event.values[2])
        }
      }
    }
 }

  override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
  }
}

Java


public void setIntegratedAccelerometerActive(int deviceId) {
    InputDevice device = InputDevice.getDevice(deviceId);
    // Safe handling for null device or sensor manager
    if (device == null) {
      return;
    }
    SensorManager sensorManager = device.getSensorManager();
    if (sensorManager == null) {
      return;
    }

    Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    if (accelerometer != null) {
      GameControllerAccelerometerListener   accelerometerListener =
          new GameControllerAccelerometerListener(accelerometer);
        sensorManager.registerListener(
          accelerometerListener, accelerometer,
          SensorManager.SENSOR_DELAY_GAME
        );
    }
}

public void setIntegratedGyroscopeActive(int deviceId) {
    InputDevice device = InputDevice.getDevice(deviceId);
    if (device == null) {
        return;
    }
    SensorManager sensorManager = device.getSensorManager();
    if (sensorManager == null) {
        return;
    }
    Sensor gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
    if (gyroscope != null) {
        GameControllerGyroscopeListener gyroscopeListener =
          new GameControllerGyroscopeListener(gyroscope);
        sensorManager.registerListener(
          gyroscopeListener, gyroscope,
          SensorManager.SENSOR_DELAY_GAME
        );
    }
}

public static class GameControllerAccelerometerListener implements SensorEventListener {
    private final Sensor listenerAccelerometer;
    public GameControllerAccelerometerListener(Sensor   listenerAccelerometer) {
        this.listenerAccelerometer = listenerAccelerometer;
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (listenerAccelerometer != null) {
            synchronized (listenerAccelerometer) {
                if (event.sensor == listenerAccelerometer) {
                    Log.d("Accelerometer",
                      "onSensorChanged " + event.values[0] + ", "
                      + event.values[1] + ", " + event.values[2]);
                }
            }
        }
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

public static class GameControllerGyroscopeListener implements SensorEventListener {
    private final Sensor listenerGyroscope;

    public GameControllerGyroscopeListener(Sensor listenerGyroscope) {
        this.listenerGyroscope = listenerGyroscope;
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (listenerGyroscope != null) {
            synchronized (listenerGyroscope) {
                if (event.sensor == listenerGyroscope) {
                    Log.d("Gyroscope",
                      "onSensorChanged " + event.values[0] +  ", " +
                        event.values[1] + ", " + event.values  [2]);
                }
            }
        }
    }
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

Для получения дополнительной информации о датчиках движения и SensorEventListener .

Свет

Настройки цветовой гаммы подсветки на игровых контроллерах Android добавляют новый уровень погружения в игровой процесс благодаря визуальным элементам.

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

Подсветка игровых контроллеров для Android выполняет не только декоративную функцию — она играет важную роль в создании атмосферы игры и улучшении пользовательского опыта.

Котин

fun changeControllerLightColor(deviceId: Int, color: Int) {
  val device = InputDevice.getDevice(deviceId)
  device?.let {
    if (it.sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) {
      val lightsManager = device.lightsManager
      lightsManager?.let { manager ->
        manager.lights.forEach { light ->
          val stateBuilder = LightState.Builder()
          stateBuilder.setColor(color)
          val requestBuilder = LightsRequest.Builder()
          requestBuilder.addLight(light, stateBuilder.build())
          val lightsSession = lightsManager.openSession()
          lightsSession.requestLights(requestBuilder.build())
        }
      }
    }
  }
}

Java

public void changeControllerLightColor(int deviceId, int  color) {
    InputDevice device = InputDevice.getDevice(deviceId);

    if (device != null) {
      // Check if the device is a joystick.
      // Note: Parentheses are required around the bitwise AND operation in Java
      // because == has higher precedence than &.
        if ((device.getSources() & InputDevice.  SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK) {
            LightsManager lightsManager = device.getLightsManager();

            if (lightsManager != null) {
                for (Light light : lightsManager.getLights()) {
                    LightState.Builder stateBuilder = new   LightState.Builder();
                    stateBuilder.setColor(color);

                    LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
                    requestBuilder.addLight(light, stateBuilder.build());

                    LightsManager.Session lightsSession =   lightsManager.openSession();
                    lightsSession.requestLights(requestBuilder.build());
                }
            }
        }
    }
}

Для использования вибрации необходимо установить функцию и права доступа.

<application ...>
  ...
  <uses-feature android:name="android.hardware.gamepad" android:required="true"/>
  <uses-permission android:name="android.permission.LIGHTS" />
  ...
</application>

Для получения дополнительной информации о LightsManager и манифесте приложения .

сенсорная панель контроллера

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

Сенсорная панель на игровом контроллере
Рисунок 1. Сенсорная панель игрового контроллера.

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