כשמעבר מטלפונים לפורמטים שונים של מסכים גדולים, צריך להביא בחשבון איך המשחק יטפל בניהול החלונות. ב-ChromeOS וב-Google Play Games במחשב, המשחק יכול לפעול במצב חלון בממשק הראשי של שולחן העבודה. בטאבלטים ומכשירים מתקפלים חדשים עם Android שפועלים עם Android 12L (רמת API 32) ואילך, עם רוחב מסך של יותר מ-600dp, אפשר להריץ את המשחק לצד אפליקציות אחרות במצב מסך מפוצל, לשנות את הגודל שלו ואפילו להעביר אותו בין המסך הפנימי למסך החיצוני במכשירים מתקפלים. כתוצאה מכך, יתבצע שינוי בהגדרה של גודל החלון, ובמכשירים מסוימים גם של הכיוון.
הגדרה בסיסית של מסך גדול
עליכם לציין אם המשחק שלכם יכול להתאים את עצמו לגודל המסך:
<android:resizeableActivity="true" or "false" />
אם אין אפשרות לתמוך בשינוי הגודל, צריך לוודא שמניפסט המשחק מגדיר במפורש את יחסי הגובה-רוחב המינימליים והמקסימליים הנתמכים:
<!-- Render full screen between 3:2 and 21:9 aspect ratio -->
<!-- Let the platform letterbox otherwise -->
<activity android:minAspectRatio="1.5">
<activity android:maxAspectRatio="2.33">
Google Play Games במחשב
ב-Google Play Games במחשב, הפלטפורמה מטפלת באפשרות לשנות את גודל החלון תוך שמירה על יחס הגובה-רוחב שצוין. גודל החלון נעול באופן אוטומטי למימדים האופטימליים. אם הכיוון הראשי של המשחק הוא לרוחב, צריך לתמוך ביחס גובה-רוחב של 16:9 לפחות. אם המשחק בפורמט לאורך, צריך לתמוך ביחס גובה-רוחב של 9:16 לפחות. כדי ליהנות מחוויית המשחק הטובה ביותר, מומלץ לתמוך באופן מפורש ביחסי גובה-רוחב של 21:9, 16:10 ו-3:2 במשחקים שמוגדרים לרוחב. אין צורך לשנות את גודל החלון, אבל כדאי לעשות זאת כדי לשמור על תאימות לגורמים אחרים של פורמט.
מידע נוסף ושיטות מומלצות זמינים במאמר הגדרת הגרפיקה ב-Google Play Games במחשב.
מסכים גדולים של ChromeOS ו-Android
כדי להגדיל את אזור התצוגה של המשחק במסך מלא ב-ChromeOS ובמכשירי Android עם מסך גדול, צריך לתמוך במצב צפייה immersive במסך מלא ולהסתיר את שורת הסטטוס של המערכת על ידי הגדרת דגלים ב-decorView
, בהצגת ממשק המשתמש של המערכת או דרך ממשק ה-API של WindowInsetsCompat
. כדאי גם לטפל בצורה חלקה באירועי תצורה של סיבוב ושינוי גודל, או למנוע את התרחשותם במכשירי ChromeOS.
חשוב לזכור שבמכשירי Android עם מסך גדול, המשחק יכול לפעול בהגדרות שאולי עדיין לא מטפלים בהן. אם המשחק לא תומך בכל ההגדרות של גודל החלון והכיוון, הפלטפורמה תציג את המשחק בפורמט letterbox במצב תאימות, ואם יש צורך, תופיע הודעה לשחקן לפני שינוי ההגדרה ללא תמיכה.

