diff --git a/.cargo/config.toml b/.cargo/config.toml index b310a20..c3738d9 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -14,6 +14,10 @@ codegen-units = 1 opt-level = "z" lto = "thin" +[profile.wasm-dev] +inherits = "dev" +codegen-backend = "llvm" + [profile.wasm-release] inherits = "release" opt-level = "s" diff --git a/TODO b/TODO new file mode 100644 index 0000000..6c3de78 --- /dev/null +++ b/TODO @@ -0,0 +1,2 @@ +* setup CI template in web server running on port 8000 +* setup mock bevy app running on port 8001 w/ first app running in iframe diff --git a/examples/game2d.rs b/examples/game2d.rs index c833c87..40a1ae2 100644 --- a/examples/game2d.rs +++ b/examples/game2d.rs @@ -5,6 +5,7 @@ fn main() { .add_plugins((BaseGamePlugin { name: "2d game example".into(), game_type: GameType::Two, + ..default() },)) .add_systems(Startup, spawn_guy) .run(); diff --git a/examples/window.rs b/examples/window.rs new file mode 100644 index 0000000..d3775fe --- /dev/null +++ b/examples/window.rs @@ -0,0 +1,34 @@ +use games::*; + +fn main() { + App::new() + .add_plugins(BaseGamePlugin { target_resolution: (360.0, 640.0).into(), ..default() }) + .add_systems(Startup, init_window_info) + .add_systems(Update, update_window_info.run_if(any_component_changed::)) + .run(); +} + +#[derive(Component)] +struct WindowInfo; + +fn init_window_info(mut commands: Commands) { + commands.spawn(( + Node { + align_self: AlignSelf::Center, + justify_self: JustifySelf::Center, + ..default() + }, + children![(Text::new("Placeholder"), WindowInfo,)], + )); +} + +fn update_window_info(mut text: Single<&mut Text, With>, window: Single<&Window>) { + text.0 = format!( + "Logical: {}x{}\nPhysical: {}x{}\nScale Factor: {}", + window.resolution.physical_width(), + window.resolution.physical_height(), + window.resolution.width(), + window.resolution.height(), + window.resolution.scale_factor(), + ); +} diff --git a/justfile b/justfile index 891a653..7172f17 100644 --- a/justfile +++ b/justfile @@ -1,26 +1,38 @@ VERSION := `git rev-parse --short HEAD` -# Build the web version -web GAME: - # base directory - mkdir -p dist/{{GAME}}/assets/{{GAME}} - - # wasm binary - cargo build --bin {{GAME}} --profile wasm-release --target wasm32-unknown-unknown +bindgen profile name: + mkdir -p ./dist/{{name}} - # wasm bindgen - wasm-bindgen --no-typescript --target web \ - --out-dir ./dist/{{GAME}} \ + wasm-bindgen \ + --no-typescript \ + --target web \ + --out-dir ./dist/{{name}} \ --out-name "bin" \ - ${CARGO_TARGET_DIR}/wasm32-unknown-unknown/release/{{GAME}}.wasm + ${CARGO_TARGET_DIR}/wasm32-unknown-unknown/{{profile}}/{{name}}.wasm +optimize NAME: # Size pass wasm-opt -Oz \ - -o dist/{{GAME}}/bin_bg-tmp.wasm \ - ./dist/{{GAME}}/bin_bg.wasm + -o dist/{{NAME}}/bin_bg-tmp.wasm \ + ./dist/{{NAME}}/bin_bg.wasm # Replace old bin with new (compressed) bin - mv ./dist/{{GAME}}/bin_bg-tmp.wasm ./dist/{{GAME}}/bin_bg.wasm + mv ./dist/{{NAME}}/bin_bg-tmp.wasm ./dist/{{NAME}}/bin_bg.wasm + +build-example EXAMPLE: + cargo build --example {{EXAMPLE}} --profile wasm-dev --target wasm32-unknown-unknown + +build-bin GAME: + # wasm binary + cargo build --bin {{GAME}} --profile wasm-release --target wasm32-unknown-unknown + +example NAME: (build-example NAME) (bindgen "wasm-dev" "examples"/NAME) + cp web/example.html ./dist/examples/{{NAME}}/index.html + +# Build the web version +web GAME: (build-bin GAME) (bindgen "wasm-release" GAME) (optimize GAME) + # base directory + mkdir -p dist/{{GAME}}/assets/{{GAME}} # index.html cp ./web/{{GAME}}.html ./dist/{{GAME}}/index.html diff --git a/src/base_game.rs b/src/base_game.rs index d994c14..db5441a 100644 --- a/src/base_game.rs +++ b/src/base_game.rs @@ -1,5 +1,3 @@ -use bevy::platform::hash::RandomState; - use super::*; /// A good starting place for creating a game building on top of the base Bevy app @@ -7,7 +5,7 @@ pub struct BaseGamePlugin { pub title: String, pub name: String, pub game_type: GameType, - pub window: Option, + pub target_resolution: WindowResolution, } pub enum GameType { @@ -21,7 +19,7 @@ impl Default for BaseGamePlugin { title: "My Game".into(), name: "mygame".into(), game_type: GameType::Three, - window: None, + target_resolution: WindowResolution::default(), } } } @@ -33,19 +31,27 @@ impl Plugin for BaseGamePlugin { .set(WindowPlugin { primary_window: Some(Window { fit_canvas_to_parent: true, - canvas: Some(format!("#{}-canvas", self.name)), - ..self.window.clone().unwrap_or_default() + resolution: self.target_resolution.clone(), + ..default() }), ..default() }) - .set(ImagePlugin::default_nearest()), + .set(ImagePlugin::default_nearest()) + .set(AssetPlugin { + meta_check: AssetMetaCheck::Never, + ..default() + }), ) .add_plugins(DebuggingPlugin) .add_plugins(MeshPickingPlugin) .add_plugins(LoadingPlugin) .add_plugins(BaseUiPlugin) .add_plugins(ParallaxPlugin) - .init_resource::(); + .insert_resource(TargetResolution(self.target_resolution.clone())) + .init_resource::() + .add_systems( + Update, scale_game.run_if(any_component_changed::) + ); match self.game_type { GameType::Two => app.add_systems(Startup, create_camera_2d), @@ -54,6 +60,9 @@ impl Plugin for BaseGamePlugin { } } +#[derive(Resource)] +struct TargetResolution(WindowResolution); + /// System to toggle the visibility of entities based on their state pub fn toggle_state_visibility( mut q: Query<(Entity, &mut Visibility, &S)>, @@ -87,3 +96,22 @@ pub fn create_camera_2d(mut commands: Commands) { #[derive(Default, Resource)] pub struct Rand(pub RandomState); + +/// Scale the game based on the difference between the target and real resolution +fn scale_game( + mut window: Single<&mut Window>, + target_resolution: Res, + mut last_scale_factor: Local, +) { + let current_resolution: Vec2 = window.resolution.size(); + let scale_width = current_resolution.x.round() / target_resolution.0.width().round(); + let scale_height = current_resolution.y.round() / target_resolution.0.height().round(); + let scale_factor = f32::min(scale_width, scale_height); + if window.resolution.scale_factor() != scale_factor { + // Need to check the previously set scale factor because the system can flip-flop otherwise + if scale_factor != *last_scale_factor { + *last_scale_factor = window.resolution.scale_factor(); + window.resolution.set_scale_factor(scale_factor); + } + } +} diff --git a/src/bin/flappy/main.rs b/src/bin/flappy/main.rs index 32abb6b..0b5f04f 100644 --- a/src/bin/flappy/main.rs +++ b/src/bin/flappy/main.rs @@ -2,12 +2,11 @@ #![allow(clippy::type_complexity)] use bevy::image::{ImageLoaderSettings, ImageSampler}; +use bevy::platform::time::Instant; use bevy::render::view::ColorGrading; -use bevy::window::WindowResolution; use games::physics2d::*; use games::*; use std::hash::BuildHasher; -use std::time::Instant; fn main() { App::new() @@ -16,6 +15,7 @@ fn main() { title: "flappy bird (with rewind)".into(), name: "flappy".into(), game_type: GameType::Two, + target_resolution: (360.0, 640.0).into(), ..default() }, Physics2dPlugin, @@ -687,17 +687,15 @@ fn init_ui(mut commands: Commands, server: Res) { )); commands - .spawn(( - Node { - align_self: AlignSelf::End, - justify_self: JustifySelf::Center, - flex_direction: FlexDirection::Row, - justify_content: JustifyContent::SpaceEvenly, - width: Val::Percent(100.0), - min_height: Val::Percent(10.0), - ..default() - }, - )) + .spawn((Node { + align_self: AlignSelf::End, + justify_self: JustifySelf::Center, + flex_direction: FlexDirection::Row, + justify_content: JustifyContent::SpaceEvenly, + width: Val::Percent(100.0), + min_height: Val::Percent(10.0), + ..default() + },)) .with_children(|parent| { let rewind_image = server.load_with_settings( "flappy/rewind.png", @@ -1281,7 +1279,13 @@ fn shimmer_button(mut bg: Single<&mut BackgroundColor, With>, t let red = (((t / period) % 1.0) * std::f32::consts::PI).cos(); let green = ((((t / period) + 0.3) % 1.0) * std::f32::consts::PI).cos(); let blue = ((((t / period) + 0.6) % 1.0) * std::f32::consts::PI).cos(); - bg.0 = Srgba { red, green, blue, alpha: bg.0.alpha() }.into(); + bg.0 = Srgba { + red, + green, + blue, + alpha: bg.0.alpha(), + } + .into(); } fn reset_button(mut bg: Single<&mut BackgroundColor, With>) { diff --git a/src/lib.rs b/src/lib.rs index c161b32..8abbec8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ pub use std::fmt::Display; // Community libraries pub use bevy::{ - asset::{AssetLoader, LoadContext, LoadState, LoadedFolder, io::Reader}, + asset::{AssetLoader, LoadContext, LoadState, LoadedFolder, io::Reader, AssetMetaCheck}, color::palettes::css::*, gizmos::{aabb::AabbGizmoPlugin, light::LightGizmoPlugin}, input::{ @@ -29,11 +29,11 @@ pub use bevy::{ mouse::{MouseScrollUnit, MouseWheel}, }, pbr::wireframe::{WireframeConfig, WireframePlugin}, - platform::collections::HashMap, + platform::{collections::HashMap, hash::RandomState}, prelude::*, reflect::TypePath, sprite::AlphaMode2d, - window::WindowResized, + window::{WindowResized, WindowResolution}, }; pub use serde::Deserialize; pub use thiserror::Error; diff --git a/web/example.html b/web/example.html new file mode 100644 index 0000000..234156d --- /dev/null +++ b/web/example.html @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/web/flappy.html b/web/flappy.html index 6113efa..234156d 100644 --- a/web/flappy.html +++ b/web/flappy.html @@ -1,15 +1,16 @@ - - + + +