Dodatkowe funkcje kontrolera

Kontrolery do gier są wyposażone w dodatkowe funkcje, które znacznie zwiększają interakcję i immersję gracza. Reakcja na dotyk, czujniki ruchu i funkcje świetlne kontrolerów do gier na Androida są szczególnie przydatne w pogłębianiu i wzbogacaniu wrażeń z gry. Każda funkcja w wyjątkowy sposób stymuluje zmysły gracza, co sprzyja bardziej znaczącym i intuicyjnym interakcjom w grze.

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

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

Reakcja na dotyk

Funkcja reakcji na dotyk w kontrolerach do gier na Androida to kluczowa technologia, która zapewnia realistyczne reakcje dotykowe podczas rozgrywki.

Technologia reakcji na dotyk przekazuje użytkownikowi fizyczne doznania za pomocą wibracji lub ruchów. Jeśli na przykład w grze nastąpi eksplozja, kontroler wibruje, co pozwala graczowi realistycznie odczuć uderzenie. Ponadto subtelne wibracje można zsynchronizować z dźwiękiem chodzenia lub biegania postaci, co zapewnia bardziej realistyczne wrażenia. Ten rodzaj reakcji haptycznej umożliwia graczom fizyczne odczuwanie różnych wydarzeń w grze.

Ta technologia maksymalizuje immersję gracza, wzmacnia reakcje emocjonalne i wzbogaca dynamikę gry. Ustawienia reakcji na dotyk w kontrolerach do gier na Androida nie tylko poszerzają możliwości twórcze deweloperów gier, ale także zapewniają graczom bardziej realistyczne wrażenia niż kiedykolwiek wcześniej.

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żyć wibracji, ustaw funkcję i uprawnienia.

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

Więcej informacji o VibratorManager i pliku manifestu aplikacji.

Czujniki ruchu

Jedną z najbardziej innowacyjnych technologii, które zwiększają wrażenia z gry, jest kontroler do gier na Androida wyposażony w czujnik ruchu. Ta technologia precyzyjnie wykrywa fizyczne ruchy użytkowników i przekłada te dane na działania w grze, zapewniając bardziej intuicyjne i wciągające wrażenia z gry. 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.

Musisz zaimplementować klasy nasłuchiwania akceleracji i żyroskopu oraz zarejestrować te nasłuchiwacze 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 w kontrolerach do gier na Androida dodają nowy wymiar immersji do rozgrywki dzięki elementom wizualnym.

Funkcja koloru światła wykorzystuje wbudowane w kontroler diody LED 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 stan zdrowia gracza jest krytyczny, lub świecić na zielono po ukończeniu określonej misji, co zapewnia wizualne informacje zwrotne na podstawie wydarzeń w grze. Te ustawienia koloru światła zwiększają zaangażowanie użytkowników, potęgują napięcie i przyjemność z gry oraz pomagają graczom w pełni zanurzyć się w świecie gry.

Funkcje koloru światła w kontrolerach do gier na Androida służą nie tylko jako element dekoracyjny – 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ć świateł kontrolera, zadeklaruj w pliku manifestu funkcję gamepada i uprawnienie LIGHTS:

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

Więcej informacji o LightsManager i pliku manifestu aplikacji.

Touchpad kontrolera

Niektóre kontrolery do gier mają touchpad, który można wykorzystać do 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 zintegrowanym touchpadem umożliwiają bezpośrednie sterowanie urządzeniem na Androidzie. Dotknięcie touchpada powoduje wyświetlenie na ekranie wskaźnika myszy, co umożliwia intuicyjne poruszanie się po ekranie podobne do używania myszy.