diff --git a/docs/design.md b/docs/design.md deleted file mode 100644 index 84008b7..0000000 --- a/docs/design.md +++ /dev/null @@ -1,353 +0,0 @@ -# Design philosophy - -The high-level goal of this template is to feel like the official template that is currently missing from Bevy. -The exists an [official CI template](https://github.com/bevyengine/bevy_github_ci_template), but, in our opinion, -that one is currently more of an extension to the [Bevy examples](https://bevyengine.org/examples/) than an actual template. -We say this because it is extremely bare-bones and as such does not provide things that in practice are necessary for game development. - -## Principles - -So, how would an official template that is built for real-world game development look like? -The Bevy Jam working group has agreed on the following guiding design principles: - -- Show how to do things in pure Bevy. This means using no 3rd-party dependencies. -- Have some basic game code written out already. -- Have everything outside of code already set up. - - Nice IDE support. - - `cargo-generate` support. - - Workflows that provide CI and CD with an auto-publish to itch.io. - - Builds configured for performance by default. -- Answer questions that will quickly come up when creating an actual game. - - How do I structure my code? - - How do I preload assets? - - What are best practices for creating UI? - - etc. - -The last point means that in order to make this template useful for real-life projects, -we have to make some decisions that are necessarily opinionated. - -These opinions are based on the experience of the Bevy Jam working group and -what we have found to be useful in our own projects. -If you disagree with any of these, it should be easy to change them. - -Bevy is still young, and many design patterns are still being discovered and refined. -Most do not even have an agreed name yet. For some prior work in this area that inspired us, -see [the Unofficial Bevy Cheatbook](https://bevy-cheatbook.github.io/) and [bevy_best_practices](https://github.com/tbillington/bevy_best_practices). - -## Pattern Table of Contents - -- [Plugin Organization](#plugin-organization) -- [Widgets](#widgets) -- [Asset Preloading](#asset-preloading) -- [Spawn Commands](#spawn-commands) -- [Interaction Callbacks](#interaction-callbacks) -- [Dev Tools](#dev-tools) -- [Screen States](#screen-states) - -When talking about these, use their name followed by "pattern", -e.g. "the widgets pattern", or "the plugin organization pattern". - -## Plugin Organization - -### Pattern - -Structure your code into plugins like so: - -```rust -// game.rs -mod player; -mod enemy; -mod powerup; - -use bevy::prelude::*; - -pub(super) fn plugin(app: &mut App) { - app.add_plugins((player::plugin, enemy::plugin, powerup::plugin)); -} -``` - -```rust -// player.rs / enemy.rs / powerup.rs -use bevy::prelude::*; - -pub(super) fn plugin(app: &mut App) { - app.add_systems(Update, (your, systems, here)); -} -``` - -### Reasoning - -Bevy is great at organizing code into plugins. The most lightweight way to do this is by using simple functions as plugins. -By splitting your code like this, you can easily keep all your systems and resources locally grouped. Everything that belongs to the `player` is only in `player.rs`, and so on. - -A good rule of thumb is to have one plugin per file, -but feel free to leave out a plugin if your file does not need to do anything with the `App`. - -## Widgets - -### Pattern - -Spawn your UI elements by extending the [`Widgets` trait](../src/theme/widgets.rs): - -```rust -pub trait Widgets { - fn button(&mut self, text: impl Into) -> EntityCommands; - fn header(&mut self, text: impl Into) -> EntityCommands; - fn label(&mut self, text: impl Into) -> EntityCommands; - fn text_input(&mut self, text: impl Into) -> EntityCommands; - fn image(&mut self, texture: Handle) -> EntityCommands; - fn progress_bar(&mut self, progress: f32) -> EntityCommands; -} -``` - -### Reasoning - -This pattern is inspired by [sickle_ui](https://github.com/UmbraLuminosa/sickle_ui). -`Widgets` is implemented for `Commands` and similar, so you can easily spawn UI elements in your systems. -By encapsulating a widget inside a function, you save on a lot of boilerplate code and can easily change the appearance of all widgets of a certain type. -By returning `EntityCommands`, you can easily chain multiple widgets together and insert children into a parent widget. - -## Asset Preloading - -### Pattern - -Define your assets with a resource that maps asset paths to `Handle`s. -If you're defining the assets in code, add their paths as constants. -Otherwise, load them dynamically from e.g. a file. - -```rust -#[derive(Resource, Debug, Deref, DerefMut, Reflect)] -#[reflect(Resource)] -pub struct ImageHandles(HashMap>); - -impl ImageHandles { - pub const PATH_PLAYER: &'static str = "images/player.png"; - pub const PATH_ENEMY: &'static str = "images/enemy.png"; - pub const PATH_POWERUP: &'static str = "images/powerup.png"; -} - -impl FromWorld for ImageHandles { - fn from_world(world: &mut World) -> Self { - let asset_server = world.resource::(); - - let paths = [ - ImageHandles::PATH_PLAYER, - ImageHandles::PATH_ENEMY, - ImageHandles::PATH_POWERUP, - ]; - let map = paths - .into_iter() - .map(|path| (path.to_string(), asset_server.load(path))) - .collect(); - - Self(map) - } -} -``` - -Then start preloading in the `assets::plugin`: - -```rust -pub(super) fn plugin(app: &mut App) { - app.register_type::(); - app.init_resource::(); -} -``` - -And finally add a loading check to the `screens::loading::plugin`: - -```rust -fn all_assets_loaded( - image_handles: Res, -) -> bool { - image_handles.all_loaded(&asset_server) -} -``` - -### Reasoning - -This pattern is inspired by [bevy_asset_loader](https://github.com/NiklasEi/bevy_asset_loader). -By preloading your assets, you can avoid hitches during gameplay. -We start loading as soon as the app starts and wait for all assets to be loaded in the loading screen. - -By using strings as keys, you can dynamically load assets based on input data such as a level file. -If you prefer a purely static approach, you can also use an `enum YourAssetHandleKey` and `impl AsRef for YourAssetHandleKey`. -You can also mix the dynamic and static approach according to your needs. - -## Spawn Commands - -### Pattern - -Spawn a game object by using a custom command. Inside the command, -run the spawning code with `world.run_system_once` or `world.run_system_once_with`: - -```rust -// monster.rs - -#[derive(Debug)] -pub struct SpawnMonster { - pub health: u32, - pub transform: Transform, -} - -impl Command for SpawnMonster { - fn apply(self, world: &mut World) { - world.run_system_once_with(self, spawn_monster); - } -} - -fn spawn_monster( - spawn_monster: In, - mut commands: Commands, -) { - commands.spawn(( - Name::new("Monster"), - Health::new(spawn_monster.health), - SpatialBundle::from_transform(spawn_monster.transform), - // other components - )); -} -``` - -And then to use a spawn command, add it to `Commands`: - -```rust -// dangerous_forest.rs - -fn spawn_forest_goblin(mut commands: Commands) { - commands.add(SpawnMonster { - health: 100, - transform: Transform::from_xyz(10.0, 0.0, 0.0), - }); -} -``` - -### Reasoning - -By encapsulating the spawning of a game object in a custom command, -you save on boilerplate code and can easily change the behavior of spawning. -We use `world.run_system_once_with` to run the spawning code with the same syntax as a regular system. -That way you can easily add system parameters to access things like assets and resources while spawning the entity. - -A limitation of this approach is that calling code cannot extend the spawn call with additional components or children, -as custom commands don't return `Entity` or `EntityCommands`. This kind of usage will be possible in future Bevy versions. - -## Interaction Callbacks - -### Pattern - -When spawning an entity that can be interacted with, such as a button that can be pressed, -use an observer to handle the interaction: - -```rust -fn spawn_button(mut commands: Commands) { - // See the Widgets pattern for information on the `button` method - commands.button("Pay up!").observe(pay_money); -} - -fn pay_money(_trigger: Trigger, mut money: ResMut) { - money.0 -= 10.0; -} -``` - -The event `OnPress`, which is [defined in this template](../src/theme/interaction.rs), -is triggered when the button is [`Interaction::Pressed`](https://docs.rs/bevy/latest/bevy/prelude/enum.Interaction.html#variant.Pressed). - -If you have many interactions that only change a state, consider using the following helper function: - -```rust -fn spawn_button(mut commands: Commands) { - commands.button("Play the game").observe(enter_state(Screen::Gameplay)); -} - -fn enter_state( - new_state: S, -) -> impl Fn(Trigger, ResMut>) { - move |_trigger, mut next_state| next_state.set(new_state.clone()) -} -``` - -### Reasoning - -This pattern is inspired by [bevy_mod_picking](https://github.com/aevyrie/bevy_mod_picking). -By pairing the system handling the interaction with the entity as an observer, -the code running on interactions can be scoped to the exact context of the interaction. - -For example, the code for what happens when you press a *specific* button is directly attached to that exact button. - -This also keeps the interaction logic close to the entity that is interacted with, -allowing for better code organization. - -## Dev Tools - -### Pattern - -Add all systems that are only relevant while developing the game to the [`dev_tools` plugin](../src/dev_tools.rs): - -```rust -// dev_tools.rs -pub(super) fn plugin(app: &mut App) { - app.add_systems(Update, (draw_debug_lines, show_debug_console, show_fps_counter)); -} -``` - -### Reasoning - -The `dev_tools` plugin is only included in dev builds. -By adding your dev tools here, you automatically guarantee that they are not included in release builds. - -## Screen States - -### Pattern - -Use the [`Screen`](../src/screen/mod.rs) enum to represent your game's screens as states: - -```rust -#[derive(States, Debug, Hash, PartialEq, Eq, Clone, Default)] -pub enum Screen { - #[default] - Splash, - Loading, - Title, - Gameplay, - Victory, - Leaderboard, - MultiplayerLobby, - SecretMinigame, -} -``` - -Constrain entities that should only be present in a certain screen to that screen by adding a -[`StateScoped`](https://docs.rs/bevy/latest/bevy/prelude/struct.StateScoped.html) component to them. -Transition between screens by setting the [`NextState`](https://docs.rs/bevy/latest/bevy/prelude/enum.NextState.html) resource. - -For each screen, create a plugin that handles the setup and teardown of the screen with `OnEnter` and `OnExit`: - -```rust -// game_over.rs -pub(super) fn plugin(app: &mut App) { - app.add_systems(OnEnter(Screen::Victory), show_victory_screen); - app.add_systems(OnExit(Screen::Victory), reset_highscore); -} - -fn show_victory_screen(mut commands: Commands) { - commands. - .ui_root() - .insert((Name::new("Victory screen"), StateScoped(Screen::Victory))) - .with_children(|parent| { - // Spawn UI elements. - }); -} - -fn reset_highscore(mut highscore: ResMut) { - *highscore = default(); -} -``` - -### Reasoning - -"Screen" is not meant as a physical screen, but as "what kind of screen is the game showing right now", e.g. the title screen, the loading screen, the credits screen, the victory screen, etc. -These screens usually correspond to different logical states of your game that have different systems running. - -By using dedicated `State`s for each screen, you can easily manage systems and entities that are only relevant for a certain screen. -This allows you to flexibly transition between screens whenever your game logic requires it. diff --git a/docs/img/readme-manual-setup.png b/docs/img/readme-manual-setup.png deleted file mode 100644 index e4a6099..0000000 Binary files a/docs/img/readme-manual-setup.png and /dev/null differ diff --git a/docs/img/thumbnail.png b/docs/img/thumbnail.png deleted file mode 100644 index abeaa5c..0000000 Binary files a/docs/img/thumbnail.png and /dev/null differ diff --git a/docs/img/workflow-dispatch-release.png b/docs/img/workflow-dispatch-release.png deleted file mode 100644 index f7a01f1..0000000 Binary files a/docs/img/workflow-dispatch-release.png and /dev/null differ diff --git a/docs/img/workflow-itch-release.png b/docs/img/workflow-itch-release.png deleted file mode 100644 index ab96972..0000000 Binary files a/docs/img/workflow-itch-release.png and /dev/null differ diff --git a/docs/img/workflow-ruleset.png b/docs/img/workflow-ruleset.png deleted file mode 100644 index 1469dcd..0000000 Binary files a/docs/img/workflow-ruleset.png and /dev/null differ diff --git a/docs/img/workflow-secrets.png b/docs/img/workflow-secrets.png deleted file mode 100644 index 3ecfa8f..0000000 Binary files a/docs/img/workflow-secrets.png and /dev/null differ diff --git a/docs/known-issues.md b/docs/known-issues.md deleted file mode 100644 index 68bcce8..0000000 --- a/docs/known-issues.md +++ /dev/null @@ -1,27 +0,0 @@ -# Known Issues - -## My audio is stuttering on web - -There are a number of issues with audio on web, so this is not an exhaustive list. The short version is that you can try the following: - -- If you use materials, make sure to force render pipelines to [load at the start of the game](https://github.com/rparrett/bevy_pipelines_ready/blob/main/src/lib.rs). -- Keep the FPS high. -- Advise your users to play on Chromium-based browsers. -- Apply the suggestions from the blog post [Workaround for the Choppy Music in Bevy Web Builds](https://necrashter.github.io/bevy-choppy-music-workaround). - -## My game window is flashing white for a split second when I start the game on native - -The game window is created before the GPU is ready to render everything. -This means that it will start with a white screen for a little bit. -The workaround is to [spawn the Window hidden](https://github.com/bevyengine/bevy/blob/release-0.14.0/examples/window/window_settings.rs#L29-L32) -and then [make it visible after a few frames](https://github.com/bevyengine/bevy/blob/release-0.14.0/examples/window/window_settings.rs#L56-L64). - -## My character or camera is not moving smoothly - -Choppy movement is often caused by movement updates being tied to the frame rate. -See the [physics_in_fixed_timestep](https://github.com/bevyengine/bevy/blob/main/examples/movement/physics_in_fixed_timestep.rs) example -for how to fix this. - -A camera not moving smoothly is pretty much always caused by the camera position being tied too tightly to the character's position. -To give the camera some inertia, use the [`smooth_nudge`](https://github.com/bevyengine/bevy/blob/main/examples/movement/smooth_follow.rs#L127-L142) -to interpolate the camera position towards its target position. diff --git a/docs/tooling.md b/docs/tooling.md deleted file mode 100644 index 6ed70e2..0000000 --- a/docs/tooling.md +++ /dev/null @@ -1,64 +0,0 @@ -# Recommended 3rd-party tools - -Check out the [Bevy Assets](https://bevyengine.org/assets/) page for more great options. - -## Libraries - -A few libraries that the authors of this template have vetted and think you might find useful: - -| Name | Category | Description | -| -------------------------------------------------------------------------------------- | -------------- | ------------------------------------- | -| [`leafwing-input-manager`](https://github.com/Leafwing-Studios/leafwing-input-manager) | Input | Input -> Action mapping | -| [`bevy_mod_picking`](https://github.com/aevyrie/bevy_mod_picking) | Input | Advanced mouse interaction | -| [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) | Debugging | Live entity inspector | -| [`bevy_mod_debugdump`](https://github.com/jakobhellermann/bevy_mod_debugdump) | Debugging | Schedule inspector | -| [`avian`](https://github.com/Jondolf/avian) | Physics | Physics engine | -| [`bevy_rapier`](https://github.com/dimforge/bevy_rapier) | Physics | Physics engine (not ECS-driven) | -| [`bevy_common_assets`](https://github.com/NiklasEi/bevy_common_assets) | Asset loading | Asset loaders for common file formats | -| [`bevy_asset_loader`](https://github.com/NiklasEi/bevy_asset_loader) | Asset loading | Asset management tools | -| [`iyes_progress`](https://github.com/IyesGames/iyes_progress) | Asset loading | Progress tracking | -| [`bevy_kira_audio`](https://github.com/NiklasEi/bevy_kira_audio) | Audio | Advanced audio | -| [`sickle_ui`](https://github.com/UmbraLuminosa/sickle_ui) | UI | UI widgets | -| [`bevy_egui`](https://github.com/mvlabat/bevy_egui) | UI / Debugging | UI framework (great for debug UI) | -| [`tiny_bail`](https://github.com/benfrankel/tiny_bail) | Error handling | Error handling macros | - -In particular: - -- `leafwing-input-manager` and `bevy_mod_picking` are very likely to be upstreamed into Bevy in the near future. -- `bevy-inspector-egui` and `bevy_mod_debugdump` help fill the gap until Bevy has its own editor. -- `avian` or `bevy_rapier` helps fill the gap until Bevy has its own physics engine. `avian` is easier to use, while `bevy_rapier` is more performant. -- `sickle_ui` is well-aligned with `bevy_ui` and helps fill the gap until Bevy has a full collection of UI widgets. - -None of these are necessary, but they can save you a lot of time and effort. - -## VS Code extensions - -If you're using [VS Code](https://code.visualstudio.com/), the following extensions are highly recommended: - -| Name | Description | -|-----------------------------------------------------------------------------------------------------------|-----------------------------------| -| [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) | Rust support | -| [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) | TOML support | -| [vscode-ron](https://marketplace.visualstudio.com/items?itemName=a5huynh.vscode-ron) | RON support | -| [Dependi](https://marketplace.visualstudio.com/items?itemName=fill-labs.dependi) | `crates.io` dependency resolution | -| [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) | `.editorconfig` support | - -> [!Note] ->
-> About the included rust-analyzer settings -> -> This template sets [`rust-analyzer.cargo.targetDir`](https://rust-analyzer.github.io/generated_config.html#rust-analyzer.cargo.targetDir) -> to `true` in [`.vscode/settings.json`](../.vscode/settings.json). -> -> This makes `rust-analyzer` use a different `target` directory than `cargo`, -> which means that you can run commands like `cargo run` even while `rust-analyzer` is still indexing. -> As a trade-off, this will use more disk space. -> -> If that is an issue for you, you can set it to `false` or remove the setting entirely. ->
- -## Other templates - -There are many other Bevy templates out there. -Check out the [templates category](https://bevyengine.org/assets/#templates) on Bevy Assets for more options. -Even if you don't end up using them, they are a great way to learn how to implement certain features you might be interested in. diff --git a/docs/workflows.md b/docs/workflows.md deleted file mode 100644 index 9d59766..0000000 --- a/docs/workflows.md +++ /dev/null @@ -1,125 +0,0 @@ -# Workflows - -This template uses [GitHub workflows](https://docs.github.com/en/actions/using-workflows) for [CI / CD](https://www.redhat.com/en/topics/devops/what-is-ci-cd), defined in [`.github/workflows/`](../.github/workflows). - -## CI (testing) - -The [CI workflow](.github/workflows/ci.yaml) will trigger on every commit or PR to `main`, and do the following: - -- Run tests. -- Run Clippy lints. -- Check formatting. -- Check documentation. - -> [!Tip] ->
-> You may want to set up a GitHub ruleset to require that all commits to main pass CI. -> -> A screenshot showing a GitHub ruleset with status checks enabled ->
- -## CD (releasing) - -The [CD workflow](../.github/workflows/release.yaml) will trigger on every pushed tag in the format `v1.2.3`, and do the following: - -- Create a release build for Windows, macOS, Linux, and web. -- (Optional) Upload to [GitHub releases](https://docs.github.com/en/repositories/releasing-projects-on-github). -- (Optional) Upload to [itch.io](https://itch.io). - -
- This workflow can also be triggered manually. - - In your GitHub repository, navigate to `Actions > Release > Run workflow`: - - ![A screenshot showing a manually triggered workflow on GitHub Actions](./img/workflow-dispatch-release.png) - - Enter a version number in the format `v1.2.3`, then hit the green `Run workflow` button. -
- -> [!Important] -> Using this workflow requires some setup. We will go through this now. - -### Configure environment variables - -The release workflow can be configured by tweaking the environment variables in [`.github/workflows/release.yaml`](../.github/workflows/release.yaml). - -
- Click here for a list of variables and how they're used. - - ```yaml - # The base filename of the binary produced by `cargo build`. - cargo_build_binary_name: bevy_quickstart - - # The path to the assets directory. - assets_path: assets - - # Whether to upload the packages produced by this workflow to a GitHub release. - upload_to_github: true - - # The itch.io project to upload to in the format `user-name/project-name`. - # There will be no upload to itch.io if this is commented out. - upload_to_itch: the-bevy-flock/bevy-quickstart - - ############ - # ADVANCED # - ############ - - # The ID of the app produced by this workflow. - # Applies to macOS releases. - # Must contain only A-Z, a-z, 0-9, hyphens, and periods: https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier - app_id: the-bevy-flock.bevy-quickstart - - # The base filename of the binary in the package produced by this workflow. - # Applies to Windows, macOS, and Linux releases. - # Defaults to `cargo_build_binary_name` if commented out. - app_binary_name: bevy_quickstart - - # The name of the `.zip` or `.dmg` file produced by this workflow. - # Defaults to `app_binary_name` if commented out. - app_package_name: bevy_quickstart - - # The display name of the app produced by this workflow. - # Applies to macOS releases. - # Defaults to `app_package_name` if commented out. - app_display_name: Bevy Quickstart - - # The short display name of the app produced by this workflow. - # Applies to macOS releases. - # Must be 15 or fewer characters: https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundlename - # Defaults to `app_display_name` if commented out. - app_short_name: Bevy Quickstart - - # Before enabling LFS, please take a look at GitHub's documentation for costs and quota limits: - # https://docs.github.com/en/repositories/working-with-files/managing-large-files/about-storage-and-bandwidth-usage - git_lfs: false - ``` - -
- -The values are set automatically by `cargo generate`, or you can edit them yourself and push a commit. - -### Set up itch.io upload - -#### Add butler credentials - -
- In your GitHub repository, navigate to Settings > Secrets and variables > Actions. - - ![A screenshot showing where to add secrets in the GitHub Actions settings](./img/workflow-secrets.png) -
- -Hit `New repository secret` and enter the following values, then hit `Add secret`: - -- **Name:** `BUTLER_CREDENTIALS` -- **Secret:** Your [itch.io API key](https://itch.io/user/settings/api-keys) (create a new one if necessary) - -#### Create itch.io project - -Create a new itch.io project with the same user and project name as in the `upload_to_itch` variable in [`.github/workflows/release.yaml`](../.github/workflows/release.yaml). -Hit `Save & view page` at the bottom of the page. - -[Trigger the release workflow](#cd-releasing) for the first time. Once it's done, go back to itch.io and hit `Edit game` in the top left. - -Set `Kind of project` to `HTML`, then find the newly uploaded `web` build and tick the box that says "This file will be played in the browser". - -![A screenshot showing a web build selected in the itch.io uploads](img/workflow-itch-release.png)