אופטימיזציה של משחקי Unity למסכים גדולים ולמכשירים מתקפלים

1. מבוא

בשיעור Codelab הזה נסביר איך לבצע אופטימיזציה למשחק Unity Android למכשירים עם מסך גדול ומכשירים מתקפלים. תשתמשו בפרויקט לדוגמה Lost Crypt כדי להטמיע תמיכה ביחסי גובה-רוחב, להרחיב את השימוש במסך מלא ולהגיב למצבי מכשיר מתקפל.

דרישות מוקדמות

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

מה תלמדו

  • איך מטמיעים תמיכה ביחסי גובה-רוחב שונים.
  • איך משתמשים בעוגנים בהגדרות הממשק והמצלמה של המשחק כדי להתאים את עצמם באופן אוטומטי לגודל המסך.
  • איך משתמשים ב-Jetpack Window Manager של Android כדי לזהות את מצב הקיפול של המכשיר ולשנות את הפריסה.

מה צריך

  • ‫Unity Hub ו-Editor: גרסה Unity 2020 LTS או Unity 2022 מותקנת, עם תמיכה ב-Android build.
  • חומרה: מכשירי Android עם מסך גדול, כמו טאבלטים או טלפונים מתקפלים.
  • פלטפורמת פיתוח אג'נטית, כמו Google Antigravity, אם אתם מתכננים להשתמש בסקיל.

2. מיומנות של סוכן

אופציונלית, יש מיומנות של סוכן שזמינה כדי להעריך את פרויקט המשחק שלכם ב-Unity. הידע הזה יעזור לכם להוסיף תמיכה בשינוי גודל ובמכשירים עם מסך גדול ובמכשירים מתקפלים. מידע נוסף זמין ב-GitHub

כדי להשתמש במיומנות הזו:

  1. מעתיקים את הקובץ SKILL.md לספריית הסוכן. מידע נוסף על הוספת מיומנות מופיע במאמר תחילת העבודה עם Google Antigravity.
  2. מזינים הנחיה לסוכן בסביבת פיתוח משולבת (IDE) עם יכולות AI אקטיבי, כמו Antigravity, עם שאלות לגבי מסכים גדולים, מכשירים מתקפלים או שינוי גודל.
  3. מבקשים מהנציג להוסיף תמיכה במסכים גדולים ובמכשירים מתקפלים.

3. תמיכה ביחס גובה-רוחב

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

  1. חשוב לוודא שהאפליקציה משתמשת בהגדרה של מסך מלא שניתן לשינוי גודל.
  2. ב-AndroidManifest.xml (בכתובת Assets/Plugins/Android/AndroidManifest.xml):
    1. מגדירים את android:resizeableActivity="true".
    2. מגדירים את android:screenOrientation="fullSensor".

לדוגמה:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    <application>
        <activity android:name="com.unity.lostcryptlargescreenexample.LargeScreenPlayableActivity"
                  android:theme="@style/UnityThemeSelector"
                  android:screenOrientation="fullSensor"
                  android:launchMode="singleTask"
                  android:resizeableActivity="true"
                  android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
                  android:hardwareAccelerated="false">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>
  1. בודקים את הגדרות ה-Build ב-Unity כדי לראות את ההגדרות של פלטפורמת Android.

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

4. ניצול מסך מלא

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

כדי להשתמש בכל המסך:

  1. משתמשים בעוגנים בממשק המשתמש של המשחק ובהגדרות המצלמה. כך רכיבי ממשק המשתמש משנים את המיקום שלהם באופן אוטומטי בהתאם לגודל המסך.
  2. כדי לראות איך מוגדרת הצמדה של Canvas בממשק המשתמש, אפשר לפתוח את סצנת ההצמדה בפרויקט לדוגמה.
  3. בסצנת העגינה, בדף העריכה מתעדכן כך שיוצג בו אזור בטוח, ואמצעי הבקרה של התנועה מעוגנים בפינות המסך.

