When developing a game for Android, it's important to anticipate the variety of possible player experiences and remain adaptive to a player's real-time interaction needs. By supporting different player experiences, you increase gameplay flexibility, helping you expand your game's reach.
Specific differences in player experience include the following:
- Device form factors: Although phones provide the traditional Android device experience, it's possible to interact with games on other form factors. ChromeOS devices can run an Android container that displays your game. Tablets that can run Android support several different levels of fidelity. Android TV devices support more detail-rich and more immersive experiences. Players can simulate a multi-window environment by using a display extension tool. And when using foldables, players can change the size of the screen during a gameplay session.
- Interaction methods: Players can provide input by touching a device's screen, but they can also use a mouse, touchpad, keyboard, or controller instead. In addition, the availability of display extension tools and foldable devices allows players to experience your game on a larger screen, making longer gameplay sessions and more complex interfaces more feasible.
- Hardware support: Some Android-powered devices don't have hardware that is more typical in a handheld device, such as a rear-facing camera, a GPS, and network connectivity. Your game should adapt to the hardware that's available and gracefully handle situations where certain features aren't available.
This guide presents best practices related to developing your game for different types of screens and user interactions. This guide also provides suggestions on designing your game and developing an effective testing strategy.
Game design best practices
When planning your game's design and architecture, follow the best practices described in the following sections.
Respond to configuration changes manually
When the Android system detects a configuration change, such as a change in
screen size, screen orientation, or input method, the system by default restarts
the current activity. To preserve state within an app or game, the activity by
default calls
onSaveInstanceState()
before it restarts and
onRestoreInstanceState()
after it restarts. This process, however, requires the activity to reload all
associated services and resources. To learn more about this default behavior,
see the guide on handling configuration
changes.
A typical gameplay session undergoes several configuration changes. If your game lets the system handle each configuration change, your game's scene is destroyed and restarted over and over, reducing your game's performance. For this reason, we highly encourage you to handle these configuration changes yourself in your game.
To learn how to add this configuration change logic to your game, see the section on how to create custom configuration change handlers.
Create a flexible architecture
To add support for your game on as many devices as possible, follow these best practices:
- Deploy Android App Bundles instead of individual APKs. Android App Bundles allow you to package artifacts of different resolutions and different architecture models, such as x86, ARM, into a single artifact. Better still, Android App Bundles support higher size limits for your game; each base APK can be as large as 150 MB, and the bundle itself can be many gigabytes in size.
- Add support for x86 architectures. This step improves your game's performance on devices that don't support ARM, because these devices can now execute instructions without having to translate them first.
Add support for Vulkan
By supporting Vulkan, your game can achieve higher graphics performance. Most devices support this graphics API.
Create custom configuration change handlers
To declare the types of configuration changes that your game handles itself, add
the android:configChanges
attribute to each <activity>
element in your manifest that represents a screen
or complex interface.
The following code snippet demonstrates how to declare that your game takes care of screen size, screen orientation, and input method changes:
<activity ... android:configChanges="screenSize|orientation|keyboard|keyboardHidden"> </activity>
When the declared configuration changes occur, the system now invokes a
different method,
onConfigurationChanged()
.
Within this method, add logic to update your game's UI:
- Update the scale factor and orientation of the screen. Keep in mind that, for performance purposes, it's sometimes better to scale your game's UI along only one dimension.
- Identify the optimal input method for the player to use.
Handle screen configuration changes
Your game handles screen size and screen orientation changes manually whenever
you include the screenSize
and orientation
values, respectively, in an
android:configChanges
attribute. You can use these new values to update your scene's content and
player input areas. For guidance on how to design your game's layout to make it
easier to update, see the guide on supporting different screen
sizes.
In your game's implementation of onConfigurationChanged()
, use the passed-in
Configuration
object and the
window manager's Display
object to
determine the updated values for screen size and screen orientation,
respectively.
The following code snippet shows how to obtain your game's updated screen size and orientation:
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() // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or // Configuration.ORIENTATION_LANDSCAPE. val newScreenOrientation: Int = newConfig.orientation // Get general rotation; one of: 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); // Get general orientation; either Configuration.ORIENTATION_PORTRAIT or // Configuration.ORIENTATION_LANDSCAPE. int newScreenOrientation = newConfig.orientation; // Get general rotation; one of: ROTATION_0, ROTATION_90, ROTATION_180, // or ROTATION_270. int newScreenRotation = getWindowManager().getDefaultDisplay() .getRotation(); }
Note that changing the pose of a foldable device changes the configuration, even if your app runs in fullscreen mode. As a result, your app might have to handle changes to screen size or pixel density if the user folds or unfolds the device while your game is running.
Game-specific screen qualities
The following sections describe how to adjust how your game reacts to screen size or screen orientation changes, depending on your game's qualities:
Fullscreen mode
On some platforms, such as ChromeOS, Android apps and games can be windowed and
resizeable by default. If your game should always run in fullscreen mode, you
can set the
android:resizeableActivity
attribute to false
in one of your <activity>
elements, as shown in the
following code snippet:
<activity ... android:resizeableActivity="false"> </activity>
You can also set android:resizeableActivity
attribute to false
to prevent
size-based configuration changes from occurring. Unless your game always runs in fullscreen mode, however, you should add this attribute only as a temporary fix
for testing purposes.
Screen orientation
If your game depends on a device's sensors having a specific orientation,
specify a value for
android:screenOrientation
in
your game's activity, as shown in the following code snippet. This setting helps
prevent a scene in your game from flipping upside down unexpectedly.
<activity ... android:screenOrientation="landscape"> </activity>
Device-specific screen qualities
The following sections describe how to handle screen-based configuration changes given specific qualities that some devices have.
Aspect ratio
Some devices support different aspect ratios. For example, foldable devices are designed to support an aspect ratio of 21:9 when in the folded state. To handle this potential variety in aspect ratio, do at least one of the following:
- Target Android 8.0 (API level 26) or higher.
- Make your game's scene and interface resizeable. Set
android:resizeableActivity
totrue
on devices running Android 7.0 (API level 24) and higher. Declare a maximum supported aspect ratio. In a
<meta-data>
attribute associated with your game, setandroid.max_aspect
to2.4
, as shown in the following code snippet. Keep in mind, however, that aspect ratios larger than the one you've specified cause the game to appear letterboxed within a display.<application> <meta-data android:name="android.max_aspect" android:value="2.4" /> </application>
Multiple activities visible simultaneously
Many modern devices support a variety of screen layouts, including split-screen, picture-in-picture, and large display areas. When using one of these layouts, the system can make multiple activities visible at the same time.
On devices running Android 9 (API level 28) or higher, it's possible for all top
visible activities to be resumed at the same time. In order for this behavior to
work, however, both your game and the device's OEM need to opt into the
functionality. You can add support within your game by setting
android.allow_multiple_resumed_activities
to true
in your game's manifest,
as shown in the following snippet:
<application> <meta-data android:name="android.allow_multiple_resumed_activities" android:value="true" /> </application>
You can then test your game on different devices to see which of them provide the OEM support necessary for multi-resume to function properly.
For more information on configuring your game to appear as part of a multi-window display, see the guide on how to add multi-window support.
Handle different types of interaction models
Your game handles keyboard presence and keyboard availability manually whenever
you include the keyboard
and keyboardHidden
values, respectively, in an
android:configChanges
attribute. You can use these new values to update your game's primary input
method.
When configuring your game for supporting multiple types of user input, keep the following in mind:
- Detect input methods rather than individual devices. This mindset makes it easier to improve the player experience without focusing too much on the specific device that the player might have.
- Include the
keyboardHidden
attribute in your list of manually-handled configuration changes. That way, your game can keep track of when a keyboard is physically attached to the device but unusable. Determine input methods that are currently available. To do so, call
getInputDeviceIds()
on game startup and after each configuration change.You can often determine how the player plans to interact with your game based on their preferred input device:
- Players typically use a keyboard or game controller to perform rapid button sequences.
- Players typically use a touchscreen or touchpad to perform more complex gestures.
- Players typically use a mouse to perform higher-precision input.
The following sections provide best practices for specific types of input devices.
Keyboard
When creating a keyboard layout for your game, consider how the player navigates through a given scene as well as how they interact with your game's settings.
The WASD keys or arrow keys are usually best for controlling character movement. It's also best to assign a particular key for each important action or skill that a controllable character can perform within your game. To maximize the player experience, consider adding support for custom key bindings in your game.
Players should also be able to open your game's menus and navigate through them
using the keyboard. The Esc
key is a common mapping for pausing a scene and
showing the game's menu.
For more information on supporting keyboard input in your game, see the guide on how to support keyboard navigation as well as the guide on how to handle keyboard actions.
Game controller
For more information on handling controller input in your game, see the guide on how to support game controllers.
Mouse or touchpad
If your game supports player input from a mouse or touchpad, keep in mind that players interact with the device in ways other than playing your game. It's important to be aware that, by requesting pointer capture, all mouse input is directed to your game. Therefore, after your game has the information it needs, release pointer capture so that players regain standard mouse control of their device.
On devices running Android 8.0 (API level 26) and higher, you can use the Mouse
Capture API to assist with the pointer capture process. In games that react to
high-precision input, you can get the pointer's current coordinates by calling
the getX()
and
getY()
methods.
For additional information on adding support for mouse input and touchpad input in your game, see the guide on how to track touch and pointer movements as well as the guide on how to handle multi-touch gestures.
Test your game
Before launching your game, test how it responds to configuration changes by completing the steps described in the following sections.
Update your test plan
When validating your game's functionality, include the following test cases:
- Minimize and maximize the window that contains your game. (Doesn't apply if your game is always in fullscreen mode.)
- Change screen size.
- Change screen orientation. (Doesn't apply if your game has a fixed orientation.)
- Connect and disconnect input devices, such as keyboards and mice.
- Perform multi-resume, if your game supports it.
Also, consider updating your game's quality control system so that you can optimize for a wider variety of player experiences.
For best practices related to testing your game, see the Fundamentals of Testing guide.
Use testing and debugging tools
You can perform tests using a variety of tools that the platform supports:
Emulators, including the Android emulator and Firebase Test Lab.
ChromeOS Performance Analyzer, available when running ChromeOS M75 or higher.