במכשירים מסוימים, כששחקן עובר לתצורה שאינה נתמכת, עשויה להופיע בקשה לטעון מחדש את המשחק וליצור מחדש את הפעילות כך שתתאים בצורה הטובה ביותר לפריסה החדשה של החלון, מה שעלול להפריע לחוויית המשחק. כדאי לבדוק את המשחק בהגדרות שונות של מצב חלונות מרובים (גודל חלון של 2/3, 1/2 או 1/3) ולוודא שאף רכיב של משחק או ממשק המשתמש לא חתוך או לא נגיש. בנוסף, כדאי לבדוק איך המשחק מגיב לרצף במכשירים מתקפלים כשעוברים בין המסך הפנימי למסך החיצוני. אם נתקלתם בבעיות, עליכם לטפל באופן מפורש באירועי ההגדרה האלה ולהוסיף תמיכה מתקדמת לשינוי הגודל של המסך הגדול.
שינוי גודל מתקדם במסך גדול
כדי לצאת ממצב התאימות ולהימנע מיצירה מחדש של פעילות, מבצעים את הפעולות הבאות:
מגדירים את הפעילות הראשית כפעילות שניתן לשנות את הגודל שלה:
<android:resizeableActivity="true" />
כדי לקבל את כל אירועי ההגדרה של מסך גדול, צריך להצהיר על תמיכה מפורשת ב-'orientation', ב-'screenSize', ב-'smallestScreenSize', ב-'screenLayout' וב-'density' במאפיין
android:configChanges
של הרכיב<activity>
במניפסט של המשחק:<android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard | keyboardHidden | density" />
משנים את ברירת המחדל של
onConfigurationChanged()
ומטפלים באירוע ההגדרה, כולל הכיוון הנוכחי, גודל החלון, הרוחב והגובה:Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val density: Float = resources.displayMetrics.density val newScreenWidthPixels = (newConfig.screenWidthDp * density).toInt() val newScreenHeightPixels = (newConfig.screenHeightDp * density).toInt() // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE val newScreenOrientation: Int = newConfig.orientation // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270 val newScreenRotation: Int = windowManager.defaultDisplay.rotation }
Java
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); float density = getResources().getDisplayMetrics().density; int newScreenWidthPixels = (int) (newConfig.screenWidthDp * density); int newScreenHeightPixels = (int) (newConfig.screenHeightDp * density); // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE int newScreenOrientation = newConfig.orientation; // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270 int newScreenRotation = getWindowManager().getDefaultDisplay() .getRotation(); }
אפשר גם להריץ שאילתה על WindowManager
כדי לבדוק את כיוון הסיבוב הנוכחי של המכשיר. בעזרת המטא-נתונים האלה, בודקים את המאפיינים של החלון החדש ומריצים רינדור בגודל החלון המלא. יכול להיות שהפתרון הזה לא יפעל בכל המקרים בגלל הבדלים ביחס גובה-רוחב. לחלופין, תוכלו לכוונן את ממשק המשתמש של המשחק לגודל החלון החדש ולהציג את תוכן הליבה של משחקי הווידאו בפורמט letterbox. אם יש מגבלות טכניות או עיצוביות שמונעות את השימוש באחת מהגישות, תוכלו לבצע חיתוך בתוך המנוע כדי לשמור על יחס גובה-רוחב, ולשנות את הגודל למימדים הטובים ביותר האפשריים תוך הכרזה על resizeableActivity = false
והימנעות ממצב תצורה.
לא משנה באיזו גישה תבחרו, עליכם לבדוק את המשחק בתצורות שונות (קיפול ופתיחה, שינויי רוטציה שונים, מצב מסך מפוצל) ולוודא שאין אלמנטים של ממשק המשתמש במשחק שנחתכו או חופפים, בעיות בגישה ליעדים למגע או בעיות ביחס גובה-רוחב שגורמות למשחק להתארך, להתכווץ או להתעוות בדרכים אחרות.
בנוסף, במסכים גדולים יותר בדרך כלל יש פיקסלים גדולים יותר, כי יש את אותו מספר פיקסלים באזור הרבה יותר גדול. כתוצאה מכך, יכול להיות שיופיעו פיקסלים בתמונות עם מאגרי רינדור מוקטנים או בנכסים ברזולוציה נמוכה יותר. כדאי להשתמש בנכסים באיכות הגבוהה ביותר במכשירים עם מסך גדול, ולבדוק את פרופיל הביצועים של המשחק כדי לוודא שאין בעיות. אם המשחק תומך בכמה רמות איכות, חשוב לוודא שהוא מתאים למכשירים עם מסך גדול.
מצב ריבוי חלונות
מצב חלונות מרובים מאפשר להציג כמה אפליקציות בו-זמנית באותו מסך. מצב 'ריבוי חלונות' לא משנה את מחזור החיים של הפעילות. עם זאת, המצב של האפליקציות שחוזרות לפעול בחלונות מרובים משתנה בגרסאות שונות של Android (מידע נוסף זמין במאמר מחזור החיים של הפעילות במצב 'ריבוי חלונות' בקטע תמיכה במצב 'ריבוי חלונות').
כשהנגן מעביר אפליקציה או משחק למצב חלונות מרובים, המערכת מעדכנת את הפעילות על שינוי בהגדרות, כפי שמפורט בקטע שינוי גודל מתקדם במסך גדול. שינוי בתצורה מתרחש גם כשהשחקן משנה את גודל המשחק או מחזיר אותו למצב מסך מלא.
אין ערובה שהאפליקציה תקבל חזרה את המיקוד כשהיא מועברת למצב ריבוי חלונות. לכן, אם אתם משתמשים באחד מהאירועים של מצב האפליקציה כדי להשהות את המשחק, אל תסתמכו על האירוע 'קבלת המיקוד' (onWindowFocusChanged()
עם ערך המיקוד true) כדי להמשיך את המשחק. במקום זאת, צריך להשתמש בטיפולי אירועים אחרים או בטיפולי שינויי מצב אחרים, כמו onConfigurationChanged()
או onResume()
. חשוב לדעת שאפשר תמיד להשתמש בשיטה isInMultiWindowMode()
כדי לזהות אם הפעילות הנוכחית פועלת במצב של חלונות מרובים.
במצב 'מספר חלונות' ב-ChromeOS, מידות החלון הראשוניות הופכות לגורם חשוב. המשחק לא חייב להיות במסך מלא, וצריך להצהיר מה יהיה גודל החלון במקרה כזה. יש שתי דרכים מומלצות לטיפול בבעיה הזו.
האפשרות הראשונה פועלת באמצעות מאפיינים ספציפיים בתג <layout>
במניפסט של Android. המאפיינים defaultHeight
ו-defaultWidth
קובעים את המאפיינים הראשוניים. חשוב גם לשים לב למאפיינים minHeight
ו-minWidth
כדי למנוע מהשחקנים לשנות את גודל חלון המשחק למימדים שאתם לא תומכים בהם. לבסוף, יש את המאפיין gravity
, שמגדיר איפה החלון יופיע במסך כשהוא יופעל. זוהי דוגמה לתג פריסה עם המאפיינים האלה:
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end"
android:minHeight="450dp"
android:minWidth="300dp" />
האפשרות השנייה להגדרת גודל החלון פועלת באמצעות שימוש בגבולות השקה דינמיים. באמצעות setLaunchBounds(Rect)
, אפשר להגדיר את המאפיינים של חלון ההתחלה. אם מציינים מלבן ריק, הפעילות תתחיל במצב מוגדל למקסימום.
בנוסף, אם אתם משתמשים במנועים של המשחקים Unity או Unreal, חשוב לוודא שאתם משתמשים בגרסה עדכנית (Unity 2019.4.40 ו-Unreal 5.3 ואילך) שמספקת תמיכה טובה במצב של חלונות מרובים.
תמיכה במצבים שונים של ישיבה
כדי לתמוך בתנוחות של מכשירים מתקפלים, כמו מצב שולחני, ולהגביר את ההתעניינות וההתמקדות של השחקנים, אפשר להשתמש בספריית הפריסה של WindowManager ב-Jetpack:

Kotlin
fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean { contract { returns(true) implies (foldFeature != null) } return foldFeature?.state == FoldingFeature.State.HALF_OPENED && foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL }
Java
boolean isTableTopPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL); }