Welcome to Echoes, a proof-of-concept game demonstrating how to use Panda3D/Ursina to build games for Android devices.
Have you ever wanted to run your Ursina game on Android? With this guide, you’ll learn how to set up your environment, port your game, and customize your project for mobile devices.
Currently, only Android is supported. iOS support is not planned due to Panda3D limitations.
First, make sure you have Git installed. Open your terminal and run:
git clone https://github.com/ShivamKR12/EchoesInstall Python 3.8. This is required for Panda3D on Android.
Ensure Python 3.8 is in your PATH and is the default Python installation. If python --version does not return Python 3.8.X, use python3.8 instead of python in all commands.
Install the required packages:
python -m pip install protobuf==3.20.0Install Panda3D for your operating system:
Windows:
python -m pip install https://buildbot.panda3d.org/downloads/68f0931f43284345893a90d5bba9ba5df8aa53bb/panda3d-1.11.0.dev2480-cp38-cp38-win_amd64.whlmacOS:
python -m pip install https://buildbot.panda3d.org/downloads/68f0931f43284345893a90d5bba9ba5df8aa53bb/panda3d-1.11.0.dev2480-cp38-cp38-macosx_10_9_x86_64.whlLinux:
python -m pip install https://buildbot.panda3d.org/downloads/68f0931f43284345893a90d5bba9ba5df8aa53bb/panda3d-1.11.0.dev2480-cp38-cp38-manylinux2010_x86_64.whlIf you encounter issues with the wheel files, please open an issue on the repository.
Create a setup.py file using the provided template. Update the following fields:
name: Set to your game name (e.g.,'Echoes')version: Set your game versionapplication_id: Use a unique identifier (e.g.,'com.yourcompany.echoes')android_version_code: Increment this for each Play Store releasegui_apps: Map your app name to your main Python file (e.g.,'echoes': 'game/__main__.py')icons: Set your app icon (e.g.,'icons': {'*': 'mylogo.png'})
Important: Do not remove the ursina_assets folder or its entry in include_patterns.
-
Application ID: This is a unique identifier for your app on the Google Play Store. It should be based on your domain name in reverse (e.g.,
com.yourcompany.yourapp). Caution: Once your app is uploaded to the Play Store, you cannot change the application ID. Choose it carefully. -
Android Version Code: This is an integer that starts at 1 and must be incremented for each new version uploaded to the Play Store. It is used internally by the Play Store for version management, separate from the user-visible version string.
-
Renderer Options: The setup uses
pandagles2as the primary renderer (with shaders) andpandaglesas a fallback (fixed-function pipeline). These are required for Android as the regularpandaglrenderer is not available. Usepandagles2if your game uses shaders; otherwise, usepandagles.
For more advanced build options, refer to the Panda3D Build Options Documentation.
Instead of signing during bundletool conversion, you can sign the .aab bundle directly for Play Store upload. Generate a certificate using OpenSSL:
openssl genpkey -algorithm RSA -aes256 -out private.pem
openssl req -new -x509 -sha256 -days 365 -key private.pem > cert.pemNever share the private key! Then, modify setup.py to include signing:
'bdist_apps': {
'signing_certificate': 'cert.pem',
'signing_private_key': 'private.pem',
},Alternatively, use keytool to generate a keystore and sign manually with jarsigner.
Disclaimer: Choose between signing during build (as above) or during bundletool conversion (in step 5). For Play Store uploads, signing the .aab is recommended.
For each new version uploaded to the Play Store, increment the android_version_code in setup.py by 1. This is required for the Play Store to accept the update.
Note that the android_abis option (e.g., 'android_abis': ['arm64-v8a']) can be used in setup.py to specify which Android CPU architectures to build for. This option is not listed in the official build options but is supported.
When using the bundletool build-apks command, you can specify various flags to customize the build:
| Flag | Description |
|---|---|
| --bundle=path | (Required) Path to the app bundle (.aab) you built. |
| --output=path | (Required) Name of the output .apks file containing all APK artifacts. |
| --overwrite | Overwrites existing output file if it exists. |
| --aapt2=path | Specifies a custom path to AAPT2. By default, bundletool includes its own version. |
| --ks=path | (Optional) Path to the keystore used to sign the APKs. If omitted, bundletool signs with a debug key. |
| --ks-pass=pass:password or --ks-pass=file:/path/to/file | Specifies the keystore password, either inline or from a file. |
| --ks-key-alias=alias | Alias of the signing key to use. |
| --key-pass=pass:password or --key-pass=file:/path/to/file | Password for the signing key. Can be omitted if same as keystore password. |
| --connected-device | Builds APKs targeting the configuration of a connected device. |
| --device-id=serial-number | Specifies the serial ID of the device to target if multiple devices are connected. |
| --device-spec=spec_json | Path to a JSON file specifying the device configuration to target. |
| --mode=universal | Builds a single universal APK compatible with all device configurations. Larger but easier for testing. |
| --local-testing | Enables local testing mode for quick iterative testing without uploading to Play Store. |
Use these flags to customize your APK builds when running the bundletool build-apks command in step 5.
Place your Ursina game inside the game folder. At the top of your main script, add:
from setup_ursina_android import setup_ursina_android
setup_ursina_android()Edit game/setup_ursina_android.py and set app_id to match your application_id in setup.py.
If your game requires additional Python packages, add them to requirements.txt. Only add new dependencies; do not remove existing ones.
-
Assets: Place your assets in
game/assets. Add'game/assets/**'toinclude_patternsinsetup.py. Important: Ensure all assets are included ininclude_patterns, especiallyursina_assets/**andgame/assets/**, as missing assets can cause the app to crash. -
Asset List: List your assets in
game/setup_ursina_android.py:game_assets = ['your_first_file.png', 'your_second_file.png'] game_assets_src_dir = "game/assets"
-
Using Assets: Load assets in your game like:
from ursina import Entity Entity(texture="your_first_file.png")
-
Dependencies: Add PyPI dependencies to
requirements.txt. Ensure they support Python 3.8 and are platform-independent (py3-none-any).
- Enable developer options and USB debugging on your Android device.
- Install ADB.
- Install Java.
- Download BundleTool.
Build the Android App Bundle (AAB):
Navigate to the src directory and run:
python setup.py bdist_appsConvert AAB to APKS:
you would need a key to sign the .aab when converting it to .apks or otherwise, Android wouldn't let you install the .apk . use the below command to create a key :
Windows:
keytool -genkeypair -alias <alias-name> -keyalg RSA -keysize 2048 -validity 10000 -keystore <keystore-name.keystore> -storepass "your_keystore_password" -keypass "your_key_password" -dname "CN=YourName, OU=YourUnit, O=YourOrg, L=YourCity, ST=YourState, C=YourCountry"macOS:
keytool -genkeypair -alias <alias-name> -keyalg RSA -keysize 2048 -validity 10000 -keystore <keystore-name.keystore> -storepass "your_keystore_password" -keypass "your_key_password" -dname "CN=YourName, OU=YourUnit, O=YourOrg, L=YourCity, ST=YourState, C=YourCountry"Linux:
keytool -genkeypair -alias <alias-name> -keyalg RSA -keysize 2048 -validity 10000 -keystore <keystore-name.keystore> -storepass "your_keystore_password" -keypass "your_key_password" -dname "CN=YourName, OU=YourUnit, O=YourOrg, L=YourCity, ST=YourState, C=YourCountry"Use BundleTool to convert your .aab to .apks:
Windows:
java -jar "Path/To/BundleTool/bundletool.jar" build-apks --bundle "./dist/app-release.aab" --output "./dist/app.apks" --ks "Path/To/Your-keystore.keystore" --ks-pass pass:your_keystore_password --ks-key-alias <alias-name> --key-pass pass:your_keystore_password --mode universal --verbosemacOS:
java -jar "Path/To/BundleTool/bundletool.jar" build-apks --bundle "./dist/app-release.aab" --output "./dist/app.apks" --ks "Path/To/Your-keystore.keystore" --ks-pass pass:your_keystore_password --ks-key-alias <alias-name> --key-pass pass:your_keystore_password --mode universal --verboseLinux:
java -jar "Path/To/BundleTool/bundletool.jar" build-apks --bundle "./dist/app-release.aab" --output "./dist/app.apks" --ks "Path/To/Your-keystore.keystore" --ks-pass pass:your_keystore_password --ks-key-alias <alias-name> --key-pass pass:your_keystore_password --mode universal --verboseIf you get a file exists error, delete dist/app.apks and try again.
then extract the .apk output of the .apks using any archieve extractors. or you can do :
Windows:
# Make output folder
New-Item -ItemType Directory -Path "/path/to/extract/folder" -Force
# Rename .apks to .zip
Rename-Item -Path "path/to/app.apks" -NewName "app.zip"
# Extract zip
Expand-Archive -LiteralPath "/path/to/app.zip" -DestinationPath "/path/to/extract/folder" -ForcemacOS:
# Make output folder
mkdir -p /path/to/extract/folder
# Unzip directly without renaming
unzip /path/to/app.apks -d /path/to/extract/folderLinux:
# Make output folder
mkdir -p /path/to/extract/folder
# Unzip directly without renaming
unzip /path/to/app.apks -d /path/to/extract/folderInstall the APK on Your Device:
Connect your device and verify with:
adb devicesInstall your app:
adb install "Path/to/your/app.apk"For more information on BundleTool, refer to the BundleTool Documentation.
- Missing .aab file: Ensure the build command
python setup.py bdist_appscompletes successfully. Check for errors in the console output. - Signing errors: If signing fails, verify your keystore path, passwords, and alias. Use
keytool -list -v -keystore <keystore-name.keystore>to check the keystore. - Device connection issues: Run
adb devicesto confirm your device is connected. Enable USB debugging in developer options. - BundleTool errors: Ensure Java is installed and BundleTool is the latest version. Use
--verboseflag for more details.
- Always test on a physical device rather than emulator for accurate performance.
- If the app crashes on launch, check logs with
adb logcatfor Python errors. - Ensure all assets are included in
include_patternsin setup.py, especiallyursina_assets/**andgame/assets/**.
View your app logs with:
adb logcat -v brief -v color Panda3D:V Python:V *:SClear logs:
adb logcat -cYou can use print() in your Python code to output to logcat.
To test your game without building for Android each time, install Ursina 7.0.0 for Python 3.8:
python3.8 -m pip install src/wheels/ursina-7.0.0-py3-none-any.whlRun your game:
python3.8 src/game/__main__.py- Desktop input methods (mouse, keyboard) do not work on Android. Replace them with touch controls or virtual joysticks.
- Optimize for mobile performance and screen sizes.
- Adapt your UI and controls for touch interaction.
If you need help or want to contribute, open an issue or pull request on ShivamKR12/Echoes.
Special thanks to the Ursina and Panda3D communities for their support and inspiration.
Thank you for checking out Echoes and exploring Ursina on Android!
Here are some screenshots showcasing what the game has to offer:





