תכונות בקר נוספות

שלטים למשחקים מצוידים בתכונות נוספות שמשפרות באופן משמעותי את האינטראקציה של השחקנים ואת חוויית המשחק. הפונקציות של משוב הפטי, חיישני התנועה והתאורה בשלטים של משחקי Android תורמות במיוחד להעמקה ולהעשרה של חוויית הגיימינג. כל תכונה מעוררת את החושים של השחקן בצורה ייחודית, ומעודדת אינטראקציות משמעותיות ואינטואיטיביות יותר במשחק.

כדי להצהיר שהאפליקציה תומכת בשלטי משחק, צריך להוסיף את התכונה android.hardware.gamepad לקובץ המניפסט של האפליקציה:

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

מגע

תכונת המשוב ההפטי בשלטים למשחקים ב-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));
        }
    }
}

כדי להשתמש ברטט, היא מגדירה תכונה והרשאה.

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

מידע נוסף על 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());
                }
            }
        }
    }
}

כדי להשתמש בנורות של השלט, צריך להצהיר על התכונה gamepad ועל ההרשאה LIGHTS במניפסט:

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

מידע נוסף על LightsManager ועל קובץ מניפסט של אפליקציה.

לוח מגע של השלט

חלק מבקרי המשחקים כוללים לוח מגע שאפשר להשתמש בו למגוון פעולות במשחק, כמו ניווט בתפריטים או שליטה בדמויות במשחק בצורה אינטואיטיבית יותר.

לוח מגע בשלט המשחק
איור 1. לוח מגע בשלט המשחק.

שלטי משחק עם משטחי מגע משולבים מאפשרים שליטה ישירה במכשיר ב-Android. כשנוגעים בלוח המגע, מופיע סמן עכבר במסך, שמאפשר ניווט אינטואיטיבי כמו בעכבר.