IOS, Android... same thing (#7493)

# Objective

- Merge the examples on iOS and Android
- Make sure they both work from the same code

## Solution

- don't create window when not in an active state (from #6830)
- exit on suspend on Android (from #6830)
- automatically enable dependency feature of bevy_audio on android so that it works out of the box
- don't inverse y position of touch events
- reuse the same example for both Android and iOS

Fixes #4616
Fixes #4103
Fixes #3648
Fixes #3458
Fixes #3249
Fixes #86
This commit is contained in:
François 2023-02-06 18:08:49 +00:00
parent 7b7b34f635
commit 7e0a9bfade
22 changed files with 73 additions and 130 deletions

View file

@ -44,7 +44,7 @@ jobs:
--no-push \
--exclude ci \
--exclude errors \
--exclude bevy-ios-example \
--exclude bevy_mobile_example \
--exclude build-wasm-example
- name: Create PR

View file

@ -41,7 +41,7 @@ jobs:
--dependent-version upgrade \
--exclude ci \
--exclude errors \
--exclude bevy-ios-example \
--exclude bevy_mobile_example \
--exclude build-wasm-example
- name: Create PR

View file

@ -29,7 +29,7 @@ jobs:
run: rustup target add aarch64-apple-ios x86_64-apple-ios
- name: Build and install iOS app in iOS Simulator.
run: cd examples/ios && make install
run: cd examples/mobile && make install
build-android:
runs-on: ubuntu-latest
@ -56,7 +56,7 @@ jobs:
run: cargo install --force cargo-apk
- name: Build APK
run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --package bevy-android-example
run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --package bevy_mobile_example
run-examples-on-windows-dx12:
runs-on: windows-latest

View file

@ -16,8 +16,7 @@ rust-version = "1.67.0"
exclude = ["benches", "crates/bevy_ecs_compile_fail_tests", "crates/bevy_reflect_compile_fail_tests"]
members = [
"crates/*",
"examples/android",
"examples/ios",
"examples/mobile",
"tools/ci",
"tools/build-example-pages",
"tools/build-wasm-example",
@ -44,6 +43,7 @@ default = [
"vorbis",
"x11",
"filesystem_watcher",
"android_shared_stdcxx"
]
# Force dynamic linking, which improves iterative compile times
@ -118,6 +118,9 @@ debug_asset_server = ["bevy_internal/debug_asset_server"]
# Enable animation support, and glTF animation loading
animation = ["bevy_internal/animation"]
# Enable using a shared stdlib for cxx on Android.
android_shared_stdcxx = ["bevy_internal/android_shared_stdcxx"]
[dependencies]
bevy_dylib = { path = "crates/bevy_dylib", version = "0.9.0", default-features = false, optional = true }
bevy_internal = { path = "crates/bevy_internal", version = "0.9.0", default-features = false }

View file

@ -21,6 +21,9 @@ anyhow = "1.0.4"
rodio = { version = "0.16", default-features = false }
parking_lot = "0.12.1"
[target.'cfg(target_os = "android")'.dependencies]
oboe = { version = "0.4", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
rodio = { version = "0.16", default-features = false, features = ["wasm-bindgen"] }
@ -37,3 +40,5 @@ symphonia-isomp4 = ["rodio/symphonia-isomp4"]
symphonia-mp3 = ["rodio/symphonia-mp3"]
symphonia-vorbis = ["rodio/symphonia-vorbis"]
symphonia-wav = ["rodio/symphonia-wav"]
# Enable using a shared stdlib for cxx on Android.
android_shared_stdcxx = ["oboe/shared-stdcxx"]

View file

@ -74,6 +74,9 @@ animation = ["bevy_animation", "bevy_gltf?/bevy_animation"]
# Used to disable code that is unsupported when Bevy is dynamically linked
dynamic_linking = ["bevy_diagnostic/dynamic_linking"]
# Enable using a shared stdlib for cxx on Android.
android_shared_stdcxx = ["bevy_audio/android_shared_stdcxx"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.9.0" }

View file

@ -231,7 +231,7 @@ struct WinitPersistentState {
impl Default for WinitPersistentState {
fn default() -> Self {
Self {
active: true,
active: false,
low_power_event: false,
redraw_request_sent: false,
timeout_reached: false,
@ -290,7 +290,7 @@ pub fn winit_runner(mut app: App) {
}
}
{
if winit_state.active {
#[cfg(not(target_arch = "wasm32"))]
let (commands, mut new_windows, created_window_writer, winit_windows) =
create_window_system_state.get_mut(&mut app.world);
@ -475,14 +475,7 @@ pub fn winit_runner(mut app: App) {
}
},
WindowEvent::Touch(touch) => {
let mut location =
touch.location.to_logical(window.resolution.scale_factor());
// On a mobile window, the start is from the top while on PC/Linux/OSX from
// bottom
if cfg!(target_os = "android") || cfg!(target_os = "ios") {
location.y = window.height() as f64 - location.y;
}
let location = touch.location.to_logical(window.resolution.scale_factor());
// Event
input_events
@ -616,6 +609,13 @@ pub fn winit_runner(mut app: App) {
}
event::Event::Suspended => {
winit_state.active = false;
#[cfg(target_os = "android")]
{
// Bevy doesn't support suspend/resume so we just exit
// and Android will restart the application on resume
// TODO: Save save some state and load on resume
*control_flow = ControlFlow::Exit;
}
}
event::Event::Resumed => {
winit_state.active = true;

View file

@ -367,7 +367,7 @@ When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` mus
To run on a device setup for Android development, run:
```sh
cargo apk run --example android_example
cargo apk run -p bevy_mobile_example
```
When using Bevy as a library, the following fields must be added to `Cargo.toml`:
@ -414,7 +414,7 @@ min_sdk_version = >>API or less<<
Example | File | Description
--- | --- | ---
`android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## iOS
@ -435,7 +435,7 @@ rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
Using bash:
```sh
cd examples/ios
cd examples/mobile
make run
```
@ -450,7 +450,7 @@ DEVICE_ID=${YOUR_DEVICE_ID} make run
If you'd like to see xcode do stuff, you can run
```sh
open bevy_ios_example.xcodeproj/
open bevy_mobile_example.xcodeproj/
```
which will open xcode. You then must push the zoom zoom play button and wait
@ -458,7 +458,7 @@ for the magic.
Example | File | Description
--- | --- | ---
`ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS
`ios` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## WASM

View file

@ -98,7 +98,7 @@ When using `NDK (Side by side)`, the environment variable `ANDROID_NDK_ROOT` mus
To run on a device setup for Android development, run:
```sh
cargo apk run --example android_example
cargo apk run -p bevy_mobile_example
```
When using Bevy as a library, the following fields must be added to `Cargo.toml`:
@ -145,7 +145,7 @@ min_sdk_version = >>API or less<<
Example | File | Description
--- | --- | ---
`android` | [`android/android.rs`](./android/android.rs) | The `3d/3d_scene.rs` example for Android
`android` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## iOS
@ -166,7 +166,7 @@ rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
Using bash:
```sh
cd examples/ios
cd examples/mobile
make run
```
@ -181,7 +181,7 @@ DEVICE_ID=${YOUR_DEVICE_ID} make run
If you'd like to see xcode do stuff, you can run
```sh
open bevy_ios_example.xcodeproj/
open bevy_mobile_example.xcodeproj/
```
which will open xcode. You then must push the zoom zoom play button and wait
@ -189,7 +189,7 @@ for the magic.
Example | File | Description
--- | --- | ---
`ios` | [`ios/src/lib.rs`](./ios/src/lib.rs) | The `3d/3d_scene.rs` example for iOS
`ios` | [`mobile/src/lib.rs`](./mobile/src/lib.rs) | A 3d Scene with a button and playing sound
## WASM

View file

@ -1,54 +0,0 @@
use bevy::{
prelude::*,
render::{
settings::{WgpuSettings, WgpuSettingsPriority},
RenderPlugin,
},
};
// the `bevy_main` proc_macro generates the required android boilerplate
#[bevy_main]
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(RenderPlugin {
// This configures the app to use the most compatible rendering settings.
// They help with compatibility with as many devices as possible.
wgpu_settings: WgpuSettings {
priority: WgpuSettingsPriority::Compatibility,
..default()
},
}))
.add_startup_system(setup)
.run();
}
/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// plane
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })),
material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()),
..default()
});
// cube
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
});
// light
commands.spawn(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0),
..default()
});
// camera
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
}

View file

@ -1,14 +0,0 @@
[package]
name = "bevy-ios-example"
version = "0.1.0"
edition = "2021"
description = "Example for building an iOS app with Bevy"
publish = false
license = "MIT OR Apache-2.0"
[lib]
name = "bevy_ios_example"
crate-type = ["staticlib"]
[dependencies]
bevy = { path = "../../" }

View file

@ -1,14 +1,14 @@
[package]
name = "bevy-android-example"
name = "bevy_mobile_example"
version = "0.1.0"
edition = "2021"
description = "Example for building an Android app with Bevy"
description = "Example for building an iOS or Android app with Bevy"
publish = false
license = "MIT OR Apache-2.0"
[lib]
name = "bevy_android_example"
crate-type = ["cdylib"]
name = "bevy_mobile_example"
crate-type = ["staticlib", "cdylib"]
[dependencies]
bevy = { path = "../../" }

View file

@ -6,19 +6,19 @@ ifndef DEVICE_ID
endif
run: install
xcrun simctl launch --console $(DEVICE) com.rust.bevy-ios-example
xcrun simctl launch --console $(DEVICE) com.rust.bevy_mobile_example
boot-sim:
xcrun simctl boot $(DEVICE) || true
install: xcodebuild-simulator boot-sim
xcrun simctl install $(DEVICE) build/Build/Products/Debug-iphonesimulator/bevy_ios_example.app
xcrun simctl install $(DEVICE) build/Build/Products/Debug-iphonesimulator/bevy_mobile_example.app
xcodebuild-simulator:
IOS_TARGETS=x86_64-apple-ios xcodebuild -scheme bevy_ios_example -configuration Debug -derivedDataPath build -destination "id=$(DEVICE)"
IOS_TARGETS=x86_64-apple-ios xcodebuild -scheme bevy_mobile_example -configuration Debug -derivedDataPath build -destination "id=$(DEVICE)"
xcodebuild-iphone:
IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme bevy_ios_example -configuration Debug -derivedDataPath build -arch arm64
IOS_TARGETS=aarch64-apple-ios xcodebuild -scheme bevy_mobile_example -configuration Debug -derivedDataPath build -arch arm64
clean:
rm -r build

View file

@ -28,7 +28,7 @@
/* Begin PBXFileReference section */
160DB77300A3F1806F024D47 /* bindings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = bindings.h; sourceTree = "<group>"; };
55EAC02897847195D2F44C15 /* bevy_ios_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bevy_ios_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
55EAC02897847195D2F44C15 /* bevy_mobile_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bevy_mobile_example.app; sourceTree = BUILT_PRODUCTS_DIR; };
57CD6305253C7A940098CD4A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
57CD630A253C7F5F0098CD4A /* assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = assets; path = ../../../assets; sourceTree = "<group>"; };
8EE7F1E3B0303533925D7E33 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
@ -58,7 +58,7 @@
321F7D6A765B38E746C35105 /* Products */ = {
isa = PBXGroup;
children = (
55EAC02897847195D2F44C15 /* bevy_ios_example.app */,
55EAC02897847195D2F44C15 /* bevy_mobile_example.app */,
);
name = Products;
sourceTree = "<group>";
@ -116,9 +116,9 @@
/* End PBXLegacyTarget section */
/* Begin PBXNativeTarget section */
3BDB8152E4962373181B4FE5 /* bevy_ios_example */ = {
3BDB8152E4962373181B4FE5 /* bevy_mobile_example */ = {
isa = PBXNativeTarget;
buildConfigurationList = E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "bevy_ios_example" */;
buildConfigurationList = E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "bevy_mobile_example" */;
buildPhases = (
9F13800790AD9DBC2BC0F116 /* Sources */,
D5A822CB2D6847BA8800BE4C /* Frameworks */,
@ -129,9 +129,9 @@
dependencies = (
19D4B9C22ADC6705B5132B4C /* PBXTargetDependency */,
);
name = bevy_ios_example;
productName = bevy_ios_example;
productReference = 55EAC02897847195D2F44C15 /* bevy_ios_example.app */;
name = bevy_mobile_example;
productName = bevy_mobile_example;
productReference = 55EAC02897847195D2F44C15 /* bevy_mobile_example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@ -142,7 +142,7 @@
attributes = {
LastUpgradeCheck = 1240;
};
buildConfigurationList = 9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "bevy_ios_example" */;
buildConfigurationList = 9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "bevy_mobile_example" */;
compatibilityVersion = "Xcode 10.0";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -153,7 +153,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
3BDB8152E4962373181B4FE5 /* bevy_ios_example */,
3BDB8152E4962373181B4FE5 /* bevy_mobile_example */,
D08AEBE0B1A9C9A7B8C7B33F /* cargo_ios */,
);
};
@ -290,10 +290,10 @@
);
OTHER_LDFLAGS = (
"$(inherited)",
"-lbevy_ios_example",
"-lbevy_mobile_example",
"-lc++abi",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.bevy-ios-example";
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.bevy_mobile_example";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -397,10 +397,10 @@
);
OTHER_LDFLAGS = (
"$(inherited)",
"-lbevy_ios_example",
"-lbevy_mobile_example",
"-lc++abi",
);
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.bevy-ios-example";
PRODUCT_BUNDLE_IDENTIFIER = "com.rust.bevy_mobile_example";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@ -422,7 +422,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "bevy_ios_example" */ = {
9D43D41707A5C30B227B83F9 /* Build configuration list for PBXProject "bevy_mobile_example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4AD7BC6FDD56FF18FA6DA7D7 /* Debug */,
@ -440,7 +440,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "bevy_ios_example" */ = {
E714A1AEAAE517C348B5BD27 /* Build configuration list for PBXNativeTarget "bevy_mobile_example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
A2D5B73DD30D562B6F366526 /* Debug */,

View file

@ -15,9 +15,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
BuildableName = "bevy_ios_example.app"
BlueprintName = "bevy_ios_example"
ReferencedContainer = "container:bevy_ios_example.xcodeproj">
BuildableName = "bevy_mobile_example.app"
BlueprintName = "bevy_mobile_example"
ReferencedContainer = "container:bevy_mobile_example.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@ -45,9 +45,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
BuildableName = "bevy_ios_example.app"
BlueprintName = "bevy_ios_example"
ReferencedContainer = "container:bevy_ios_example.xcodeproj">
BuildableName = "bevy_mobile_example.app"
BlueprintName = "bevy_mobile_example"
ReferencedContainer = "container:bevy_mobile_example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
@ -62,9 +62,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3BDB8152E4962373181B4FE5"
BuildableName = "bevy_ios_example.app"
BlueprintName = "bevy_ios_example"
ReferencedContainer = "container:bevy_ios_example.xcodeproj">
BuildableName = "bevy_mobile_example.app"
BlueprintName = "bevy_mobile_example"
ReferencedContainer = "container:bevy_mobile_example.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>

View file

@ -40,16 +40,16 @@ for arch in $ARCHS; do
# Intel iOS simulator
export CFLAGS_x86_64_apple_ios="-target x86_64-apple-ios"
cargo build --lib $RELFLAG --target x86_64-apple-ios
cargo rustc --crate-type staticlib --lib $RELFLAG --target x86_64-apple-ios
;;
arm64)
if [ $IS_SIMULATOR -eq 0 ]; then
# Hardware iOS targets
cargo build --lib $RELFLAG --target aarch64-apple-ios
cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios
else
# M1 iOS simulator -- currently in Nightly only and requires to build `libstd`
cargo build --lib $RELFLAG --target aarch64-apple-ios-sim
cargo rustc --crate-type staticlib --lib $RELFLAG --target aarch64-apple-ios-sim
fi
esac
done

View file

@ -1,6 +1,6 @@
use bevy::{input::touch::TouchPhase, prelude::*, window::WindowMode};
// the `bevy_main` proc_macro generates the required ios boilerplate
// the `bevy_main` proc_macro generates the required boilerplate for iOS and Android
#[bevy_main]
fn main() {
App::new()