Add UI scaling and window example

main
Elijah Voigt 2 months ago
parent a201c2466b
commit 7130672c40

@ -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"

@ -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

@ -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();

@ -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::<Window>))
.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<WindowInfo>>, 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(),
);
}

@ -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

@ -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<Window>,
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::<Rand>();
.insert_resource(TargetResolution(self.target_resolution.clone()))
.init_resource::<Rand>()
.add_systems(
Update, scale_game.run_if(any_component_changed::<Window>)
);
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<S: States + Component>(
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<TargetResolution>,
mut last_scale_factor: Local<f32>,
) {
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);
}
}
}

@ -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<AssetServer>) {
));
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<T: Component>(mut bg: Single<&mut BackgroundColor, With<T>>, 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<T: Component>(mut bg: Single<&mut BackgroundColor, With<T>>) {

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

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<body style="margin: 0px;">
<script type="module">
import init from './bin.js'
init().catch((error) => {
if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
throw error;
}
});
</script>
</body>
</html>

@ -1,15 +1,16 @@
<!doctype html>
<html lang="en">
<body>
<canvas id="flappy-canvas" ></canvas>
<body style="margin: 0px;">
<script type="module">
import init from './bin.js'
import init from './bin.js'
init().catch((error) => {
if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
throw error;
}
});
init().catch((error) => {
if (!error.message.startsWith("Using exceptions for control flow, don't mind me. This isn't actually an error!")) {
throw error;
}
});
</script>
</body>
</html>

Loading…
Cancel
Save