Dodatkowe funkcje kontrolera

Kontrolery do gier są wyposażone w dodatkowe funkcje, które znacznie poprawiają interakcję gracza i jego zaangażowanie. Funkcje haptyczne, czujniki ruchu i oświetlenie w kontrolerach do gier na Androida są szczególnie przydatne do wzbogacania wrażeń z rozgrywki. Każda funkcja w unikalny sposób stymuluje zmysły gracza, sprzyjając bardziej znaczącym i intuicyjnym interakcjom w grze.

Aby zadeklarować, że aplikacja obsługuje kontrolery do gier, dodaj do manifestu aplikacji funkcję android.hardware.gamepad:

<manifest ...>
  ...
  <uses-feature android:name="android.hardware.gamepad" android:required="false"/>
  ...
</manifest>

Reakcja na dotyk

Funkcja haptyczna w kontrolerach do gier na Androida to kluczowa technologia, która zapewnia realistyczne wrażenia dotykowe podczas rozgrywki.

Technologia haptyczna dostarcza użytkownikowi wrażeń fizycznych za pomocą wibracji lub ruchów. Na przykład gdy w grze dochodzi do wybuchu, kontroler wibruje, dzięki czemu gracz może realistycznie odczuć jego skutki. Dodatkowo subtelne wibracje można zsynchronizować z dźwiękiem chodzenia lub biegania postaci, co zapewnia bardziej realistyczne wrażenia. Ten rodzaj reakcji haptycznej pozwala graczom fizycznie odczuwać różne zdarzenia, które mają miejsce w grze.

Ta technologia maksymalizuje immersję gracza, wzmacnia reakcje emocjonalne i wzbogaca dynamikę gry. Ustawienia haptyczne w kontrolerach do gier na Androida nie tylko poszerzają możliwości twórcze deweloperów gier, ale też zapewniają graczom bardziej realistyczne wrażenia z rozgrywki.

Kotlin

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));
        }
    }
}

Aby używać wibracji, ustawia funkcję i uprawnienie.

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

Więcej informacji o VibratorManager i manifeście aplikacji.

Czujniki ruchu

Jedną z najbardziej innowacyjnych technologii, które poprawiają wrażenia z rozgrywki, jest kontroler do gier na Androida wyposażony w czujnik ruchu. Ta technologia precyzyjnie wykrywa ruchy fizyczne użytkowników i przekłada te dane na działania w grze, zapewniając bardziej intuicyjne i wciągające wrażenia. W tym wprowadzeniu omówimy, jak działają funkcje czujnika ruchu w kontrolerach do gier na Androida.

Czujniki ruchu zwykle zawierają żyroskopy i akcelerometry, które wykrywają ruchy i orientację użytkowników.

Musi implementować klasy detektorów przyspieszenia i żyroskopu oraz rejestrować te detektory w menedżerze czujników kontrolera.

Kotlin

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) {
    }
}

Więcej informacji o czujnikach ruchu i SensorEventListener.

Światła

Ustawienia koloru światła na kontrolerach do gier na Androida dodają nowy wymiar immersji do rozgrywki dzięki elementom wizualnym.

Funkcja koloru światła wykorzystuje wbudowane diody LED w kontrolerze do wyświetlania różnych kolorów, które dynamicznie reagują na różne scenariusze gry. Na przykład światła mogą migać na czerwono, gdy poziom zdrowia gracza jest krytyczny, lub świecić na zielono po ukończeniu określonej misji, zapewniając wizualne informacje zwrotne na podstawie wydarzeń w grze. Te ustawienia kolorów światła zwiększają zaangażowanie użytkowników, potęgują napięcie i przyjemność z gry oraz pomagają graczom bardziej zanurzyć się w świecie gry.

Funkcje podświetlenia w kontrolerach do gier na Androida nie tylko pełnią funkcję dekoracyjną, ale też odgrywają ważną rolę w tworzeniu nastroju gry i poprawianiu wrażeń użytkownika.

Kotlin

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());
                }
            }
        }
    }
}

Aby używać podświetlenia kontrolera, zadeklaruj w pliku manifestu funkcję pada do gier i uprawnienie LIGHTS:

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

Więcej informacji o LightsManager i manifeście aplikacji.

Touchpad kontrolera

Niektóre kontrolery do gier mają touchpad, który może służyć do wykonywania różnych działań w grze, takich jak poruszanie się po menu nawigacyjnym czy sterowanie postaciami w bardziej intuicyjny sposób.

Touchpad na kontrolerze do gier
Rysunek 1. Touchpad na kontrolerze do gier.

Kontrolery do gier ze zintegrowanymi touchpadami umożliwiają bezpośrednie sterowanie urządzeniem na Androidzie. Dotknięcie touchpada powoduje wyświetlenie na ekranie wskaźnika myszy, co umożliwia intuicyjną nawigację podobną do tej za pomocą myszy.