ברמת המערכת, מערכת Android מדווחת על קודי אירועים שהוזנו מנאמני המשחקים. כקודי מפתח וערכי צירים של Android. במשחק שלך אפשר לקבל את הקודים הבאים והערכים ולהמיר אותם לפעולות ספציפיות בתוך המשחק.
כששחקנים מתחברים פיזית או מתאימים אלחוטי בקר משחקים אל
במכשירים שלהם מבוססי Android, המערכת מזהה את הבקר באופן אוטומטי
כמכשיר לקליטת נתונים, ומתחיל לדווח על אירועי הקלט שלו. המשחק שלך יכול לקבל
באירועי הקלט האלה, על ידי הטמעת השיטות הבאות לקריאה חוזרת (callback) בחשבון הפעיל
Activity
או View
במוקד (עליך
להטמיע את הקריאות החוזרות עבור Activity
או
View
, אבל לא גם וגם):
- החל מ-
Activity
:dispatchGenericMotionEvent(android.view. MotionEvent)
קריאה לעיבוד אירועי תנועה כלליים כמו תנועות בג'ויסטיק.
dispatchKeyEvent(android.view.KeyEvent)
קריאה לעיבוד אירועים מרכזיים, כמו עיתונות או הודעה על לחצן בקר משחקים או לחצני החיצים (D-pad).
- החל מ-
View
:onGenericMotionEvent(android.view.MotionEvent)
קריאה לעיבוד אירועי תנועה כלליים כמו תנועות בג'ויסטיק.
onKeyDown(int, android.view.KeyEvent)
בוצעה קריאה לעיבוד לחיצה על מקש פיזי, כמו בקר משחקים או לחצן D-pad.
onKeyUp(int, android.view.KeyEvent)
נשלחה קריאה לעיבוד של שחרור מפתח פיזי, כמו בקר משחקים או לחצן D-pad.
הגישה המומלצת היא לתעד את האירועים
אובייקט View
ספציפי שהמשתמש מקיים איתו אינטראקציה.
כדי לקבל מידע, יש לבדוק את האובייקטים הבאים שסופקו על ידי הקריאות החוזרות
על סוג אירוע הקלט שהתקבל:
KeyEvent
- אובייקט שמתאר כיוונים
אירועים בלחצני החיצים (D-pad) ובקר משחקים. אירועים מרכזיים מלווים
קוד מפתח שמציין את הלחצן הספציפי שמופעל, כמו
DPAD_DOWN
אוBUTTON_A
. אפשר לקבל את לקוד המפתחgetKeyCode()
או מהמפתח קריאות חוזרות (callback) של אירועים, כמוonKeyDown()
. MotionEvent
- אובייקט שמתאר קלט מהג'ויסטיק ומההדק בכתף
תנועות חדשות. אירועי תנועה מלווים בקוד פעולה ובקבוצה של
ערכי צירים. קוד הפעולה מציין את שינוי המצב שהתרחש
למשל ג'ויסטיק שמזיז. ערכי הציר מתארים את המיקום
מאפייני תנועה לשליטה פיזית ספציפית,
AXIS_X
אוAXIS_RTRIGGER
. אפשר לקבל את קוד הפעולה באמצעות קריאה ל-getAction()
וערך הציר בהתקשרות אלgetAxisValue()
.
השיעור הזה מתמקד באופן שבו אפשר להתמודד עם קלט מהסוגים הנפוצים ביותר
אמצעי בקרה פיזיים (לחצני בקר משחקים, רפידות כיווניות
ג'ויסטיקים) במסך משחק על ידי הטמעת
View
שיטות להתקשרות חזרה ועיבוד
KeyEvent
ו-MotionEvent
אובייקטים.
איך לוודא שמחובר בקר משחקים
במהלך דיווח על אירועי קלט, מערכת Android לא מבחינה בין
בין אירועים שהגיעו ממכשיר שהוא לא שלט רחוק של משחקים לבין אירועים שהגיעו
לבקרת משחקים. לדוגמה, פעולה במסך מגע יוצרת
אירוע AXIS_X
שמייצג את ה-X
של משטח המגע, אבל ג'ויסטיק יוצר
אירוע AXIS_X
שמייצג את המיקום X של הג'ויסטיק. אם המיקום
במשחק שלכם מתייחסים לקלט של בקר משחקים, צריך לבדוק קודם
שאירוע הקלט מגיע מסוג מקור רלוונטי.
כדי לוודא שמכשיר קלט מחובר הוא שלט לגיימינג, צריך להתקשר
getSources()
כדי לקבל שדה ביט משולב של
בסוגי מקורות הקלט שנתמכים במכשיר הזה. לאחר מכן אפשר לבדוק אם
השדות הבאים מוגדרים:
SOURCE_GAMEPAD
מציין לפי סוג המקור שמכשיר הקלט כולל לחצנים של בקר משחקים (לדוגמה,BUTTON_A
). שימו לב שהמקור הזה type לא מציין במפורש אם בקר המשחק כולל לחצנים בלחצני החיצים (D-pad). למרות שרוב הגיימפאדים בדרך כלל כוללים פקדי כיוון,- סוג מקור של
SOURCE_DPAD
מציין למכשיר לקליטת נתונים יש לחצני D-pad (לדוגמה,DPAD_UP
). - סוג מקור של
SOURCE_JOYSTICK
מציין שלמכשיר הקלט יש התקן שליטה אנלוגי (לדוגמה, ג'ויסטיק שמקליט תנועות לאורךAXIS_X
ו-AXIS_Y
).
קטע הקוד הבא מציג שיטה מסייעת שמאפשרת לכם לבדוק מכשירי הקלט המחוברים הם בקרי משחקים. במקרה כזה, השיטה מאחזרת מזהי המכשירים לבקרי המשחקים. לאחר מכן אפשר לשייך כל מכשיר מזהה עם שחקן במשחק שלך, ועיבוד פעולות במשחק לכל קישור בנגן בנפרד. למידע נוסף על תמיכה בכמה שלטים לגיימינג שמחוברים בו-זמנית באותו מכשיר Android, לתמוך בכמה בקרי משחקים.
Kotlin
fun getGameControllerIds(): List<Int> { val gameControllerDeviceIds = mutableListOf<Int>() val deviceIds = InputDevice.getDeviceIds() deviceIds.forEach { deviceId -> InputDevice.getDevice(deviceId).apply { // Verify that the device has gamepad buttons, control sticks, or both. if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK) { // This device is a game controller. Store its device ID. gameControllerDeviceIds .takeIf { !it.contains(deviceId) } ?.add(deviceId) } } } return gameControllerDeviceIds }
Java
public ArrayList<Integer> getGameControllerIds() { ArrayList<Integer> gameControllerDeviceIds = new ArrayList<Integer>(); int[] deviceIds = InputDevice.getDeviceIds(); for (int deviceId : deviceIds) { InputDevice dev = InputDevice.getDevice(deviceId); int sources = dev.getSources(); // Verify that the device has gamepad buttons, control sticks, or both. if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { // This device is a game controller. Store its device ID. if (!gameControllerDeviceIds.contains(deviceId)) { gameControllerDeviceIds.add(deviceId); } } } return gameControllerDeviceIds; }
בנוסף, כדאי לבדוק את היכולות של כל סוג קלט נתמך על ידי בקר משחקים מחובר. האפשרות הזו יכולה להיות שימושית, לדוגמה, אם אתם רוצים שהמשחק ישתמש רק בקלט מתוך קבוצת הפקדים הפיזיים מבינה.
כדי לזהות אם יש תמיכה בקוד מפתח או בקוד ציר ספציפי שלט רחוק לגיימינג, צריך להשתמש בשיטות הבאות:
- ב-Android מגרסה 4.4 (רמת API 19) ואילך, אפשר לקבוע אם קוד מפתח
נתמכת בבקר משחקים מחובר באמצעות התקשרות
hasKeys(int...)
- ב-Android 3.1 (רמת API 12) ואילך, ניתן למצוא את כל הצירים הזמינים
נתמכת בבקר משחקים מחובר באמצעות קריאה ראשונה
getMotionRanges()
לאחר מכן, בכל הוחזר אובייקט אחד (InputDevice.MotionRange
), קריאהgetAxis()
כדי לקבל את מזהה הציר שלו.
עיבוד לחיצות על לחצני בקר משחקים
איור 1 מראה איך מערכת Android ממפה ערכי מפתחות וערכי צירים אמצעי הבקרה הזמינים ברוב בקרי המשחקים.
![](https://developer.android.com/static/images/training/game-controller-profiles.png?authuser=9&hl=he)
איור 1. פרופיל לבקר משחקים גנרי.
נכסי היתרונות המרכזיים שמופיעים באיור מתייחסים לפרטים הבאים:
קודי מפתח נפוצים שנוצרים על ידי לחיצות על בקר משחקים:
BUTTON_A
,
BUTTON_B
,
BUTTON_SELECT
,
ו-BUTTON_START
. חלק מהמשחק
הבקרים מפעילים גם את קוד המפתח DPAD_CENTER
כשלוחצים על המרכז של משטח ה-D-pad. שלך
המשחק יכול לבדוק את קוד המפתח באמצעות התקשרות אל getKeyCode()
או מקריאות חוזרות של אירועים מרכזיים כמו
onKeyDown()
,
ואם הוא מייצג אירוע שרלוונטי למשחק, לעבד אותו
פעולה במשחק. טבלה 1 מפרטת את פעולות המשחק המומלצות לפעולות הנפוצות ביותר
לחצני בקר משחקים.
טבלה 1. פעולות מומלצות במשחק לגיימפאד הלחצנים.
פעולה במשחק | קוד מפתח לחצן |
---|---|
התחלת המשחק בתפריט הראשי, או השהיה או ביטול השהיה במהלך המשחק | BUTTON_START * |
הצגת התפריט | BUTTON_SELECT *
ו-KEYCODE_MENU * |
זהה להתנהגות הניווט הקודם של Android שמתוארת עיצוב ניווט מותאמת אישית. | KEYCODE_BACK |
ניווט חזרה לפריט קודם בתפריט | BUTTON_B |
אישור הבחירה או ביצוע פעולה ראשית במשחק | BUTTON_A והקבוצה
DPAD_CENTER |
* אסור שהמשחק יסתמך על הלחצנים 'התחלה', 'בחירה' או 'תפריט' לחצנים.
טיפ: כדאי להגדיר מסך הגדרה במשחק שלכם כדי לאפשר למשתמשים להתאים אישית את המיפויים של בקר המשחקים שלהם פעולות במשחק.
קטע הקוד הבא מראה איך אפשר לשנות
onKeyDown()
עד
לשייך את BUTTON_A
לחיצות על הלחצן DPAD_CENTER
באמצעות פעולה במשחק.
Kotlin
class GameView(...) : View(...) { ... override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { var handled = false if (event.source and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD) { if (event.repeatCount == 0) { when (keyCode) { // Handle gamepad and D-pad button presses to navigate the ship ... else -> { keyCode.takeIf { isFireKey(it) }?.run { // Update the ship object to fire lasers ... handled = true } } } } if (handled) { return true } } return super.onKeyDown(keyCode, event) } // Here we treat Button_A and DPAD_CENTER as the primary action // keys for the game. private fun isFireKey(keyCode: Int): Boolean = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A }
Java
public class GameView extends View { ... @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean handled = false; if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) { if (event.getRepeatCount() == 0) { switch (keyCode) { // Handle gamepad and D-pad button presses to // navigate the ship ... default: if (isFireKey(keyCode)) { // Update the ship object to fire lasers ... handled = true; } break; } } if (handled) { return true; } } return super.onKeyDown(keyCode, event); } private static boolean isFireKey(int keyCode) { // Here we treat Button_A and DPAD_CENTER as the primary action // keys for the game. return keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_BUTTON_A; } }
הערה: ב-Android 4.2 (API)
ברמה 17 ומטה, המערכת מתייחסת
BUTTON_A
בתור Android
כברירת מחדל, המקש הקודם. אם האפליקציה תומכת במכשירי Android האלה
חשוב לטפל
BUTTON_A
בתור המשחק הראשי
פעולה. כדי לבדוק מהי גרסת ה-SDK הנוכחית של Android
במכשיר עצמו, קראו את
ערך של Build.VERSION.SDK_INT
.
עיבוד קלט לחצנים כיווניים
לחצני החיצים (D-pad) הם אמצעי בקרה פיזי נפוץ במשחקים רבים
לנאמני מידע. מערכת Android מדווחת על לחצני החיצים למעלה ולמטה בתור
AXIS_HAT_Y
אירועים עם טווח
מ-1.0- (למעלה) ל-1.0 (למטה), ולחצני החיצים LEFT או RIGHT מקישים בתור
AXIS_HAT_X
אירועים עם טווח של -1.0
(משמאל) ל-1.0 (ימין).
במקום זאת, חלק מהבקרים מדווחים על לחיצות בלחצני החיצים עם קוד מפתח. אם המשחק שלך לגבי לחיצות עם מקשי החיצים, צריך לטפל באירועים בציר הכובע ובלחצני החיצים (D-pad) לקודי מפתח כמו אותם אירועי קלט, כפי המומלץ בטבלה 2.
טבלה 2. פעולות ברירת מחדל מומלצות במשחק למקש D-pad והערכים של ציר הכובע.
פעולה במשחק | קוד מקש D-pad | קוד ציר הכובע |
---|---|---|
הזזה למעלה | KEYCODE_DPAD_UP |
AXIS_HAT_Y (לערכים 0 עד 1.0) |
הזזה למטה | KEYCODE_DPAD_DOWN |
AXIS_HAT_Y (לערכים 0 עד 1.0) |
הזזה שמאלה | KEYCODE_DPAD_LEFT |
AXIS_HAT_X (לערכים 0 עד 1.0) |
הזזה ימינה | KEYCODE_DPAD_RIGHT |
AXIS_HAT_X (לערכים 0 עד 1.0) |
בקטע הקוד הבא מוצגת מחלקה מסייעת שמאפשרת לבדוק את הכובע של הציר וקוד המפתח מאירוע קלט כדי לקבוע את הכיוון של לחצני החיצים (D-pad).
Kotlin
class Dpad { private var directionPressed = -1 // initialized to -1 fun getDirectionPressed(event: InputEvent): Int { if (!isDpadDevice(event)) { return -1 } // If the input event is a MotionEvent, check its hat axis values. (event as? MotionEvent)?.apply { // Use the hat axis value to find the D-pad direction val xaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_X) val yaxis: Float = event.getAxisValue(MotionEvent.AXIS_HAT_Y) directionPressed = when { // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad // LEFT and RIGHT direction accordingly. xaxis.compareTo(-1.0f) == 0 -> Dpad.LEFT xaxis.compareTo(1.0f) == 0 -> Dpad.RIGHT // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad // UP and DOWN direction accordingly. yaxis.compareTo(-1.0f) == 0 -> Dpad.UP yaxis.compareTo(1.0f) == 0 -> Dpad.DOWN else -> directionPressed } } // If the input event is a KeyEvent, check its key code. (event as? KeyEvent)?.apply { // Use the key code to find the D-pad direction. directionPressed = when(event.keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> Dpad.LEFT KeyEvent.KEYCODE_DPAD_RIGHT -> Dpad.RIGHT KeyEvent.KEYCODE_DPAD_UP -> Dpad.UP KeyEvent.KEYCODE_DPAD_DOWN -> Dpad.DOWN KeyEvent.KEYCODE_DPAD_CENTER -> Dpad.CENTER else -> directionPressed } } return directionPressed } companion object { internal const val UP = 0 internal const val LEFT = 1 internal const val RIGHT = 2 internal const val DOWN = 3 internal const val CENTER = 4 fun isDpadDevice(event: InputEvent): Boolean = // Check that input comes from a device with directional pads. event.source and InputDevice.SOURCE_DPAD != InputDevice.SOURCE_DPAD } }
Java
public class Dpad { final static int UP = 0; final static int LEFT = 1; final static int RIGHT = 2; final static int DOWN = 3; final static int CENTER = 4; int directionPressed = -1; // initialized to -1 public int getDirectionPressed(InputEvent event) { if (!isDpadDevice(event)) { return -1; } // If the input event is a MotionEvent, check its hat axis values. if (event instanceof MotionEvent) { // Use the hat axis value to find the D-pad direction MotionEvent motionEvent = (MotionEvent) event; float xaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_X); float yaxis = motionEvent.getAxisValue(MotionEvent.AXIS_HAT_Y); // Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad // LEFT and RIGHT direction accordingly. if (Float.compare(xaxis, -1.0f) == 0) { directionPressed = Dpad.LEFT; } else if (Float.compare(xaxis, 1.0f) == 0) { directionPressed = Dpad.RIGHT; } // Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad // UP and DOWN direction accordingly. else if (Float.compare(yaxis, -1.0f) == 0) { directionPressed = Dpad.UP; } else if (Float.compare(yaxis, 1.0f) == 0) { directionPressed = Dpad.DOWN; } } // If the input event is a KeyEvent, check its key code. else if (event instanceof KeyEvent) { // Use the key code to find the D-pad direction. KeyEvent keyEvent = (KeyEvent) event; if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) { directionPressed = Dpad.LEFT; } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) { directionPressed = Dpad.RIGHT; } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_UP) { directionPressed = Dpad.UP; } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) { directionPressed = Dpad.DOWN; } else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) { directionPressed = Dpad.CENTER; } } return directionPressed; } public static boolean isDpadDevice(InputEvent event) { // Check that input comes from a device with directional pads. if ((event.getSource() & InputDevice.SOURCE_DPAD) != InputDevice.SOURCE_DPAD) { return true; } else { return false; } } }
אפשר להשתמש בכיתה העוזרת הזו במשחק בכל מקום שרוצים לעבד
קלט D-pad (לדוגמה,
onGenericMotionEvent()
או
onKeyDown()
קריאות חוזרות (callbacks).
לדוגמה:
Kotlin
private val dpad = Dpad() ... override fun onGenericMotionEvent(event: MotionEvent): Boolean { if (Dpad.isDpadDevice(event)) { when (dpad.getDirectionPressed(event)) { Dpad.LEFT -> { // Do something for LEFT direction press ... return true } Dpad.RIGHT -> { // Do something for RIGHT direction press ... return true } Dpad.UP -> { // Do something for UP direction press ... return true } ... } } // Check if this event is from a joystick movement and process accordingly. ... }
Java
Dpad dpad = new Dpad(); ... @Override public boolean onGenericMotionEvent(MotionEvent event) { // Check if this event if from a D-pad and process accordingly. if (Dpad.isDpadDevice(event)) { int press = dpad.getDirectionPressed(event); switch (press) { case LEFT: // Do something for LEFT direction press ... return true; case RIGHT: // Do something for RIGHT direction press ... return true; case UP: // Do something for UP direction press ... return true; ... } } // Check if this event is from a joystick movement and process accordingly. ... }
עיבוד התנועות בג'ויסטיק
כששחקנים מזיזים ג'ויסטיק בשלטי המשחק שלהם, מערכת Android מדווחת על
MotionEvent
שמכיל את
קוד הפעולה ACTION_MOVE
והגרסה המעודכנת
את הצירים של הג'ויסטיק. המשחק שלך יכול להשתמש בנתונים שסופקו על ידי
MotionEvent
כדי לקבוע אם ג'ויסטיק מזיז אותו
שאכפת לך ממנו.
חשוב לשים לב שאירועים של תנועת ג'ויסטיק עשויים לקבץ יחד מספר דוגמאות תנועה
בתוך אובייקט יחיד. האובייקט MotionEvent
מכיל
את המיקום הנוכחי של כל ציר ג'ויסטיק וגם את המיקום הנוכחי של כל אחד מצירי הג'ויסטיק
במיקומים השונים בכל ציר. כשמדווחים על אירועי תנועה עם קוד הפעולה ACTION_MOVE
(כמו תנועות בג'ויסטיק), מערכת Android מקבץ את
בצירים של היעילות. הערכים ההיסטוריים של ציר כוללים את
קבוצה של ערכים ייחודיים ישנים יותר מהערך הנוכחי של הציר, ועדכניים יותר מ-
ערכים שדווחו באירועי תנועה קודמים. לצפייה
לפרטים נוספים, אפשר להיעזר ב-MotionEvent
.
תוכלו להשתמש במידע ההיסטורי כדי לעבד את המשחק בצורה מדויקת יותר
על סמך קלט הג'ויסטיק. שפת תרגום
מאחזרים את הערכים הנוכחיים וההיסטוריים,
getAxisValue()
או getHistoricalAxisValue()
. אפשר גם למצוא את מספר הנתונים
נקודות באירוע הג'ויסטיק באמצעות התקשרות
getHistorySize()
קטע הקוד הבא מראה איך אפשר לשנות את
קריאה חוזרת של onGenericMotionEvent()
לעיבוד קלט של ג'ויסטיק. אתם צריכים קודם
לעבד את הערכים ההיסטוריים של ציר מסוים, לעבד את המיקום הנוכחי שלו.
Kotlin
class GameView(...) : View(...) { override fun onGenericMotionEvent(event: MotionEvent): Boolean { // Check that the event came from a game controller return if (event.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK && event.action == MotionEvent.ACTION_MOVE) { // Process the movements starting from the // earliest historical position in the batch (0 until event.historySize).forEach { i -> // Process the event at historical position i processJoystickInput(event, i) } // Process the current movement sample in the batch (position -1) processJoystickInput(event, -1) true } else { super.onGenericMotionEvent(event) } } }
Java
public class GameView extends View { @Override public boolean onGenericMotionEvent(MotionEvent event) { // Check that the event came from a game controller if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE) { // Process all historical movement samples in the batch final int historySize = event.getHistorySize(); // Process the movements starting from the // earliest historical position in the batch for (int i = 0; i < historySize; i++) { // Process the event at historical position i processJoystickInput(event, i); } // Process the current movement sample in the batch (position -1) processJoystickInput(event, -1); return true; } return super.onGenericMotionEvent(event); } }
לפני השימוש בקלט הג'ויסטיק, עליך לקבוע אם הג'ויסטיק במרכז, וחשב את תנועות הציר שלו בהתאם. ג'ויסטיקים בדרך כלל כוללים שטח שטוח, כלומר טווח ערכים ליד הקואורדינטה (0,0) שבו הציר נחשב למרכז. אם הערך של הציר מדווח לפי Android נמצא בתוך האזור השטוח, עליך להתייחס לבקר מנוחה (כלומר, ללא תנועה לאורך שני הצירים).
בקטע הבא מוצגת שיטה מסייעת לחישוב התנועה לאורך
בכל ציר. הפעלת כלי העזר הזה בשיטה processJoystickInput()
שמתואר בהמשך.
Kotlin
private fun getCenteredAxis( event: MotionEvent, device: InputDevice, axis: Int, historyPos: Int ): Float { val range: InputDevice.MotionRange? = device.getMotionRange(axis, event.source) // A joystick at rest does not always report an absolute position of // (0,0). Use the getFlat() method to determine the range of values // bounding the joystick axis center. range?.apply { val value: Float = if (historyPos < 0) { event.getAxisValue(axis) } else { event.getHistoricalAxisValue(axis, historyPos) } // Ignore axis values that are within the 'flat' region of the // joystick axis center. if (Math.abs(value) > flat) { return value } } return 0f }
Java
private static float getCenteredAxis(MotionEvent event, InputDevice device, int axis, int historyPos) { final InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource()); // A joystick at rest does not always report an absolute position of // (0,0). Use the getFlat() method to determine the range of values // bounding the joystick axis center. if (range != null) { final float flat = range.getFlat(); final float value = historyPos < 0 ? event.getAxisValue(axis): event.getHistoricalAxisValue(axis, historyPos); // Ignore axis values that are within the 'flat' region of the // joystick axis center. if (Math.abs(value) > flat) { return value; } } return 0; }
כך אפשר לעבד תנועות ג'ויסטיק המשחק שלך:
Kotlin
private fun processJoystickInput(event: MotionEvent, historyPos: Int) { val inputDevice = event.device // Calculate the horizontal distance to move by // using the input value from one of these physical controls: // the left control stick, hat axis, or the right control stick. var x: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos) if (x == 0f) { x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos) } if (x == 0f) { x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos) } // Calculate the vertical distance to move by // using the input value from one of these physical controls: // the left control stick, hat switch, or the right control stick. var y: Float = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos) if (y == 0f) { y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos) } if (y == 0f) { y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos) } // Update the ship object based on the new x and y values }
Java
private void processJoystickInput(MotionEvent event, int historyPos) { InputDevice inputDevice = event.getDevice(); // Calculate the horizontal distance to move by // using the input value from one of these physical controls: // the left control stick, hat axis, or the right control stick. float x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos); if (x == 0) { x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos); } if (x == 0) { x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos); } // Calculate the vertical distance to move by // using the input value from one of these physical controls: // the left control stick, hat switch, or the right control stick. float y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos); if (y == 0) { y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos); } if (y == 0) { y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos); } // Update the ship object based on the new x and y values }
לתמוך בבקרי משחקים עם תמיכה מתוחכמת יותר לא לג'ויסטיק אחד, כדאי לפעול לפי השיטות המומלצות הבאות:
- טיפול במדבקות עם שני בקרים. לבקרים רבים של משחקים יש
ג'ויסטיק שמאלה וימינה. עבור המקל השמאלי, Android
מדווח על תנועות אופקיות כאירועי
AXIS_X
ותנועות אנכיות כאירועיAXIS_Y
. עבור המקל הימני, מערכת Android מדווחת על תנועות אופקיות בתורAXIS_Z
אירועים ותנועות אנכיות בתורAXIS_RZ
אירועים. חשוב להשתמש בכינוי ושני הבקרים נתקעו בקוד. - לחיצות על הכתפיים בכתפיים (אבל מספקות קלט חלופי
). לחלק מהבקרים יש כתף שמאלי ואוזן ימין
טריגרים. אם הטריגרים האלה קיימים, מערכת Android מדווחת על טריגר שמאלי בלחיצה
כאירוע
AXIS_LTRIGGER
לחיצה על טריגר ימני אירועAXIS_RTRIGGER
. ב-Android 4.3 (רמת API 18), בקר שמפיקAXIS_LTRIGGER
מדווח גם על ערך זהה עבור הצירAXIS_BRAKE
. אותו עיקרון נכון גם לגביAXIS_RTRIGGER
AXIS_GAS
. המערכת של Android מדווחת על כל הטריגרים האנלוגיים לוחץ עם ערך מנורמל מ-0.0 (פורסם) ל-1.0 (בלחיצה מלאה). לא לכל הבקרים יש טריגרים, לכן כדאי לאפשר לשחקנים לבצע אותם פעולות במשחק באמצעות לחצנים אחרים.