הטמעת בקרת מד תאוצה למשחקי מובייל
הטיית הטלפון כשלט משחק — שיטת שליטה אינטואיטיבית למשחקי מרוצים, ארקייד ומבוכים. מפתחים רבים משלבים אותה ביום אחד, ואז מבלים שבועות בליטוש: החלקת השהיה, מאבק בסחיפה, הגדרת אזורים מתים וכיול. חריפות במיוחד הן בעיות סחיפת גירוסקופ וקריאות לא יציבות בין מכשירים. אנו מציעים הטמעה סוהר עם היענות מובטחת והתנהגות צפויה בכל חומרה. נבחן את הפרויקט שלך ביום אחד. צור קשר לקבלת הדגמה.
מיזוג חיישנים נכון נותן איכות שליטה טובה פי 10 מאשר מד תאוצה גולמי. מודול מוכן חותך עלויות פיתוח פי 2–3 בהשוואה להטמעה פנימית — אתה חוסך תקציב על מו"פ ומתמקד במשחקיות. לפי תיעוד Apple Core Motion, שילוב של מד תאוצה, גירוסקופ ומגנומטר מניב את התוצאות הטובות ביותר.
למה מד תאוצה גולמי לא עובד
מד תאוצה גולמי כולל כוח משיכה. על שולחן שטוח: (x: 0, y: 0, z: -9.81). כשמטים את המכשיר, וקטור הכבידה מתפזר על פני הצירים, ושובר את השליטה. המקור הנכון הוא Device Motion / Linear Acceleration — נתונים ללא כוח משיכה. אבל יש בו רעש וסחיפת גירוסקופ איטית. מיזוג חיישנים משלב מד תאוצה, גירוסקופ ומגנומטר כדי לקבל זווית הטיה נקייה. ב-iOS זה CoreMotion, באנדרואיד — SensorManager עם אלגוריתם Rotation Vector.
איך להימנע מסחיפת גירוסקופ?
סחיפת גירוסקופ נובעת מאינטגרציה של מהירות זוויתית. הפתרון הוא לשלב אותו עם מד התאוצה (מסנן משלים או מסנן Kalman). ב-iOS אנו משתמשים ב-CMAttitude עם xArbitraryZVertical, באנדרואיד — getRotationMatrixFromVector. לסצנות קריטיות אנו משלבים גם את המגנומטר.
מסנן משלים לעומת מסנן Kalman
כדי להחליק נתוני חיישנים, משתמשים בשתי גישות עיקריות: מסנן משלים ומסנן Kalman. המסנן המשלים (alpha = 0.98 לגירוסקופ, 0.02 למד תאוצה) פשוט ליישום ונותן השהיה של <5 אלפיות שנייה ב-iOS. מסנן Kalman מדויק ב-30% יותר אך יקר יותר מבחינה חישובית. אנו בוחרים לפי ז'אנר: ליריות — Kalman, למשחקים מזדמנים — משלים.
| פרמטר | משלים | Kalman |
|---|---|---|
| דיוק | גבוה | גבוה מאוד |
| השהיה | <5 אלפיות שנייה | ~10 אלפיות שנייה |
| מורכבות | נמוכה | בינונית |
| ביצועים | מהיר | תובעני |
הטמעה ב-iOS (Swift)
let motionManager = CMMotionManager()
motionManager.deviceMotionUpdateInterval = 1.0 / 60.0
motionManager.startDeviceMotionUpdates(
using: .xArbitraryZVertical,
to: OperationQueue.main
) { [weak self] motion, _ in
guard let motion = motion else { return }
self?.applyTilt(
pitch: Float(motion.attitude.pitch),
roll: Float(motion.attitude.roll)
)
} הטמעה באנדרואיד (Kotlin)
private var baselineAttitude: FloatArray? = null
private val currentRotationMatrix = FloatArray(16)
// В SensorEventListener.onSensorChanged для TYPE_ROTATION_VECTOR:
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
val orientationAngles = FloatArray(3)
SensorManager.getOrientation(rotationMatrix, orientationAngles)
val pitch = orientationAngles[1]
val roll = orientationAngles[2]
val calibratedPitch = pitch - (baselineAttitude?.get(0) ?: 0f)
val calibratedRoll = roll - (baselineAttitude?.get(1) ?: 0f)
gameEngine.setTilt(calibratedPitch, calibratedRoll) איך לבצע כיול?
כיול מתקן את מיקום האפס בתחילת השימוש או בלחיצת כפתור. בלעדיו, השליטה תהיה מוסטת.
fun calibrate() { baselineAttitude = floatArrayOf(currentPitch, currentRoll) } שמור את קו הבסיס ב-SharedPreferences — כדי להימנע מכיול מחדש בהפעלה הבאה.
אלגוריתם כיול מפורט
- לכידת מיקום אפס (pitch=0, roll=0).
- שמירת קו בסיס (ממוצע על פני 100 אלפיות שנייה).
- הפחתת קו הבסיס מהזוויות הנוכחיות.
- הפעלת מסנן מעביר נמוך על ההפרש.
- עדכון קו הבסיס בכל הפעלה חדשה.
החלקה: מסנן מעביר נמוך ואזור מת
מסנן אקספוננציאלי פשוט מסיר רעידות יד. אזור מת של ±5° מונע תנועה לא רצויה.
struct LowPassFilter {
var value: Float = 0
let alpha: Float
mutating func update(_ newValue: Float) -> Float {
value = alpha * newValue + (1 - alpha) * value
return value
}
}
func applyDeadZone(_ value: Float, threshold: Float = 0.087) -> Float {
guard abs(value) > threshold else {
return 0
}
let sign: Float = value > 0 ? 1 : -1
return sign * (abs(value) - threshold)
}רגישות לא ליניארית (פונקציית חזקה) נותנת שליטה מדויקת בזוויות קטנות ותגובה מהירה בזוויות גדולות.
השוואה: iOS לעומת אנדרואיד
| פרמטר | iOS (CoreMotion) | אנדרואיד (Rotation Vector) |
|---|---|---|
| השהיה | ~5–10 אלפיות שנייה | ~10–15 אלפיות שנייה |
| כיול | מובנה | ידני דרך קו בסיס |
| סחיפה | מינימלית | מתוגמלת על ידי מגנומטר |
| קלות אינטגרציה | גבוהה | בינונית |
פרמטרים לז'אנרים שונים
| ז'אנר | Alpha (מעביר נמוך) | אזור מת | אי-ליניאריות |
|---|---|---|---|
| מרוצים | 0.3-0.4 | ±5° | 1.5 |
| ארקייד | 0.2 | ±3° | 1.2 |
| יריות | 0.6-0.7 | ±2° | 1.0 |
תהליך אינטגרציה
- אתחול חיישן — 60 הרץ.
- איסוף נתונים — pitch/roll, תיקון כיוון.
- כיול — מיקום אפס.
- סינון — מעביר נמוך.
- אזור מת — ביטול רעידות.
- רגישות לא ליניארית — מיפוי זווית.
- פלט למנוע המשחק.
מה כלול
- קוד מקור למודול (Swift, Kotlin, C#).
- תיעוד אינטגרציה.
- המלצות לבדיקת מכשירים.
- תמיכה במהלך הגשה לחנות.
- כוונון עדין לפי ז'אנר.
לוח זמנים
שליטה בסיסית — 3–5 ימי עבודה. עם ליטוש לפי ז'אנר — 1–2 שבועות. צור קשר להערכה מדויקת — ניסיון של מעל 10 שנים מבטיח איכות. קבל הדגמה ובחן את השליטה.







