추가 컨트롤러 기능

게임 컨트롤러에는 플레이어 상호작용과 몰입도를 크게 향상하는 추가 기능이 탑재되어 있습니다. Android 게임 컨트롤러의 햅틱, 동작 센서, 조명 기능은 특히 게임 환경을 심화하고 풍부하게 하는 데 유용합니다. 각 기능은 플레이어의 감각을 고유하게 자극하여 게임 내에서 더 의미 있고 직관적인 상호작용을 촉진합니다.

햅틱

Android 게임 컨트롤러의 햅틱 기능은 게임 플레이 중에 현실적인 촉각 피드백을 제공하는 중요한 기술입니다.

햅틱 기술은 진동이나 움직임을 통해 사용자에게 물리적 감각을 전달합니다. 예를 들어 게임에서 폭발이 발생하면 컨트롤러가 진동하여 플레이어가 충격을 현실적으로 느낄 수 있습니다. 또한 미세한 진동을 캐릭터가 걷거나 뛰는 소리와 동기화하여 더욱 생생한 경험을 제공할 수 있습니다. 이 유형의 햅틱 피드백을 통해 플레이어는 게임 내에서 발생하는 다양한 이벤트를 물리적으로 느낄 수 있습니다.

이 기술은 플레이어의 몰입도를 극대화하고, 감정적 반응을 증폭하며, 게임의 역동성을 풍부하게 합니다. Android 게임 컨트롤러의 햅틱 설정은 게임 개발자의 창의적인 가능성을 넓힐 뿐만 아니라 플레이어에게 이전보다 더 현실적인 게임 환경을 제공합니다.

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

자바

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 게임 컨트롤러의 동작 센서 기능이 어떻게 작동하는지 알아봅니다.

동작 센서는 일반적으로 자이로스코프와 가속도계를 통합하여 사용자의 움직임과 방향을 감지합니다.

가속도 및 자이로스코프 리스너 클래스를 구현하고 컨트롤러의 센서 관리자에 이러한 리스너를 등록해야 합니다.

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

자바


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 게임 컨트롤러의 조명 색상 설정은 시각적 요소를 통해 게임플레이에 새로운 차원의 몰입감을 더합니다.

조명 색상 기능은 컨트롤러에 내장된 LED 조명을 활용하여 다양한 색상을 표시하며, 이는 다양한 게임 시나리오에 동적으로 반응합니다. 예를 들어 플레이어의 체력이 위험한 수준일 때 조명이 빨간색으로 깜박이거나 특정 미션을 완료하면 녹색으로 빛나 인게임 이벤트에 따라 시각적 피드백을 제공할 수 있습니다. 이러한 밝은 색상 설정은 사용자 참여도를 높이고, 게임의 긴장감과 재미를 더하며, 플레이어가 게임 세계에 더 몰입할 수 있도록 지원합니다.

Android 게임 컨트롤러의 조명 색상 기능은 단순한 장식용 이상의 역할을 합니다. 게임의 분위기를 설정하고 사용자 환경을 개선하는 데 중요한 역할을 합니다.

Kotin

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

자바

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에서 직접 기기 제어를 제공합니다. 터치패드를 터치하면 화면에 마우스 포인터가 생성되어 직관적인 마우스와 같은 탐색이 가능합니다.