Optimizing Unity Games for Large Screens and Foldables

1. Introduction

In this codelab, you will learn how to optimize a Unity Android game for large screen and foldable devices. You will use the Lost Crypt sample project to implement aspect ratio support, expand full screen utilization, and respond to foldable states.

Prerequisites

  • Basic understanding of Unity and Android development.
  • Large screen Android devices like tablets or foldable phones.

What you learn

  • How to implement support for various aspect ratios.
  • How to use anchors in your game's UI and camera settings to automatically adjust to the screen size.
  • How to use Android's Jetpack Window Manager to detect the device's folding posture and adjust layout.

What you need

  • Unity Hub and Editor: Unity 2020 LTS or Unity 2022 installed, with Android build support.
  • Hardware: Large screen Android devices like tablets or foldable phones.
  • Agentic development platform such as Google Antigravity if you plan to to use the skill.

2. Agent Skill

Optionally, an agent skill is available to evaluate your Unity game project. This skill helps you add support for resizability and support for large screen and foldable devices. For more information, see: GitHub

To use this skill:

  1. Copy the SKILL.md file to your agent directory. For more information about how to add a skill, see Getting Started with Google Antigravity.
  2. Prompt your agent in your Agentic IDE, such as Antigravity, with questions about large screens, foldables, or resizability.
  3. Ask the agent to add support for large screens and foldables.

3. Aspect Ratio Support

To ensure your game appears correctly across different screen sizes, start by implementing support for various aspect ratios. You might consider adjusting the camera or canvas aspect ratio to better fit different screens.

  1. Ensure your application utilizes the full screen resizable setting.
  2. In your AndroidManifest.xml (located at Assets/Plugins/Android/AndroidManifest.xml):
    1. Set android:resizeableActivity="true".
    2. Set android:screenOrientation="fullSensor".

For example:

<?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. Check your Build Settings in Unity for Android platform configurations.

Note: Supporting various aspect ratios is the first step to making your game look great on tablets and foldables.

4. Full Screen Utilization

After adapting to different aspect ratios, ensure your game uses the entire screen to make gameplay more immersive.

To use the entire screen:

  1. Use anchors in your game's UI and camera settings. This lets UI elements automatically adjust their positions relative to the screen size.
  2. Open the Anchoring scene in the example project to see how UI canvas anchoring is set up.
  3. In the Anchoring scene, the canvas is updated to be safe-frame aware, and the movement controls are anchored to the corners of the screen.

To maintain the aspect ratio for your main camera, dynamically adjust the camera's pixelRect. The CameraAspectLock.cs script demonstrates how to respond to orientation changes and letterbox the camera view:

[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. Foldable devices support

With foldable devices, you should adjust your game's layout based on the device's folding posture (For example, HALF_OPENED, FLAT) and screen size on the fly.

  1. Use Android's Jetpack Window Manager to detect the device's state.
  2. In the example project, open the HingeAware scene.
  3. This scene contains a ConfigurationManager MonoBehaviour which bridges Android's Jetpack WindowManager events to Unity and responds to folding states via UnityEvent callbacks.
  4. The HingeSensor.cs reads the hinge angle using the com.unity.lostcryptlargescreenexample.HingeAngleSensor Java plugin.

Using ConfigurationManager

ConfigurationManager.cs communicates with a custom Android activity and parses JSON strings representing the state. It listens for onConfigurationChanged and onFoldChanged callbacks from the 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));
}

You can also query the state synchronously using the getFoldableState property:

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. Utilize the HingeSensor

Some foldable devices have a physical hinge sensor that provides the exact angle of the fold (0 to 360 degrees). You can use this to drive gameplay mechanics or adjust the camera.

In HingeSensor.cs, an Android plugin is initialized to read the hardware sensor. You can also check if the hinge is enabled:

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;
	}
}

Note: Make sure to call Dispose() or StopSensing() on your HingeSensor instance when your application pauses or is destroyed to prevent battery drain.

7. Create an AAB file

To create an AAB file in Unity Editor, follow these steps:

  1. In the Unity Editor, click File > Build settings.
  2. Select Build App Bundle ( Google Play ).For more information, see Android Build Settings reference.
  3. Click Build.
  4. Download the AAB file from the Unity Editor.

Create an internal testing release

To create an internal testing release and add testers in Play Console, perform the following steps:

  1. In the Google Play Console, select a game.
  2. Navigate to the Test and release page (Testing > Internal testing).
  3. Click Upload and select the AAB file.
  4. In the Release details field, enter a name.
  5. Click Next and review the release details.
  6. Click Save and publish.

8. Build and run the project

Now you are ready to build the project and test it on your device.

  1. Make sure you have the game open.
  2. In the Play Console, go to File > Build Profile (or Build Settings).
  3. Ensure Build App Bundle (Google Play) is checked if you want to upload to Play Console, or click Build and Run to deploy directly to your connected device.
  4. Verify the foldable devices, you should adjust your game's layout based on the device's folding posture (For example, HALF_OPENED, FLAT) and screen size on the fly.

9. Congratulations

You have successfully learned the principles of adapting a Unity Android game for large screens and foldables!

Survey

Your feedback on this codelab is highly valued. Consider taking a few minutes to complete our survey.