mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
chore: remove demo project
This commit is contained in:
parent
8ef2db1d48
commit
dfb653898f
@ -43,7 +43,6 @@ dev_native = [
|
|||||||
# Enable embedded asset hot reloading for native dev builds.
|
# Enable embedded asset hot reloading for native dev builds.
|
||||||
"bevy/embedded_watcher",
|
"bevy/embedded_watcher",
|
||||||
]
|
]
|
||||||
demo = []
|
|
||||||
|
|
||||||
|
|
||||||
# Idiomatic Bevy code often triggers these lints, and the CI workflow treats them as errors.
|
# Idiomatic Bevy code often triggers these lints, and the CI workflow treats them as errors.
|
||||||
|
|||||||
131
QUICKSTART.md
131
QUICKSTART.md
@ -1,131 +0,0 @@
|
|||||||
_Brought to you by the Bevy Jam working group._
|
|
||||||
|
|
||||||
# Bevy Quickstart
|
|
||||||
|
|
||||||
This template is a great way to get started on a new [Bevy](https://bevyengine.org/) game—especially for a game jam!
|
|
||||||
Start with a [basic project structure](#write-your-game) and [CI / CD](#release-your-game) that can deploy to [itch.io](https://itch.io).
|
|
||||||
You can [try this template in your web browser!](https://the-bevy-flock.itch.io/bevy-quickstart)
|
|
||||||
|
|
||||||
[@ChristopherBiscardi](https://github.com/ChristopherBiscardi) made a video on how to use this template from start to finish:
|
|
||||||
|
|
||||||
[<img src="./docs/img/thumbnail.png" width=40% height=40% alt="A video tutorial for bevy_quickstart"/>](https://www.youtube.com/watch?v=ESBRyXClaYc)
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
We assume that you know how to use Bevy already and have seen the [official Quick Start Guide](https://bevyengine.org/learn/quick-start/introduction/).
|
|
||||||
|
|
||||||
If you're new to Bevy, the patterns used in this template may look a bit weird at first glance.
|
|
||||||
See our [Design Document](./docs/design.md) for more information on how we structured the code and why.
|
|
||||||
|
|
||||||
## Create a new game
|
|
||||||
|
|
||||||
Install [`cargo-generate`](https://github.com/cargo-generate/cargo-generate) and run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cargo generate TheBevyFlock/bevy_quickstart --branch cargo-generate
|
|
||||||
```
|
|
||||||
|
|
||||||
Then navigate to the newly generated directory and run the following commands:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git branch --move main
|
|
||||||
cargo update
|
|
||||||
git commit -am 'Initial commit'
|
|
||||||
```
|
|
||||||
|
|
||||||
Then [create a GitHub repository](https://github.com/new) and push your local repository to it.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>This template can also be set up manually.</summary>
|
|
||||||
|
|
||||||
Navigate to the top of [this GitHub repository](https://github.com/TheBevyFlock/bevy_quickstart/) and select `Use this template > Create a new repository`:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Clone your new Github repository to a local repository and push a commit with the following changes:
|
|
||||||
|
|
||||||
- Delete `LICENSE`, `README`, and `docs/` files.
|
|
||||||
- Search for and replace instances of `bevy_quickstart` with the name of your project.
|
|
||||||
- Adjust the `env` variables in [`.github/workflows/release.yaml`](./.github/workflows/release.yaml).
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Write your game
|
|
||||||
|
|
||||||
The best way to get started is to play around with what you find in [`src/demo/`](./src/demo).
|
|
||||||
|
|
||||||
This template comes with a basic project structure that you may find useful:
|
|
||||||
|
|
||||||
| Path | Description |
|
|
||||||
| -------------------------------------------------- | ------------------------------------------------------------------ |
|
|
||||||
| [`src/lib.rs`](./src/lib.rs) | App setup |
|
|
||||||
| [`src/asset_tracking.rs`](./src/asset_tracking.rs) | A high-level way to load collections of asset handles as resources |
|
|
||||||
| [`src/audio/`](./src/audio) | Marker components for sound effects and music |
|
|
||||||
| [`src/demo/`](./src/demo) | Example game mechanics & content (replace with your own code) |
|
|
||||||
| [`src/dev_tools.rs`](./src/dev_tools.rs) | Dev tools for dev builds (press \` aka backtick to toggle) |
|
|
||||||
| [`src/screens/`](./src/screens) | Splash screen, title screen, gameplay screen, etc. |
|
|
||||||
| [`src/theme/`](./src/theme) | Reusable UI widgets & theming |
|
|
||||||
|
|
||||||
Feel free to move things around however you want, though.
|
|
||||||
|
|
||||||
> [!Tip]
|
|
||||||
> Be sure to check out the [3rd-party tools](./docs/tooling.md) we recommend!
|
|
||||||
|
|
||||||
## Run your game
|
|
||||||
|
|
||||||
Running your game locally is very simple:
|
|
||||||
|
|
||||||
- Use `cargo run` to run a native dev build.
|
|
||||||
- Use [`trunk serve`](https://trunkrs.dev/) to run a web dev build.
|
|
||||||
|
|
||||||
If you're using [VS Code](https://code.visualstudio.com/), this template comes with a [`.vscode/tasks.json`](./.vscode/tasks.json) file.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Run release builds</summary>
|
|
||||||
|
|
||||||
- Use `cargo run --profile release-native --no-default-features` to run a native release build.
|
|
||||||
- Use `trunk serve --release --no-default-features` to run a web release build.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Linux dependencies</summary>
|
|
||||||
|
|
||||||
If you are using Linux, make sure you take a look at Bevy's [Linux dependencies](https://github.com/bevyengine/bevy/blob/main/docs/linux_dependencies.md).
|
|
||||||
Note that this template enables Wayland support, which requires additional dependencies as detailed in the link above.
|
|
||||||
Wayland is activated by using the `bevy/wayland` feature in the [`Cargo.toml`](./Cargo.toml).
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>(Optional) Improve your compile times</summary>
|
|
||||||
|
|
||||||
[`.cargo/config_fast_builds.toml`](./.cargo/config_fast_builds.toml) contains documentation on how to set up your environment to improve compile times.
|
|
||||||
After you've fiddled with it, rename it to `.cargo/config.toml` to enable it.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Release your game
|
|
||||||
|
|
||||||
This template uses [GitHub workflows](https://docs.github.com/en/actions/using-workflows) to run tests and build releases.
|
|
||||||
See [Workflows](./docs/workflows.md) for more information.
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
There are some known issues in Bevy that require some arcane workarounds.
|
|
||||||
To keep this template simple, we have opted not to include those workarounds.
|
|
||||||
You can read about them in the [Known Issues](./docs/known-issues.md) document.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The source code in this repository is licensed under any of the following at your option:
|
|
||||||
|
|
||||||
- [CC0-1.0 License](./LICENSE-CC0-1.0.txt)
|
|
||||||
- [MIT License](./LICENSE-MIT.txt)
|
|
||||||
- [Apache License, Version 2.0](./LICENSE-Apache-2.0.txt)
|
|
||||||
|
|
||||||
The CC0 license explicitly does not waive patent rights, but we confirm that we hold no patent rights to anything presented in this repository.
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
The [assets](./assets) in this repository are all 3rd-party. See the [credits screen](./src/screens/credits.rs) for more information.
|
|
||||||
@ -1,177 +0,0 @@
|
|||||||
//! Player sprite animation.
|
|
||||||
//! This is based on multiple examples and may be very different for your game.
|
|
||||||
//! - [Sprite flipping](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_flipping.rs)
|
|
||||||
//! - [Sprite animation](https://github.com/bevyengine/bevy/blob/latest/examples/2d/sprite_animation.rs)
|
|
||||||
//! - [Timers](https://github.com/bevyengine/bevy/blob/latest/examples/time/timers.rs)
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
audio::SoundEffect,
|
|
||||||
demo::{movement::MovementController, player::PlayerAssets},
|
|
||||||
AppSet,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
|
||||||
// Animate and play sound effects based on controls.
|
|
||||||
app.register_type::<PlayerAnimation>();
|
|
||||||
app.add_systems(
|
|
||||||
Update,
|
|
||||||
(
|
|
||||||
update_animation_timer.in_set(AppSet::TickTimers),
|
|
||||||
(
|
|
||||||
update_animation_movement,
|
|
||||||
update_animation_atlas,
|
|
||||||
trigger_step_sound_effect,
|
|
||||||
)
|
|
||||||
.chain()
|
|
||||||
.run_if(resource_exists::<PlayerAssets>)
|
|
||||||
.in_set(AppSet::Update),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the sprite direction and animation state (idling/walking).
|
|
||||||
fn update_animation_movement(
|
|
||||||
mut player_query: Query<(&MovementController, &mut Sprite, &mut PlayerAnimation)>,
|
|
||||||
) {
|
|
||||||
for (controller, mut sprite, mut animation) in &mut player_query {
|
|
||||||
let dx = controller.intent.x;
|
|
||||||
if dx != 0.0 {
|
|
||||||
sprite.flip_x = dx < 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let animation_state = if controller.intent == Vec2::ZERO {
|
|
||||||
PlayerAnimationState::Idling
|
|
||||||
} else {
|
|
||||||
PlayerAnimationState::Walking
|
|
||||||
};
|
|
||||||
animation.update_state(animation_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the animation timer.
|
|
||||||
fn update_animation_timer(time: Res<Time>, mut query: Query<&mut PlayerAnimation>) {
|
|
||||||
for mut animation in &mut query {
|
|
||||||
animation.update_timer(time.delta());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the texture atlas to reflect changes in the animation.
|
|
||||||
fn update_animation_atlas(mut query: Query<(&PlayerAnimation, &mut TextureAtlas)>) {
|
|
||||||
for (animation, mut atlas) in &mut query {
|
|
||||||
if animation.changed() {
|
|
||||||
atlas.index = animation.get_atlas_index();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If the player is moving, play a step sound effect synchronized with the
|
|
||||||
/// animation.
|
|
||||||
fn trigger_step_sound_effect(
|
|
||||||
mut commands: Commands,
|
|
||||||
player_assets: Res<PlayerAssets>,
|
|
||||||
mut step_query: Query<&PlayerAnimation>,
|
|
||||||
) {
|
|
||||||
for animation in &mut step_query {
|
|
||||||
if animation.state == PlayerAnimationState::Walking
|
|
||||||
&& animation.changed()
|
|
||||||
&& (animation.frame == 2 || animation.frame == 5)
|
|
||||||
{
|
|
||||||
let rng = &mut rand::thread_rng();
|
|
||||||
let random_step = player_assets.steps.choose(rng).unwrap();
|
|
||||||
commands.spawn((
|
|
||||||
AudioBundle {
|
|
||||||
source: random_step.clone(),
|
|
||||||
settings: PlaybackSettings::DESPAWN,
|
|
||||||
},
|
|
||||||
SoundEffect,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Component that tracks player's animation state.
|
|
||||||
/// It is tightly bound to the texture atlas we use.
|
|
||||||
#[derive(Component, Reflect)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct PlayerAnimation {
|
|
||||||
timer: Timer,
|
|
||||||
frame: usize,
|
|
||||||
state: PlayerAnimationState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Reflect, PartialEq)]
|
|
||||||
pub enum PlayerAnimationState {
|
|
||||||
Idling,
|
|
||||||
Walking,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerAnimation {
|
|
||||||
/// The number of idle frames.
|
|
||||||
const IDLE_FRAMES: usize = 2;
|
|
||||||
/// The duration of each idle frame.
|
|
||||||
const IDLE_INTERVAL: Duration = Duration::from_millis(500);
|
|
||||||
/// The number of walking frames.
|
|
||||||
const WALKING_FRAMES: usize = 6;
|
|
||||||
/// The duration of each walking frame.
|
|
||||||
const WALKING_INTERVAL: Duration = Duration::from_millis(50);
|
|
||||||
|
|
||||||
fn idling() -> Self {
|
|
||||||
Self {
|
|
||||||
timer: Timer::new(Self::IDLE_INTERVAL, TimerMode::Repeating),
|
|
||||||
frame: 0,
|
|
||||||
state: PlayerAnimationState::Idling,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn walking() -> Self {
|
|
||||||
Self {
|
|
||||||
timer: Timer::new(Self::WALKING_INTERVAL, TimerMode::Repeating),
|
|
||||||
frame: 0,
|
|
||||||
state: PlayerAnimationState::Walking,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::idling()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update animation timers.
|
|
||||||
pub fn update_timer(&mut self, delta: Duration) {
|
|
||||||
self.timer.tick(delta);
|
|
||||||
if !self.timer.finished() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.frame = (self.frame + 1)
|
|
||||||
% match self.state {
|
|
||||||
PlayerAnimationState::Idling => Self::IDLE_FRAMES,
|
|
||||||
PlayerAnimationState::Walking => Self::WALKING_FRAMES,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update animation state if it changes.
|
|
||||||
pub fn update_state(&mut self, state: PlayerAnimationState) {
|
|
||||||
if self.state != state {
|
|
||||||
match state {
|
|
||||||
PlayerAnimationState::Idling => *self = Self::idling(),
|
|
||||||
PlayerAnimationState::Walking => *self = Self::walking(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether animation changed this tick.
|
|
||||||
pub fn changed(&self) -> bool {
|
|
||||||
self.timer.finished()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return sprite index in the atlas.
|
|
||||||
pub fn get_atlas_index(&self) -> usize {
|
|
||||||
match self.state {
|
|
||||||
PlayerAnimationState::Idling => self.frame,
|
|
||||||
PlayerAnimationState::Walking => 6 + self.frame,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
//! Spawn the main level.
|
|
||||||
|
|
||||||
use bevy::{ecs::world::Command, prelude::*};
|
|
||||||
|
|
||||||
use crate::demo::player::SpawnPlayer;
|
|
||||||
|
|
||||||
pub(super) fn plugin(_app: &mut App) {
|
|
||||||
// No setup required for this plugin.
|
|
||||||
// It's still good to have a function here so that we can add some setup
|
|
||||||
// later if needed.
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [`Command`] to spawn the level.
|
|
||||||
/// Functions that accept only `&mut World` as their parameter implement [`Command`].
|
|
||||||
/// We use this style when a command requires no configuration.
|
|
||||||
pub fn spawn_level(world: &mut World) {
|
|
||||||
// The only thing we have in our level is a player,
|
|
||||||
// but add things like walls etc. here.
|
|
||||||
SpawnPlayer { max_speed: 400.0 }.apply(world);
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
//! Demo gameplay. All of these modules are only intended for demonstration
|
|
||||||
//! purposes and should be replaced with your own game logic.
|
|
||||||
//! Feel free to change the logic found here if you feel like tinkering around
|
|
||||||
//! to get a feeling for the template.
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
|
|
||||||
mod animation;
|
|
||||||
pub mod level;
|
|
||||||
mod movement;
|
|
||||||
pub mod player;
|
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
|
||||||
app.add_plugins((
|
|
||||||
animation::plugin,
|
|
||||||
movement::plugin,
|
|
||||||
player::plugin,
|
|
||||||
level::plugin,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
//! Handle player input and translate it into movement through a character
|
|
||||||
//! controller. A character controller is the collection of systems that govern
|
|
||||||
//! the movement of characters.
|
|
||||||
//!
|
|
||||||
//! In our case, the character controller has the following logic:
|
|
||||||
//! - Set [`MovementController`] intent based on directional keyboard input.
|
|
||||||
//! This is done in the `player` module, as it is specific to the player
|
|
||||||
//! character.
|
|
||||||
//! - Apply movement based on [`MovementController`] intent and maximum speed.
|
|
||||||
//! - Wrap the character within the window.
|
|
||||||
//!
|
|
||||||
//! Note that the implementation used here is limited for demonstration
|
|
||||||
//! purposes. If you want to move the player in a smoother way,
|
|
||||||
//! consider using a [fixed timestep](https://github.com/bevyengine/bevy/blob/main/examples/movement/physics_in_fixed_timestep.rs).
|
|
||||||
|
|
||||||
use bevy::{prelude::*, window::PrimaryWindow};
|
|
||||||
|
|
||||||
use crate::AppSet;
|
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
|
||||||
app.register_type::<(MovementController, ScreenWrap)>();
|
|
||||||
|
|
||||||
app.add_systems(
|
|
||||||
Update,
|
|
||||||
(apply_movement, apply_screen_wrap)
|
|
||||||
.chain()
|
|
||||||
.in_set(AppSet::Update),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// These are the movement parameters for our character controller.
|
|
||||||
/// For now, this is only used for a single player, but it could power NPCs or
|
|
||||||
/// other players as well.
|
|
||||||
#[derive(Component, Reflect)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct MovementController {
|
|
||||||
/// The direction the character wants to move in.
|
|
||||||
pub intent: Vec2,
|
|
||||||
|
|
||||||
/// Maximum speed in world units per second.
|
|
||||||
/// 1 world unit = 1 pixel when using the default 2D camera and no physics
|
|
||||||
/// engine.
|
|
||||||
pub max_speed: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for MovementController {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
intent: Vec2::ZERO,
|
|
||||||
// 400 pixels per second is a nice default, but we can still vary this per character.
|
|
||||||
max_speed: 400.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_movement(
|
|
||||||
time: Res<Time>,
|
|
||||||
mut movement_query: Query<(&MovementController, &mut Transform)>,
|
|
||||||
) {
|
|
||||||
for (controller, mut transform) in &mut movement_query {
|
|
||||||
let velocity = controller.max_speed * controller.intent;
|
|
||||||
transform.translation += velocity.extend(0.0) * time.delta_seconds();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct ScreenWrap;
|
|
||||||
|
|
||||||
fn apply_screen_wrap(
|
|
||||||
window_query: Query<&Window, With<PrimaryWindow>>,
|
|
||||||
mut wrap_query: Query<&mut Transform, With<ScreenWrap>>,
|
|
||||||
) {
|
|
||||||
let Ok(window) = window_query.get_single() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let size = window.size() + 256.0;
|
|
||||||
let half_size = size / 2.0;
|
|
||||||
for mut transform in &mut wrap_query {
|
|
||||||
let position = transform.translation.xy();
|
|
||||||
let wrapped = (position + half_size).rem_euclid(size) - half_size;
|
|
||||||
transform.translation = wrapped.extend(transform.translation.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
//! Plugin handling the player character in particular.
|
|
||||||
//! Note that this is separate from the `movement` module as that could be used
|
|
||||||
//! for other characters as well.
|
|
||||||
|
|
||||||
use bevy::{
|
|
||||||
ecs::{system::RunSystemOnce as _, world::Command},
|
|
||||||
prelude::*,
|
|
||||||
render::texture::{ImageLoaderSettings, ImageSampler},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
asset_tracking::LoadResource,
|
|
||||||
demo::{
|
|
||||||
animation::PlayerAnimation,
|
|
||||||
movement::{MovementController, ScreenWrap},
|
|
||||||
},
|
|
||||||
screens::Screen,
|
|
||||||
AppSet,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
|
||||||
app.register_type::<Player>();
|
|
||||||
app.load_resource::<PlayerAssets>();
|
|
||||||
|
|
||||||
// Record directional input as movement controls.
|
|
||||||
app.add_systems(
|
|
||||||
Update,
|
|
||||||
record_player_directional_input.in_set(AppSet::RecordInput),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Default, Reflect)]
|
|
||||||
#[reflect(Component)]
|
|
||||||
pub struct Player;
|
|
||||||
|
|
||||||
/// A command to spawn the player character.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SpawnPlayer {
|
|
||||||
/// See [`MovementController::max_speed`].
|
|
||||||
pub max_speed: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Command for SpawnPlayer {
|
|
||||||
fn apply(self, world: &mut World) {
|
|
||||||
world.run_system_once_with(self, spawn_player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spawn_player(
|
|
||||||
In(config): In<SpawnPlayer>,
|
|
||||||
mut commands: Commands,
|
|
||||||
player_assets: Res<PlayerAssets>,
|
|
||||||
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
|
|
||||||
) {
|
|
||||||
// A texture atlas is a way to split one image with a grid into multiple
|
|
||||||
// sprites. By attaching it to a [`SpriteBundle`] and providing an index, we
|
|
||||||
// can specify which section of the image we want to see. We will use this
|
|
||||||
// to animate our player character. You can learn more about texture atlases in
|
|
||||||
// this example: https://github.com/bevyengine/bevy/blob/latest/examples/2d/texture_atlas.rs
|
|
||||||
let layout = TextureAtlasLayout::from_grid(UVec2::splat(32), 6, 2, Some(UVec2::splat(1)), None);
|
|
||||||
let texture_atlas_layout = texture_atlas_layouts.add(layout);
|
|
||||||
let player_animation = PlayerAnimation::new();
|
|
||||||
|
|
||||||
commands.spawn((
|
|
||||||
Name::new("Player"),
|
|
||||||
Player,
|
|
||||||
SpriteBundle {
|
|
||||||
texture: player_assets.ducky.clone(),
|
|
||||||
transform: Transform::from_scale(Vec2::splat(8.0).extend(1.0)),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
TextureAtlas {
|
|
||||||
layout: texture_atlas_layout.clone(),
|
|
||||||
index: player_animation.get_atlas_index(),
|
|
||||||
},
|
|
||||||
MovementController {
|
|
||||||
max_speed: config.max_speed,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
ScreenWrap,
|
|
||||||
player_animation,
|
|
||||||
StateScoped(Screen::Gameplay),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_player_directional_input(
|
|
||||||
input: Res<ButtonInput<KeyCode>>,
|
|
||||||
mut controller_query: Query<&mut MovementController, With<Player>>,
|
|
||||||
) {
|
|
||||||
// Collect directional input.
|
|
||||||
let mut intent = Vec2::ZERO;
|
|
||||||
if input.pressed(KeyCode::KeyW) || input.pressed(KeyCode::ArrowUp) {
|
|
||||||
intent.y += 1.0;
|
|
||||||
}
|
|
||||||
if input.pressed(KeyCode::KeyS) || input.pressed(KeyCode::ArrowDown) {
|
|
||||||
intent.y -= 1.0;
|
|
||||||
}
|
|
||||||
if input.pressed(KeyCode::KeyA) || input.pressed(KeyCode::ArrowLeft) {
|
|
||||||
intent.x -= 1.0;
|
|
||||||
}
|
|
||||||
if input.pressed(KeyCode::KeyD) || input.pressed(KeyCode::ArrowRight) {
|
|
||||||
intent.x += 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize so that diagonal movement has the same speed as
|
|
||||||
// horizontal and vertical movement.
|
|
||||||
// This should be omitted if the input comes from an analog stick instead.
|
|
||||||
let intent = intent.normalize_or_zero();
|
|
||||||
|
|
||||||
// Apply movement intent to controllers.
|
|
||||||
for mut controller in &mut controller_query {
|
|
||||||
controller.intent = intent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Asset, Reflect, Clone)]
|
|
||||||
pub struct PlayerAssets {
|
|
||||||
// This #[dependency] attribute marks the field as a dependency of the Asset.
|
|
||||||
// This means that it will not finish loading until the labeled asset is also loaded.
|
|
||||||
#[dependency]
|
|
||||||
pub ducky: Handle<Image>,
|
|
||||||
#[dependency]
|
|
||||||
pub steps: Vec<Handle<AudioSource>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PlayerAssets {
|
|
||||||
pub const PATH_DUCKY: &'static str = "images/ducky.png";
|
|
||||||
pub const PATH_STEP_1: &'static str = "audio/sound_effects/step1.ogg";
|
|
||||||
pub const PATH_STEP_2: &'static str = "audio/sound_effects/step2.ogg";
|
|
||||||
pub const PATH_STEP_3: &'static str = "audio/sound_effects/step3.ogg";
|
|
||||||
pub const PATH_STEP_4: &'static str = "audio/sound_effects/step4.ogg";
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromWorld for PlayerAssets {
|
|
||||||
fn from_world(world: &mut World) -> Self {
|
|
||||||
let assets = world.resource::<AssetServer>();
|
|
||||||
Self {
|
|
||||||
ducky: assets.load_with_settings(
|
|
||||||
PlayerAssets::PATH_DUCKY,
|
|
||||||
|settings: &mut ImageLoaderSettings| {
|
|
||||||
// Use `nearest` image sampling to preserve the pixel art style.
|
|
||||||
settings.sampler = ImageSampler::nearest();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
steps: vec![
|
|
||||||
assets.load(PlayerAssets::PATH_STEP_1),
|
|
||||||
assets.load(PlayerAssets::PATH_STEP_2),
|
|
||||||
assets.load(PlayerAssets::PATH_STEP_3),
|
|
||||||
assets.load(PlayerAssets::PATH_STEP_4),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,7 @@
|
|||||||
mod asset_tracking;
|
mod asset_tracking;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
#[cfg(feature = "demo")]
|
|
||||||
mod demo;
|
|
||||||
#[cfg(feature = "dev")]
|
#[cfg(feature = "dev")]
|
||||||
mod dev_tools;
|
mod dev_tools;
|
||||||
#[cfg(not(feature = "demo"))]
|
|
||||||
mod maze;
|
mod maze;
|
||||||
mod screens;
|
mod screens;
|
||||||
mod theme;
|
mod theme;
|
||||||
@ -60,9 +57,6 @@ impl Plugin for AppPlugin {
|
|||||||
// Add other plugins.
|
// Add other plugins.
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
asset_tracking::plugin,
|
asset_tracking::plugin,
|
||||||
#[cfg(feature = "demo")]
|
|
||||||
demo::plugin,
|
|
||||||
#[cfg(not(feature = "demo"))]
|
|
||||||
maze::plugin::MazePlugin,
|
maze::plugin::MazePlugin,
|
||||||
screens::plugin,
|
screens::plugin,
|
||||||
theme::plugin,
|
theme::plugin,
|
||||||
|
|||||||
@ -51,7 +51,7 @@ impl MazeConfig {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
radius: radius as u32,
|
radius,
|
||||||
height,
|
height,
|
||||||
hex_size,
|
hex_size,
|
||||||
start_pos,
|
start_pos,
|
||||||
|
|||||||
@ -39,7 +39,7 @@ pub(super) fn setup_maze(
|
|||||||
))
|
))
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
for tile in maze.values() {
|
for tile in maze.values() {
|
||||||
spawn_single_hex_tile(parent, &assets, tile, &config)
|
spawn_single_hex_tile(parent, &assets, tile, config)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ pub(super) fn spawn_single_hex_tile(
|
|||||||
|
|
||||||
parent
|
parent
|
||||||
.spawn((
|
.spawn((
|
||||||
Name::new(format!("Hex {}", tile.to_string())),
|
Name::new(format!("Hex {}", tile)),
|
||||||
MazeTile,
|
MazeTile,
|
||||||
PbrBundle {
|
PbrBundle {
|
||||||
mesh: assets.hex_mesh.clone(),
|
mesh: assets.hex_mesh.clone(),
|
||||||
@ -33,7 +33,7 @@ pub(super) fn spawn_single_hex_tile(
|
|||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.with_children(|parent| spawn_walls(parent, assets, config, &tile.walls()));
|
.with_children(|parent| spawn_walls(parent, assets, config, tile.walls()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, config: &MazeConfig, walls: &Walls) {
|
fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, config: &MazeConfig, walls: &Walls) {
|
||||||
|
|||||||
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
||||||
|
|
||||||
#[cfg(feature = "demo")]
|
|
||||||
use crate::demo::level::spawn_level as spawn_level_command;
|
|
||||||
#[cfg(not(feature = "demo"))]
|
|
||||||
use crate::maze::spawn_maze as spawn_level_command;
|
use crate::maze::spawn_maze as spawn_level_command;
|
||||||
use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen};
|
use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user