כדי לשמור על יחס הגובה-רוחב של המצלמה הראשית, המערכת משנה באופן דינמי את pixelRect של המצלמה. סקריפט CameraAspectLock.cs מדגים איך להגיב לשינויים בכיוון ולשנות את תצוגת המצלמה למצב Letterbox:

[SerializeField]
private float minAspectRatio = 4f / 3f;

[SerializeField]
private float maxAspectRatio = 21f / 9f;

public void MaintainAspectRatio() {
    int viewWidth = Display.main.systemWidth;
    int viewHeight = Display.main.systemHeight;

    // 1. Calculate screen aspect ratio
    float scaleScreen = viewWidth / (float)viewHeight;
    Rect rect = _camera.pixelRect;

    // 2. Scale to the fullest of the lower between width or height
    if (scaleScreen > maxAspectRatio) {
        // use height, lock width to max at aspect scale
        rect.width = viewHeight * maxAspectRatio;
        rect.height = viewHeight;
    } else if (scaleScreen < minAspectRatio) {
        // use width, lock height to min at aspect scale
        rect.width = viewWidth;
        rect.height = viewWidth / minAspectRatio;
    } else {
        // within allowed aspect ratio range, use full screen
        rect.width = viewWidth;
        rect.height = viewHeight;
    }

    // make sure to center the rect within the window
    rect.x = Mathf.Floor((viewWidth - rect.width) * 0.5f);
    rect.y = Mathf.Floor((viewHeight - rect.height) * 0.5f);

    Debug.Log("camera view width = " + rect.width +
        " height = " + rect.height +
        " x = " + rect.x +
        " y = " + rect.y);

    // 3. Commit the new size to the camera so that it takes effect
    _camera.pixelRect = rect;
}

5. תמיכה במכשירים מתקפלים

במכשירים מתקפלים, צריך להתאים את פריסת המשחק בהתאם למצב הקיפול של המכשיר (לדוגמה, HALF_OPENED, ‏ FLAT) ולגודל המסך תוך כדי תנועה.

  1. משתמשים ב-Jetpack Window Manager של Android כדי לזהות את מצב המכשיר.
  2. בפרויקט לדוגמה, פותחים את הסצנה HingeAware.
  3. הסצנה הזו מכילה ConfigurationManager MonoBehaviour שמגשר בין אירועי Jetpack WindowManager של Android לבין Unity, ומגיב למצבי קיפול באמצעות קריאות חוזרות (callback) של UnityEvent.
  4. ‫HingeSensor.cs קורא את זווית הציר באמצעות תוסף ה-Java‏ com.unity.lostcryptlargescreenexample.HingeAngleSensor.

שימוש ב-ConfigurationManager

‫ConfigurationManager.cs מתקשר עם פעילות מותאמת אישית של Android ומנתח מחרוזות JSON שמייצגות את המצב. הוא מאזין לקריאות חוזרות (callbacks) של onConfigurationChanged ו-onFoldChanged מ-Android Activity.

[System.Serializable]
public class FoldInfo {
	public string orientation;
	public string state;
	public int isSeparating;
	public int boundsLeft;
	public int boundsTop;
	public int boundsRight;
	public int boundsBottom;
}

public void onFoldChanged(string strFoldInfo) {
	FoldInfo info = JsonUtility.FromJson<FoldInfo>(strFoldInfo);

	// Always call the refresh from the main Unity thread
	StartCoroutine(ExecuteOnMainUnityThread(ActionOnFoldChange, info));
}

אפשר גם לשלוח שאילתה לגבי המצב באופן סינכרוני באמצעות המאפיין getFoldableState:

public static string getFoldableState {
	get {
		if (Application.platform == RuntimePlatform.Android) {
			var foldingFeatureObject = foldablePlayerActivity.Call<AndroidJavaObject>("getFoldingFeature");
			if (foldingFeatureObject != null) {
				var state = foldingFeatureObject.Call<AndroidJavaObject>("getState");
				return state.Call<string>("toString");
			}
		}
		return "NONE";
	}
}

