-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathplayer.rs
153 lines (138 loc) · 4.99 KB
/
player.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! 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::world::Command,
image::{ImageLoaderSettings, ImageSampler},
prelude::*,
};
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) {
let _ = world.run_system_cached_with(spawn_player, self);
}
}
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,
Sprite {
image: player_assets.ducky.clone(),
texture_atlas: Some(TextureAtlas {
layout: texture_atlas_layout.clone(),
index: player_animation.get_atlas_index(),
}),
..default()
},
Transform::from_scale(Vec2::splat(8.0).extend(1.0)),
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),
],
}
}
}