الميزات الإضافية لوحدة التحكّم

تتضمّن أدوات التحكّم في الألعاب ميزات إضافية تعمل على تحسين تفاعل اللاعبين وانغماسهم في اللعبة بشكل كبير. تساهم وظائف الاهتزاز وأجهزة استشعار الحركة والإضاءة في وحدات التحكّم بالألعاب على أجهزة 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))
    }
  }
}

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.

تتضمّن أدوات استشعار الحركة عادةً جيروسكوبات ومقاييس تسارع لرصد حركات المستخدمين واتجاهاتهم.

يجب أن تنفِّذ هذه الفئة فئات أدوات معالجة التسارع والجيروسكوب وأن تسجِّل أدوات المعالجة هذه في أداة إدارة المستشعرات الخاصة بوحدة التحكّم.

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

لمزيد من المعلومات حول أجهزة استشعار الحركة و 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())
        }
      }
    }
  }
}

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. يؤدي لمس لوحة اللمس إلى إنشاء مؤشر ماوس على الشاشة، ما يتيح التنقّل بسهولة كما لو كنت تستخدم ماوس.