feat: add basic player movement
Former-commit-id: 5efbf1711d0021bb86ec96b01419c43d5ca839e8
This commit is contained in:
parent
6c12d4d5d6
commit
458689d9ce
4 changed files with 148 additions and 13 deletions
Binary file not shown.
Before Width: | Height: | Size: 5 KiB |
BIN
assets/player/player.png
Normal file
BIN
assets/player/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 397 B |
68
assets/player/player.svg
Normal file
68
assets/player/player.svg
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="40"
|
||||||
|
viewBox="0 0 8.4666665 10.583333"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
inkscape:export-filename="player.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"
|
||||||
|
inkscape:version="1.3.1 (91b66b0783, 2023-11-16)"
|
||||||
|
sodipodi:docname="player.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:zoom="8"
|
||||||
|
inkscape:cx="-11.9375"
|
||||||
|
inkscape:cy="24.75"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1080"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#7ee6ae;fill-opacity:1;stroke:none;stroke-width:9.74117;stroke-opacity:0.434347"
|
||||||
|
id="rect1"
|
||||||
|
width="8.4666662"
|
||||||
|
height="8.4666662"
|
||||||
|
x="-2.220446e-16"
|
||||||
|
y="2.1166666" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#7ee6ae;fill-opacity:1;stroke:none;stroke-width:16.9333;stroke-opacity:0.434347"
|
||||||
|
id="path1"
|
||||||
|
inkscape:flatsided="true"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="3.3918092"
|
||||||
|
sodipodi:cy="0.56753963"
|
||||||
|
sodipodi:r1="2.674541"
|
||||||
|
sodipodi:r2="1.3372704"
|
||||||
|
sodipodi:arg1="-0.52359878"
|
||||||
|
sodipodi:arg2="0.52359878"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
inkscape:transform-center-y="0.23402232"
|
||||||
|
transform="matrix(-1.8037062,0,0,-1,10.351161,3.2426846)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
93
src/main.rs
93
src/main.rs
|
@ -49,10 +49,11 @@ fn main() {
|
||||||
.init_resource::<UiState>()
|
.init_resource::<UiState>()
|
||||||
.init_resource::<OpenWindows>()
|
.init_resource::<OpenWindows>()
|
||||||
.add_systems(Startup, (setup, setup_ui))
|
.add_systems(Startup, (setup, setup_ui))
|
||||||
.add_systems(Update, render_ui)
|
.add_systems(Update, (render_ui, movement))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define UI resources
|
||||||
#[derive(Default, Resource)]
|
#[derive(Default, Resource)]
|
||||||
struct UiState {
|
struct UiState {
|
||||||
username: String,
|
username: String,
|
||||||
|
@ -64,6 +65,13 @@ struct OpenWindows {
|
||||||
login_open: bool,
|
login_open: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define the player component
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Player {
|
||||||
|
movement_speed: f32,
|
||||||
|
rotation_speed: f32,
|
||||||
|
}
|
||||||
|
|
||||||
// Bevy engine setup
|
// Bevy engine setup
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
commands.spawn(Camera2dBundle {
|
commands.spawn(Camera2dBundle {
|
||||||
|
@ -76,10 +84,13 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
});
|
});
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
SpriteBundle {
|
SpriteBundle {
|
||||||
texture: asset_server.load("black-square.png"),
|
texture: asset_server.load("player/player.png"),
|
||||||
transform: Transform::from_xyz(100., 0., 0.),
|
|
||||||
..default()
|
..default()
|
||||||
}
|
},
|
||||||
|
Player {
|
||||||
|
movement_speed: 1024.,
|
||||||
|
rotation_speed: f32::to_radians(360.),
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +110,72 @@ fn setup_ui(mut contexts: EguiContexts) {
|
||||||
contexts.ctx_mut().set_fonts(fonts);
|
contexts.ctx_mut().set_fonts(fonts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define the player movement system
|
||||||
|
fn movement(
|
||||||
|
time: Res<Time>,
|
||||||
|
keys: Res<Input<KeyCode>>,
|
||||||
|
mut windows: Query<&mut Window>,
|
||||||
|
mut query: Query<(&Player, &mut Transform)>,
|
||||||
|
) {
|
||||||
|
let (player, mut transform) = query.single_mut();
|
||||||
|
|
||||||
|
let mut rotation_factor = 0.;
|
||||||
|
let mut movement_factor = 0.;
|
||||||
|
let mut blink_factor = 0.;
|
||||||
|
|
||||||
|
if keys.pressed(KeyCode::W) {
|
||||||
|
movement_factor += 1.;
|
||||||
|
}
|
||||||
|
if keys.pressed(KeyCode::S) {
|
||||||
|
movement_factor -= 1.;
|
||||||
|
}
|
||||||
|
if keys.pressed(KeyCode::A) {
|
||||||
|
rotation_factor += 1.;
|
||||||
|
}
|
||||||
|
if keys.pressed(KeyCode::D) {
|
||||||
|
rotation_factor -= 1.;
|
||||||
|
}
|
||||||
|
if keys.pressed(KeyCode::Space) {
|
||||||
|
blink_factor += 4.;
|
||||||
|
}
|
||||||
|
if keys.pressed(KeyCode::Space) && keys.just_released(KeyCode::Right) {
|
||||||
|
blink_factor += 4.;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the player's *forward* vector
|
||||||
|
let movement_direction = transform.rotation * Vec3::Y;
|
||||||
|
|
||||||
|
// Initialise the movement distance variable (to bring it into scope)
|
||||||
|
let movement_distance: f32;
|
||||||
|
|
||||||
|
if blink_factor == 0. {
|
||||||
|
movement_distance = movement_factor * player.movement_speed * time.delta_seconds();
|
||||||
|
// Change the player rotation around the Z-axis only if not blinking
|
||||||
|
transform.rotate_z(rotation_factor * player.rotation_speed * time.delta_seconds());
|
||||||
|
} else {
|
||||||
|
movement_distance = blink_factor * player.movement_speed * 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the translation using the movement direction and distance
|
||||||
|
let translation_delta = movement_direction * movement_distance;
|
||||||
|
// Update the player translation with the created translation
|
||||||
|
transform.translation += translation_delta;
|
||||||
|
|
||||||
|
// Define the bounds of play (the window size)
|
||||||
|
let window = windows.single_mut();
|
||||||
|
let bounds = Vec3::from((
|
||||||
|
Vec2::new(window.resolution.width(), window.resolution.height()) / 2.,
|
||||||
|
0.,
|
||||||
|
));
|
||||||
|
transform.translation = transform.translation.min(bounds).max(-bounds);
|
||||||
|
}
|
||||||
|
|
||||||
// On update: render the UI
|
// On update: render the UI
|
||||||
fn render_ui(
|
fn render_ui(
|
||||||
mut contexts: EguiContexts,
|
mut contexts: EguiContexts,
|
||||||
mut windows: Query<&mut Window>,
|
mut windows: Query<&mut Window>,
|
||||||
mut ui_state: ResMut<UiState>,
|
mut ui_state: ResMut<UiState>,
|
||||||
mut open_windows: ResMut<OpenWindows>,
|
mut open_windows: ResMut<OpenWindows>,
|
||||||
asset_server: Res<AssetServer>,
|
|
||||||
keys: Res<Input<KeyCode>>,
|
keys: Res<Input<KeyCode>>,
|
||||||
) {
|
) {
|
||||||
let window = windows.single_mut();
|
let window = windows.single_mut();
|
||||||
|
@ -114,11 +184,8 @@ fn render_ui(
|
||||||
|
|
||||||
let ctx = contexts.ctx_mut();
|
let ctx = contexts.ctx_mut();
|
||||||
|
|
||||||
if keys.just_pressed(KeyCode::Space) {
|
if keys.just_pressed(KeyCode::Space) {}
|
||||||
}
|
if keys.pressed(KeyCode::W) {}
|
||||||
if keys.pressed(KeyCode::W) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
egui::Window::new("Login")
|
egui::Window::new("Login")
|
||||||
.anchor(egui::Align2::CENTER_CENTER, egui::Vec2::new(0., 0.))
|
.anchor(egui::Align2::CENTER_CENTER, egui::Vec2::new(0., 0.))
|
||||||
|
@ -148,13 +215,13 @@ fn render_ui(
|
||||||
ui.add_space(window_height / 28.);
|
ui.add_space(window_height / 28.);
|
||||||
|
|
||||||
// The text inputs
|
// The text inputs
|
||||||
let username = egui::TextEdit::singleline(&mut ui_state.username)
|
egui::TextEdit::singleline(&mut ui_state.username)
|
||||||
.hint_text("Username")
|
.hint_text("Username")
|
||||||
.margin(egui::vec2(10., 10.))
|
.margin(egui::vec2(10., 10.))
|
||||||
.desired_width(window_width / 4.)
|
.desired_width(window_width / 4.)
|
||||||
.show(ui);
|
.show(ui);
|
||||||
|
|
||||||
let password = egui::TextEdit::singleline(&mut ui_state.password)
|
egui::TextEdit::singleline(&mut ui_state.password)
|
||||||
.password(true)
|
.password(true)
|
||||||
.margin(egui::vec2(10., 10.))
|
.margin(egui::vec2(10., 10.))
|
||||||
.hint_text("Password")
|
.hint_text("Password")
|
||||||
|
@ -164,7 +231,7 @@ fn render_ui(
|
||||||
// Manually add some space between the text inputs and the 'confirm' button
|
// Manually add some space between the text inputs and the 'confirm' button
|
||||||
ui.add_space(window_height / 26.);
|
ui.add_space(window_height / 26.);
|
||||||
|
|
||||||
let button = ui.add(egui::Button::new("Confirm").fill(black));
|
ui.add(egui::Button::new("Confirm").fill(black));
|
||||||
|
|
||||||
// Manually add some space between the button and the bottom border of the
|
// Manually add some space between the button and the bottom border of the
|
||||||
// window...for scaling purposes
|
// window...for scaling purposes
|
||||||
|
|
Reference in a new issue