diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f253e0aa58e04bf7b4e8f0a4bc26f08a78e7ca2f..405afbdbc514dcea74ed1556c0ef7dc6f4d12640 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,8 @@ image: "r.lcr.gr/microhacks/bevy-builder:latest" variables: - BINARY_NAME: game + BINARY_FOLDER: game_core + BINARY_NAME: game_core stages: - build @@ -21,11 +22,11 @@ build-windows: - .cargo/bin/ - target/ script: - - cargo build --release -p game_core --target x86_64-pc-windows-gnu + - cargo build --release -p ${BINARY_FOLDER} --target x86_64-pc-windows-gnu artifacts: expire_in: 1 day paths: - - target/x86_64-pc-windows-gnu/release/game_core.exe + - target/x86_64-pc-windows-gnu/release/${BINARY_NAME}.exe only: - trunk @@ -43,11 +44,36 @@ build-linux: - .cargo/bin/ - target/ script: - - cargo build --release -p game_core --target x86_64-unknown-linux-gnu + - cargo build --release -p ${BINARY_FOLDER} --target x86_64-unknown-linux-gnu artifacts: expire_in: 1 day paths: - - target/x86_64-unknown-linux-gnu/release/game_core + - target/x86_64-unknown-linux-gnu/release/${BINARY_NAME} + only: + - trunk + +build-arm64: + image: "r.lcr.gr/microhacks/bevy-builder:arm64" + tags: + - arm64 + stage: build + before_script: + - export CARGO_HOME="${CI_PROJECT_DIR}/.cargo" + - export PATH="${CI_PROJECT_DIR}/.cargo/bin:$PATH" + cache: + key: build-cache-arm64 + paths: + - .cargo/registry/cache + - .cargo/registry/index + - .cargo/git/db + - .cargo/bin/ + - target/ + script: + - cargo build --release -p ${BINARY_FOLDER} --target aarch64-unknown-linux-gnu + artifacts: + expire_in: 1 day + paths: + - target/aarch64-unknown-linux-gnu/release/${BINARY_NAME} only: - trunk @@ -65,13 +91,16 @@ build-web: - .cargo/bin/ - target/ script: + - mkdir -p "${CI_PROJECT_DIR}/${BINARY_FOLDER}/fonts" + - mkdir "${CI_PROJECT_DIR}/${BINARY_FOLDER}/dist" - make assets - - cd "${CI_PROJECT_DIR}/game_core" && trunk build --release + - sed -i 's#public_url = "/"#public_url = "./"#' "${CI_PROJECT_DIR}/${BINARY_FOLDER}/Trunk.toml" + - cd "${CI_PROJECT_DIR}/${BINARY_FOLDER}" && trunk build --release - cd "${CI_PROJECT_DIR}" artifacts: expire_in: 1 day paths: - - game_core/dist/ + - ${BINARY_FOLDER}/dist/ only: - trunk @@ -81,33 +110,34 @@ package-all: - mkdir -p dist/ - make assets - cp -r assets dist/assets - - cp target/x86_64-unknown-linux-gnu/release/game_core "dist/$BINARY_NAME" - - cp target/x86_64-pc-windows-gnu/release/game_core.exe "dist/$BINARY_NAME.exe" + - cp target/x86_64-unknown-linux-gnu/release/${BINARY_NAME} "dist/${BINARY_NAME}" + - cp target/x86_64-pc-windows-gnu/release/${BINARY_NAME}.exe "dist/${BINARY_NAME}.exe" + - cp target/aarch64-unknown-linux-gnu/release/${BINARY_NAME} "dist/${BINARY_NAME}.arm64" - cd "${CI_PROJECT_DIR}/dist" && zip -r "windows.zip" "./${BINARY_NAME}.exe" ./assets - - cd "${CI_PROJECT_DIR}/dist" && zip -r "linux.zip" "./${BINARY_NAME}" ./assets - - ls "${CI_PROJECT_DIR}/game_core/dist" - - cd "${CI_PROJECT_DIR}/game_core/dist" && zip -r "web.zip" ./* - - cd "${CI_PROJECT_DIR}" && mv "${CI_PROJECT_DIR}/game_core/dist/web.zip" "${CI_PROJECT_DIR}/dist/web.zip" + - cd "${CI_PROJECT_DIR}/dist" && zip -r "linux.x86.zip" "./${BINARY_NAME}" ./assets + - cd "${CI_PROJECT_DIR}/dist" && zip -r "linux.arm64.zip" "./${BINARY_NAME}.arm64" ./assets + - cd "${CI_PROJECT_DIR}/${BINARY_FOLDER}/dist" && zip -r "web.zip" ./* + - cd "${CI_PROJECT_DIR}" && mv "${CI_PROJECT_DIR}/advent/dist/web.zip" "${CI_PROJECT_DIR}/dist/web.zip" dependencies: - build-windows - build-linux + - build-arm64 - build-web artifacts: expire_in: 7 days paths: - dist/web.zip - dist/windows.zip - - dist/linux.zip + - dist/linux.x86.zip + - dist/linux.arm64.zip only: - trunk pages: stage: package script: - - ls game_core/ - - ls game_core/dist/ - mkdir -p public/ - - cp -r game_core/dist/* public/ + - cp -r ${BINARY_FOLDER}/dist/* public/ artifacts: expire_in: 7 days paths: diff --git a/game_core/src/assets/loader.rs b/game_core/src/assets/loader.rs index 351e33894063af028a8e5ff2e051579a995c3127..c5af903fd29f6ddf9e079494c65cc97466606e49 100644 --- a/game_core/src/assets/loader.rs +++ b/game_core/src/assets/loader.rs @@ -17,6 +17,26 @@ pub struct AssetTypeLoader<'w, 's> { marker: PhantomData<&'s usize>, } +macro_rules! load_basic_type { + ($name: tt, $type: ty => $key: ident) => { + pub fn $name(&mut self, assets: &[FixedAssetNameMapping]) -> Vec<Handle<$type>> { + self.load_list(assets, |loader, path, key| { + let handle: Handle<$type> = loader.asset_server.load(&path); + loader.handles.$key.insert(key, handle.clone()); + handle + }) + } + }; +} + +macro_rules! load_state { + ($container: expr => $key: ident) => { + $container + .asset_server + .get_group_load_state($container.handles.$key.values().map(|f| f.id)) + }; +} + impl<'w, 's> AssetTypeLoader<'w, 's> { fn load_list< T: Sync + Send + TypeUuid + 'static, @@ -32,13 +52,9 @@ impl<'w, 's> AssetTypeLoader<'w, 's> { .collect() } - pub fn load_images(&mut self, assets: &[FixedAssetNameMapping]) -> Vec<Handle<Image>> { - self.load_list(assets, |loader, path, key| { - let handle: Handle<Image> = loader.asset_server.load(&path); - loader.handles.images.insert(key, handle.clone()); - handle - }) - } + load_basic_type!(load_images, Image => images); + load_basic_type!(load_audio, AudioSource => sounds); + load_basic_type!(load_font, Font => fonts); pub fn load_spritesheet( &mut self, @@ -66,15 +82,6 @@ impl<'w, 's> AssetTypeLoader<'w, 's> { handle }) } - pub fn load_audio(&mut self, assets: &[FixedAssetNameMapping]) -> Vec<Handle<AudioSource>> { - self.load_list(assets, |loader, path, key| { - let handle: Handle<AudioSource> = loader.asset_server.load(&path); - - loader.handles.sounds.insert(key, handle.clone()); - - handle - }) - } pub fn get_all_load_state(&self) -> Vec<LoadState> { let image_state = self diff --git a/game_core/src/assets/resources.rs b/game_core/src/assets/resources.rs index fb22eab99655c264f6ad156543292a180ab0256a..34bb68bc1c0901b306462b4f524507f6e3887692 100644 --- a/game_core/src/assets/resources.rs +++ b/game_core/src/assets/resources.rs @@ -36,6 +36,36 @@ pub struct AssetHandles { pub images: HashMap<String, Handle<Image>>, pub atlas: HashMap<String, Handle<TextureAtlas>>, pub sounds: HashMap<String, Handle<AudioSource>>, + pub fonts: HashMap<String, Handle<Font>>, +} + +macro_rules! fetch_wrapper { + ($name: tt, $type: ty => $key: ident) => { + pub fn $name<T: ToString>(&self, name: T) -> Handle<$type> { + let key = name.to_string(); + match self.$key.get(&key) { + Some(handle) => handle.clone_weak(), + None => { + let keys = self.$key.keys(); + panic!( + "\n\nTried to fetch {} asset with a missing key: {}.\nPossible keys: {}\n\n", + stringify!($name), + name.to_string(), + keys.map(|k| format!("'{}'", k)) + .collect::<Vec<String>>() + .join(", ") + ) + } + } + } + }; +} + +impl AssetHandles { + fetch_wrapper!(image, Image => images); + fetch_wrapper!(atlas, TextureAtlas => atlas); + fetch_wrapper!(sound, AudioSource => sounds); + fetch_wrapper!(font, Font => fonts); } impl SuppliesAudio for AssetHandles { diff --git a/game_core/src/system/camera.rs b/game_core/src/system/camera.rs index 0a9a253808c767df9fdc0e71a042c4d95d654623..e7d082bdcd5c13c6ce72ba709c682198d741a04f 100644 --- a/game_core/src/system/camera.rs +++ b/game_core/src/system/camera.rs @@ -1,11 +1,14 @@ use bevy::app::App; use bevy::math::{Vec2, Vec3Swizzles}; use bevy::prelude::{ - Camera2dBundle, Commands, Component, CoreStage, Entity, Plugin, Query, Transform, With, + Camera2dBundle, Commands, Component, CoreStage, Entity, OrthographicProjection, Plugin, Query, + Transform, With, }; +use bevy::render::camera::ScalingMode; use iyes_loopless::prelude::AppLooplessStateExt; use crate::system::flow::AppState; +use crate::system::load_config::initial_size; /// A flag component to indicate which entity should be followed by the camera #[derive(Component)] @@ -16,8 +19,22 @@ pub struct GameCamera; /// System that creates a default orthographic camera, with correct tags for querying pub fn spawn_orthographic_camera(mut commands: Commands) { + let (target_width, target_height) = initial_size(); commands - .spawn_bundle(Camera2dBundle::default()) + .spawn_bundle(Camera2dBundle { + projection: OrthographicProjection { + left: -(target_width / 2.0), + right: (target_width / 2.0), + top: (target_height / 2.0), + bottom: -(target_height / 2.0), + scaling_mode: ScalingMode::Auto { + min_width: target_width, + min_height: target_height, + }, + ..Default::default() + }, + ..Default::default() + }) .insert(GameCamera); } diff --git a/game_core/src/system/load_config.rs b/game_core/src/system/load_config.rs new file mode 100644 index 0000000000000000000000000000000000000000..4eba511274240a7a2e5f0e9938e09044e7a2d484 --- /dev/null +++ b/game_core/src/system/load_config.rs @@ -0,0 +1,71 @@ +#[cfg(not(target_arch = "wasm32"))] +mod setup { + pub fn get_asset_path_string() -> String { + std::env::current_dir() + .unwrap() + .join("assets") + .to_str() + .unwrap() + .to_string() + } + + pub fn initial_size() -> (f32, f32) { + (1280.0, 720.0) + } +} + +#[cfg(target_arch = "wasm32")] +mod setup { + pub fn get_asset_path_string() -> String { + String::from("assets") + } + + #[cfg(feature = "no_aspect")] + pub fn initial_size() -> (f32, f32) { + static default_width: f32 = 1280.0; + static default_height: f32 = 720.0; + + web_sys::window() + .and_then(|window: web_sys::Window| { + let w = window + .inner_width() + .ok() + .and_then(|val| val.as_f64().map(|v| v as f32)) + .unwrap_or(default_width); + let h = window + .inner_height() + .ok() + .and_then(|val| val.as_f64().map(|v| v as f32)) + .unwrap_or(default_height); + + Some((w, h)) + }) + .unwrap_or((default_width, default_height)) + } + + #[cfg(not(feature = "no_aspect"))] + pub fn initial_size() -> (f32, f32) { + static default_width: f32 = 1280.0; + static default_height: f32 = 720.0; + static ratio: f32 = 1280.0 / 720.0; + + web_sys::window() + .and_then(|window: web_sys::Window| { + let w = window + .inner_width() + .ok() + .and_then(|val| val.as_f64().map(|v| v as f32)) + .unwrap_or(default_width); + let h = window + .inner_height() + .ok() + .and_then(|val| val.as_f64().map(|v| v as f32)) + .unwrap_or(default_height); + + Some((w, h / ratio)) + }) + .unwrap_or((default_width, default_height)) + } +} + +pub use setup::*; diff --git a/game_core/src/system/mod.rs b/game_core/src/system/mod.rs index 229ec9fc8c849bfbde53ed91b0c6b9a2a86b186f..309ab329d7595034fdc2a5fcbbe46f67f4859192 100644 --- a/game_core/src/system/mod.rs +++ b/game_core/src/system/mod.rs @@ -1,5 +1,6 @@ pub mod camera; pub mod flow; +pub mod load_config; pub mod resources; pub mod utilities; pub mod window; diff --git a/game_core/src/system/resources.rs b/game_core/src/system/resources.rs index 815c6b846a9a2be0158f240aee47133c4dce5bb8..dfb7d23f9db124be02033a3b44f26d5665a619e8 100644 --- a/game_core/src/system/resources.rs +++ b/game_core/src/system/resources.rs @@ -4,27 +4,16 @@ use bevy::render::texture::ImageSettings; use bevy::window::PresentMode; use crate::system::camera::spawn_orthographic_camera; - -#[cfg(target_arch = "wasm32")] -pub fn get_asset_path_string() -> String { - String::from("assets") -} -#[cfg(not(target_arch = "wasm32"))] -pub fn get_asset_path_string() -> String { - std::env::current_dir() - .unwrap() - .join("assets") - .to_str() - .unwrap() - .to_string() -} +use crate::system::load_config::{get_asset_path_string, initial_size}; pub struct DefaultResourcesPlugin; impl Plugin for DefaultResourcesPlugin { fn build(&self, app: &mut App) { + let (width, height) = initial_size(); + app.insert_resource(WindowDescriptor { - width: 1280.0, - height: 720.0, + width, + height, resizable: true, title: String::from("Bevy 2D Template"), present_mode: PresentMode::AutoNoVsync,