1. לפני שמתחילים
מופשט
שיעור Lab זה מלמד אתכם איך להשתמש בנתונים מפלטפורמה של מפות Google כדי להציג מקומות בסביבה במציאות רבודה (AR) ב-Android.
דרישות מוקדמות
- הבנה בסיסית של פיתוח ב-Android באמצעות Android Studio
- היכרות עם קוטלין
מה תלמדו
- בקשת הרשאה מהמשתמש לגשת למצלמה ולמיקום של המכשיר.
- שילוב עם places API כדי לשלוף מקומות בקרבת מקום סביב המכשיר.
- משתלבים עם ARCore כדי למצוא משטחים אופקיים במטוס, כך שאובייקטים וירטואליים יוכלו לעגן אותם ולמקם אותם בחלל תלת-ממדי באמצעות תרחיש.
- אוספים מידע על מיקום המכשיר בחלל באמצעות SensorManager ומשתמשים ב-Maps SDK for Android Utility Directory כדי למקם אובייקטים וירטואליים בכותרת הנכונה.
מה צריך?
- Android Studio מגרסה 2020.3.1 ואילך
- מכונה לפיתוח התומכת ב-OpenGL ES 3.0 ומעלה
- מכשיר התומך ב-ARCore או אמולטור Android התומך ב-ARCore (ההוראות מפורטות בשלב הבא)
2. להגדרה
Android Studio
Lablab זה משתמש ב-Android 10.0 (רמת API 29) ומחייב התקנה של שירותי Google Play ב-Android Studio. כדי להתקין את שתי התלות האלה, צריך לבצע את השלבים הבאים:
- עוברים אל מנהל ה-SDK. כדי לגשת אליו, לוחצים על כלים >. SDK Manager.
- בודקים אם Android 10.0 מותקן. אם לא, מתקינים את התיבה על ידי סימון התיבה לצד Android 10.0 (Q) . לאחר מכן לוחצים על אישור ולבסוף לוחצים שוב על אישור בתיבת הדו-שיח שמופיעה.
- לסיום, מתקינים את שירותי Google Play דרך הכרטיסייה כלי SDK, מסמנים את התיבה לצד Google Play Services, לוחצים על אישור ולאחר מכן בוחרים שוב באישור בתיבת הדו-שיח שמופיעה**.**
ממשקי API נדרשים
בשלב 3 של הקטע הבא, יש להפעיל את SDK של מפות Google ל-Android ואת API של מקומות Google למעבד הקוד הזה.
תחילת העבודה עם הפלטפורמה של מפות Google
אם לא השתמשתם בעבר בפלטפורמה של מפות Google, יש לבצע את המדריך לתחילת העבודה עם מפות Google או לצפות בפלייליסט של הפלטפורמה של מפות Google כדי להשלים את השלבים הבאים:
- יוצרים חשבון לחיוב.
- יוצרים פרויקט.
- הפעלת ממשקי API וערכות SDK של מפות Google (מפורט בקטע הקודם).
- יצירת מפתח API.
אופציונלי: אמולטור Android
אם אין לך מכשיר עם ARCore, אפשר להשתמש באמולטור של Android כדי לדמות סצנה AR וגם לזייף את מיקום המכשיר. בתרגיל הזה עליכם להשתמש גם בתרחיש. עליכם גם לבצע את השלבים המפורטים בקטע ה&שקה (האמולטור) כדי לתמוך בסצנה.
3. התחלה מהירה
כדי לעזור לך להתחיל מהר ככל האפשר, הנה קוד למתחילים שיעזור לך לעקוב אחר שיעור Lab זה. אתם מוזמנים לדלג לפתרון, אבל אם אתם רוצים לראות את כל השלבים, המשיכו לקרוא.
אפשר לשכפל את המאגר אם התקנת את git
.
git clone https://github.com/googlecodelabs/display-nearby-places-ar-android.git
לחלופין, אפשר ללחוץ על הלחצן שלמטה כדי להוריד את קוד המקור.
אחרי קבלת הקוד, צריך לפתוח את הפרויקט שנמצא בספרייה של starter
.
4. סקירה כללית של הפרויקט
בודקים את הקוד שהורדתם מהשלב הקודם. בתוך מאגר זה צריך להופיע מודול אחד בשם app
, שמכיל את החבילה com.google.codelabs.findnearbyplacesar
.
AndroidManifest.xml
המאפיינים הבאים מוצהרים בקובץ AndroidManifest.xml
כדי לאפשר לך להשתמש בתכונות הנדרשות ב-codelab זה:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Sceneform requires OpenGL ES 3.0 or later. -->
<uses-feature
android:glEsVersion="0x00030000"
android:required="true" />
<!-- Indicates that app requires ARCore ("AR Required"). Ensures the app is visible only in the Google Play Store on devices that support ARCore. For "AR Optional" apps remove this line. -->
<uses-feature android:name="android.hardware.camera.ar" />
עבור uses-permission
, המדיניות הזו מציינת אילו הרשאות יש להעניק למשתמש לפני שניתן יהיה להשתמש ביכולות האלה. אלה הצהרות:
android.permission.INTERNET
— ההרשאה הזו מאפשרת לאפליקציה לבצע פעולות ברשת ולאחזר נתונים באינטרנט, כמו מידע על מקומות דרך API של מקומות Google.android.permission.CAMERA
— נדרשת גישה למצלמה כדי שניתן יהיה להשתמש במצלמה של המכשיר כדי להציג אובייקטים במציאות רבודה.android.permission.ACCESS_FINE_LOCATION
— נדרשת גישה למיקום כדי שניתן יהיה לאחזר מקומות בקרבת מקום ביחס למיקום של המכשיר.
עבור uses-feature
, המציין אילו תכונות חומרה נדרשות על ידי האפליקציה הזו, הנה ההצהרה על:
- יש צורך ב-OpenGL ES בגרסה 3.0.
- נדרש מכשיר התומך ב-ARCore.
בנוסף, תגי המטא נתונים הבאים נוספים מתחת לאובייקט האפליקציה:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--
Indicates that this app requires Google Play Services for AR ("AR Required") and causes
the Google Play Store to download and install Google Play Services for AR along with
the app. For an "AR Optional" app, specify "optional" instead of "required".
-->
<meta-data
android:name="com.google.ar.core"
android:value="required" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<!-- Additional elements here -->
</application>
רשומת המטא-נתונים הראשונה היא לציין ש-ARCore היא דרישה להפעלת האפליקציה, והשנייה היא כיצד לספק את מפתח ה-API של מפות Google עבור ה-SDK של מפות Google ל-Android.
build.gradle
ב-build.gradle
, תלויות הנוספות הבאות:
dependencies {
// Maps & Location
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.maps.android:maps-utils-ktx:1.7.0'
// ARCore
implementation "com.google.ar.sceneform.ux:sceneform-ux:1.15.0"
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.7.1"
implementation "com.squareup.retrofit2:converter-gson:2.7.1"
}
זהו תיאור קצר של כל תלות:
- הספריות עם מזהה הקבוצה
com.google.android.gms
, כלומרplay-services-location
ו-play-services-maps
, משמשות לגישה לפרטי המיקום של המכשיר ולפונקציונליות של גישה שקשורה למפות Google. com.google.maps.android:maps-utils-ktx
היא ספריית התוספים של Kotlin (KTX) ל-SDK של מפות Google ל-Android Utility Directory. הפונקציות ישמשו בספרייה הזו כדי למקם מאוחר יותר אובייקטים וירטואליים בחלל אמיתי.com.google.ar.sceneform.ux:sceneform-ux
היא ספריית סצנה, שתאפשר לך לעבד סצנות תלת-ממדיות מציאותיות בלי ללמוד את OpenGL.- התלויות במזהה הקבוצה
com.squareup.retrofit2
הן תלויות Retrofit, שמאפשרות לך לכתוב במהירות לקוח HTTP כדי לקיים אינטראקציה עם ה-API של 'מקומות'.
מבנה הפרויקט
כאן נמצאות החבילות והקבצים הבאים:
- **API—**חבילה זו כוללת כיתות המשמשות לאינטראקציה עם ממשק ה-API של 'מקומות' באמצעות Retrofit.
- **ar—**החבילה הזו מכילה את כל הקבצים הקשורים ל-ARCore.
- **מודל—**חבילה זו כוללת מחלקת נתונים אחת
Place
, המשמשת למקיף מקום אחד שמוחזר על ידי ה-API של 'מקומות'. - MainActivity.kt – זו האפליקציה
Activity
היחידה הכלולה באפליקציה, שמציגה מפה ותצוגת מצלמה.
5. הגדרת הסצנה
מתעמקים ברכיבים הבסיסיים של האפליקציה מתחילים בחלקים של המציאות רבודה.
השדה MainActivity
מכיל SupportMapFragment
, שבו תוצג אובייקט המפה וסיווג משנה של ArFragment
— PlacesArFragment
, שמציג את סצנת המציאות רבודה.
הגדרת מציאות רבודה
פרט להצגת סצינת המציאות רבודה, PlacesArFragment
יטפל גם בבקשה להרשאת מצלמה מהמשתמש אם הוא לא אושר על ידיך. ניתן לבקש הרשאות נוספות על ידי שינוי השיטה getAdditionalPermissions
. בנוסף, כדי לקבל הרשאה למיקום, יש לציין זאת ולבטל את השיטה getAdditionalPermissions
:
class PlacesArFragment : ArFragment() {
override fun getAdditionalPermissions(): Array<String> =
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
.toTypedArray()
}
להפעלה
עכשיו צריך לפתוח את קוד השלד בספרייה starter
ב-Android Studio. אם לוחצים על הפעלה > מפעילים את 'app'' מסרגל הכלים ופורסים את האפליקציה למכשיר או לאמולטור. אמורה להופיע בקשה להפעיל את הרשאת המיקום והמצלמה. לאחר מכן לוחצים על אישור. כשעושים זאת, אמורה להופיע תצוגת מצלמה ותצוגת מפה זה לצד זה:
זיהוי מטוסים
כשאתם בוחנים את הסביבה שבה אתם נמצאים עם המצלמה שלכם, אתם עשויים לראות שתי נקודות לבנות על המשטחים, כמו למשל הנקודות הלבנות על השטיח בתמונה.
הנקודות הלבנות האלה הן הנחיות של ARCore שמצביעות על זיהוי של מטוס אופקי. המטוסים המזוהים האלה מאפשרים לכם ליצור מה ש&יקוּט;&r&עוגן; כך תוכלו למקם אובייקטים וירטואליים בחלל אמיתי.
כדי לקבל מידע נוסף על ARCore ועל האופן שבו הוא מבין את הסביבה שלכם, כדאי לקרוא את המאמר על העקרונות הבסיסיים שלו.
6. קבלת מקומות קרובים
בשלב הבא יהיה עליך לגשת למיקום הנוכחי של המכשיר ולהציג אותו, ולאחר מכן לשלוף מקומות בקרבת מקום באמצעות API של מקומות Google.
הגדרת מפות
מפתח API של מפות Google
מוקדם יותר, יצרת מפתח ל-API של מפות Google כדי להפעיל שאילתות בממשק API של מקומות ולאפשר לך להשתמש ב-SDK של מפות Google ל-Android. יש לפתוח את הקובץ gradle.properties
ולהחליף את המחרוזת "YOUR API KEY HERE"
במפתח ה-API שיצרת.
הצגת המיקום של המכשיר במפה
לאחר הוספת מפתח ה-API, ניתן להוסיף עוזר דיגיטלי במפה כדי להנחות את המשתמשים במיקום היחסי שלהם במפה. כדי לעשות זאת, יש לעבור לשיטה setUpMaps
ובתוך השיחה mapFragment.getMapAsync
להגדיר את googleMap.isMyLocationEnabled
כ-true.
. פעולה זו תציג את הנקודה הכחולה במפה.
private fun setUpMaps() {
mapFragment.getMapAsync { googleMap ->
googleMap.isMyLocationEnabled = true
// ...
}
}
קבלת המיקום הנוכחי
כדי לקבל את מיקום המכשיר, יש להשתמש בשיעור FusedLocationProviderClient
. כבר התקבלה מופע כזה בשיטה onCreate
של MainActivity
. כדי להשתמש באובייקט הזה, עליך למלא את שיטת getCurrentLocation
ולאשר ארגומנט למדה כדי להעביר מיקום למתקשר בשיטה הזו.
כדי להשלים את השיטה הזו, אפשר לגשת לנכס lastLocation
של האובייקט FusedLocationProviderClient
ואז להוסיף addOnSuccessListener
באופן הבא:
fusedLocationClient.lastLocation.addOnSuccessListener { location ->
currentLocation = location
onSuccess(location)
}.addOnFailureListener {
Log.e(TAG, "Could not get location")
}
מתבצעת קריאה לשיטה getCurrentLocation
מתוך למבדה שסופקה ב-getMapAsync
בשיטת setUpMaps
שממנה נשלפים המקומות הקרובים.
התחלת שיחה ברשת עבור מקומות
בקריאה לשיטה getNearbyPlaces
, חשוב לשים לב שהפרמטרים הבאים מועברים לשיטה placesServices.nearbyPlaces
– מפתח API, מיקום המכשיר, רדיוס במטרים (שמוגדר ל-2 ק"מ) וסוג מקום (כרגע מוגדר כ-park
).
val apiKey = "YOUR API KEY"
placesService.nearbyPlaces(
apiKey = apiKey,
location = "${location.latitude},${location.longitude}",
radiusInMeters = 2000,
placeType = "park"
)
כדי להשלים את השיחה ברשת, יש להעביר את מפתח ה-API שהגדרת בקובץ gradle.properties
. קטע הקוד הבא מוגדר בקובץ build.gradle
שלך בהגדרות android > defaultConfig:
android {
defaultConfig {
resValue "string", "google_maps_key", (project.findProperty("GOOGLE_MAPS_API_KEY") ?: "")
}
}
בעקבות זאת, ערך משאב המחרוזת google_maps_key
יהיה זמין בזמן הבנייה.
כדי להשלים את השיחה ברשת, אפשר לקרוא את משאב המחרוזת הזה דרך getString
באובייקט Context
.
val apiKey = this.getString(R.string.google_maps_key)
7. מקומות ב-AR
עד עכשיו, ביצעת את הפעולות הבאות:
- המשתמש ביקש הרשאת גישה למצלמה ולמיקום כשהוא הפעיל את האפליקציה בפעם הראשונה
- צריך להגדיר את ARCore כדי להתחיל לעקוב אחר מטוסים אופקיים
- הגדרת ה-SDK של מפות Google באמצעות מפתח ה-API
- מתבצע איתור של המיקום הנוכחי של המכשיר
- אחזור מקומות בקרבת מקום (במיוחד פארקים) באמצעות ה-API של מקומות Google
השלב שנותר להשלמת תרגיל זה הוא למקם את המקומות שאתה מאחזר במציאות רבודה.
הבנת סביבת העבודה
באמצעות ARCore ניתן לזהות את הסצנה בעולם האמיתי דרך המצלמה של המכשיר. לשם כך, היא מזהה נקודות מעניינות וייחודיות שנקראות "נקודות" בכל מסגרת של תמונה. כשנקודות אלה מקובצות ומופיעות במטוס אופקי משותף, כמו טבלאות ורצפות, ה-ARCore יכולה להפוך את התכונה הזו לזמינה באפליקציה במטוס אופקי.
כפי שראינו קודם לכן, ARCore עוזרת להנחות את המשתמש כשזוהה מטוס באמצעות הצגת נקודות לבנות.
הוספת עוגנים
לאחר זיהוי מטוס, אפשר לצרף אובייקט שנקרא עוגן. בעזרת עוגן אפשר למקם אובייקטים וירטואליים ולהבטיח שהאובייקטים האלה יישארו באותו מיקום בחלל. יש לשנות את הקוד כדי לצרף קוד לאחר זיהוי טיסה.
בsetUpAr
, מצורף קובץ OnTapArPlaneListener
אל PlacesArFragment
. event listener מופעל בכל פעם שמקישים על מטוס בסצנת ה-AR. במהלך השיחה הזו, אפשר ליצור Anchor
וAnchorNode
מה-HitResult
של הפונקציה שפונקציות ההאזנה מאפשרות לך:
arFragment.setOnTapArPlaneListener { hitResult, _, _ ->
val anchor = hitResult.createAnchor()
anchorNode = AnchorNode(anchor)
anchorNode?.setParent(arFragment.arSceneView.scene)
addPlaces(anchorNode!!)
}
ה-AnchorNode
הוא המקום שבו תצרפו אובייקטים של צומת צאצא – PlaceNode
מופעים – בסצנה שבה מתבצע הקריאה בשיטת addPlaces
.
הפעלה
אם מפעילים את האפליקציה עם השינויים שלמעלה, צריך להסתכל מסביב עד שמזוהה טיסה. יש להקיש על הנקודות הלבנות שמציינות את המטוס. לאחר מכן, אמורים להופיע סמנים במפה עבור כל הפארקים הקרובים ביותר. עם זאת, אם תשימו לב לאובייקטים הווירטואליים, הם נתקעים בעוגן שנוצר ולא מוקמים במקום שבו הפארקים נמצאים בשטח.
בשלב האחרון, יש לתקן זאת באמצעות SDK של מפות Google ל-Android Utility Directory ו-SensorManager במכשיר שלך.
8. מיצוב מקומות
כדי למקם את סמל המקום הווירטואלי במציאות רבודה בכותרת מדויקת, אתם צריכים שני קטעי מידע:
- איפה בצפון אנחנו נמצאים
- הזווית בין צפון לכל מקום
ההחלטה צפון
צפונה משם אפשר לזהות באמצעות חיישני המיקום (גיאומגנטיים ומד תאוצה) הזמינים במכשיר. בעזרת שני החיישנים האלה אפשר לאסוף מידע בזמן אמת לגבי מיקום המכשיר בחלל. לקבלת מידע נוסף על חיישני מיקום, ניתן לקרוא את המאמר חישוב הכיוון של המכשיר.
כדי לגשת לחיישנים האלה, עליך לקבל SensorManager
ולאחר מכן לרשום SensorEventListener
בחיישנים האלה. השלבים האלה כבר בוצעו עבורך בשיטות מחזור החיים של MainActivity
'
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
sensorManager = getSystemService()!!
// ...
}
override fun onResume() {
super.onResume()
sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also {
sensorManager.registerListener(
this,
it,
SensorManager.SENSOR_DELAY_NORMAL
)
}
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also {
sensorManager.registerListener(
this,
it,
SensorManager.SENSOR_DELAY_NORMAL
)
}
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
בשיטה onSensorChanged
, יש אובייקט מסוג SensorEvent
שמכיל פרטים על נתון נתון של חיישן/כפי שהוא משתנה לאורך הזמן. צריך להוסיף את הקוד הבא לשיטה הזו:
override fun onSensorChanged(event: SensorEvent?) {
if (event == null) {
return
}
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
System.arraycopy(event.values, 0, accelerometerReading, 0, accelerometerReading.size)
} else if (event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD) {
System.arraycopy(event.values, 0, magnetometerReading, 0, magnetometerReading.size)
}
// Update rotation matrix, which is needed to update orientation angles.
SensorManager.getRotationMatrix(
rotationMatrix,
null,
accelerometerReading,
magnetometerReading
)
SensorManager.getOrientation(rotationMatrix, orientationAngles)
}
הקוד שלמעלה בודק את סוג החיישן ובהתאם לסוג החיישן, יעדכן את קריאת החיישן המתאימה (מד תאוצה או קריאת מגנטומטר). באמצעות קריאות החיישנים האלה, ניתן לקבוע את הערך של מספר המעלות מצפון למכשיר למכשיר (כלומר, הערך של orientationAngles[0]
).
כותרת פוטוספרית
עכשיו, אחרי שצפון נקבע, השלב הבא הוא לקבוע את הזווית בין צפון לכל מקום ואחר כך להשתמש במידע הזה כדי למקם את המקומות בכותרת הנכונה במציאות רבודה.
כדי לחשב את הכותרת, אתם משתמשים ב-SDK של מפות Google ל-Android Utility Directory. הקובץ הזה מכיל כמה פונקציות מסייעות לחישוב מרחקים וכותרות באמצעות גיאומטריה כדורית. מידע נוסף זמין בסקירה הכללית של הספרייה.
לאחר מכן, יש להשתמש בשיטה sphericalHeading
בספריית התשתיות, שמחשב את הכותרת/ההמרה בין שני אובייקטים LatLng
. המידע הזה נחוץ בשיטה getPositionVector
שהוגדרה ב-Place.kt
. שיטה זו תחזיר בסופו של דבר אובייקט Vector3
, שישמש את כל ה-PlaceNode
כמיקום המקומי שלו במרחב ה-AR.
כדאי להחליף את ההגדרה של הכותרת בשיטה הזו ולשנות אותה כך:
val heading = latLng.sphericalHeading(placeLatLng)
הפעולות הבאות אמורות להוביל להגדרת השיטה הבאה:
fun Place.getPositionVector(azimuth: Float, latLng: LatLng): Vector3 {
val placeLatLng = this.geometry.location.latLng
val heading = latLng.sphericalHeading(placeLatLng)
val r = -2f
val x = r * sin(azimuth + heading).toFloat()
val y = 1f
val z = r * cos(azimuth + heading).toFloat()
return Vector3(x, y, z)
}
מיקום מקומי
השלב האחרון בכיוון הנכון של מקומות ב-AR הוא להשתמש בתוצאה של getPositionVector
כשמוסיפים אובייקטים PlaceNode
לסצנה. יש להמשיך אל addPlaces
בMainActivity
, מתחת לשורה שבה מוגדר ההורה על כל placeNode
(מתחת ל-placeNode.setParent(anchorNode)
). לשם כך, מגדירים את ה-localPosition
של ה-placeNode
כך:
val placeNode = PlaceNode(this, place)
placeNode.setParent(anchorNode)
placeNode.localPosition = place.getPositionVector(orientationAngles[0], currentLocation.latLng)
כברירת מחדל, השיטה getPositionVector
מגדירה את מרחק ה-y של הצומת למטר אחד כפי שצוין בערך y
בשיטה getPositionVector
. אם אתם רוצים לשנות את המרחק הזה, למשל, 2 מטרים, נסו לשנות את הערך הזה לפי הצורך.
לאור השינוי הזה, עכשיו צריך להפנות את האובייקטים הנוספים PlaceNode
לכותרת הנכונה. עכשיו אפשר להריץ את האפליקציה ולראות את התוצאה!
9. מזל טוב
כל הכבוד! הגעת ליעד!