6. שימוש ב-HingeSensor

בחלק מהמכשירים המתקפלים יש חיישן ציר פיזי שמספק את הזווית המדויקת של הקיפול (0 עד 360 מעלות). אפשר להשתמש בזה כדי להפעיל מכניקות של משחקים או לשנות את המצלמה.

ב-HingeSensor.cs, פלאגין של Android עובר אתחול כדי לקרוא את חיישן החומרה. אפשר גם לבדוק אם הציר מופעל:

public class HingeSensor : IDisposable {
	AndroidJavaObject plugin;

	public static HingeSensor Start() {
		if (Application.platform == RuntimePlatform.Android) {
			return OnPlayer.Run(p => {
				var context = p.GetStatic<AndroidJavaObject>("currentActivity");
				var plugin = new AndroidJavaClass("com.unity.lostcryptlargescreenexample.HingeAngleSensor")
						.CallStatic<AndroidJavaObject>("getInstance", context);

				if (plugin != null) {
					plugin.Call("setupSensor");
					return new HingeSensor(plugin);
				}
				return null;
			});
		}
		return null;
	}

	public float GetHingeAngle() {
		return (Application.platform == RuntimePlatform.Android && plugin != null) ? plugin.Call<float>("getHingeAngle") : -1.0f;
	}

	public bool IsHingeEnabled() {
		return Application.platform == RuntimePlatform.Android && plugin != null && plugin.Call<int>("isHingeEnabled") > 0;
	}
}

הערה: כדי למנוע ניקוז של הסוללה, חשוב להתקשר אל Dispose() או אל StopSensing() במופע HingeSensor כשמשהים את האפליקציה או כשמבטלים אותה.

7. יצירת קובץ AAB

כדי ליצור קובץ AAB ב-Unity Editor, פועלים לפי השלבים הבאים:

  1. ב-Unity Editor, לוחצים על קובץ > הגדרות ה-Build.
  2. בוחרים באפשרות יצירת חבילת אפליקציות ( Google Play).מידע נוסף זמין במאמר Android Build Settings reference.
  3. לוחצים על Build.
  4. מורידים את קובץ ה-AAB מ-Unity Editor.

יצירת גרסה לבדיקה פנימית

כדי ליצור גרסה פנימית לבדיקה ולהוסיף בודקים ב-Play Console, מבצעים את השלבים הבאים:

  1. ב-Google Play Console, בוחרים משחק.
  2. עוברים לדף בדיקה ופרסום (בדיקה > בדיקה פנימית).
  3. לוחצים על העלאה ובוחרים את קובץ ה-AAB.
  4. בשדה פרטי ההפצה, מזינים שם.
  5. לוחצים על הבא ובודקים את פרטי הפריט.
  6. לוחצים על שמירה ופרסום.

8. בנייה והרצה של הפרויקט

עכשיו אפשר ליצור את הפרויקט ולבדוק אותו במכשיר.

  1. מוודאים שהמשחק פתוח.
  2. ב-Play Console, עוברים אל קובץ > פרופיל Build (או הגדרות Build).
  3. אם רוצים להעלות ל-Play Console, מוודאים שהאפשרות Build App Bundle (Google Play) (יצירת חבילת אפליקציה (Google Play)) מסומנת. לחלופין, אפשר ללחוץ על Build and Run (יצירה והפעלה) כדי לפרוס ישירות למכשיר המחובר.
  4. במכשירים מתקפלים, צריך לשנות את פריסת המשחק בהתאם למצב הקיפול של המכשיר (לדוגמה, HALF_OPENED, ‏ FLAT) ולגודל המסך תוך כדי תנועה.

9. מזל טוב

למדתם את העקרונות של התאמת משחק Unity ל-Android למסכים גדולים ולמכשירים מתקפלים.

סקר

המשוב שלכם על ה-Codelab הזה חשוב לנו מאוד. כדאי להקדיש כמה דקות למילוי הסקר שלנו.