local multiplayer
This commit is contained in:
parent
8f6001d3ec
commit
c565692e63
|
@ -0,0 +1,108 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use bevy::{
|
||||||
|
input::gamepad::{Gamepad, GamepadAxisType, GamepadButtonType, GamepadEvent, GamepadEventType},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Source {
|
||||||
|
KeyboardRight,
|
||||||
|
KeyboardLeft,
|
||||||
|
Gamepad(Gamepad),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Key {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Jump,
|
||||||
|
}
|
||||||
|
|
||||||
|
const KEYBOARD_KEYS: &[(KeyCode, Source, Key)] = &[
|
||||||
|
(KeyCode::Up, Source::KeyboardRight, Key::Up),
|
||||||
|
(KeyCode::Down, Source::KeyboardRight, Key::Down),
|
||||||
|
(KeyCode::Left, Source::KeyboardRight, Key::Left),
|
||||||
|
(KeyCode::Right, Source::KeyboardRight, Key::Right),
|
||||||
|
(KeyCode::Return, Source::KeyboardRight, Key::Jump),
|
||||||
|
(KeyCode::W, Source::KeyboardLeft, Key::Up),
|
||||||
|
(KeyCode::S, Source::KeyboardLeft, Key::Down),
|
||||||
|
(KeyCode::A, Source::KeyboardLeft, Key::Left),
|
||||||
|
(KeyCode::D, Source::KeyboardLeft, Key::Right),
|
||||||
|
(KeyCode::Space, Source::KeyboardLeft, Key::Jump),
|
||||||
|
];
|
||||||
|
|
||||||
|
const GAMEPAD_KEYS: &[(GamepadButtonType, Key)] = &[
|
||||||
|
(GamepadButtonType::DPadUp, Key::Up),
|
||||||
|
(GamepadButtonType::DPadDown, Key::Down),
|
||||||
|
(GamepadButtonType::DPadLeft, Key::Left),
|
||||||
|
(GamepadButtonType::DPadRight, Key::Right),
|
||||||
|
(GamepadButtonType::South, Key::Jump),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputState(pub HashSet<(Source, Key)>);
|
||||||
|
|
||||||
|
pub fn setup(mut commands: Commands) {
|
||||||
|
commands.insert_resource(InputState::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(
|
||||||
|
keyboard: Res<Input<KeyCode>>,
|
||||||
|
mut gamepad_event: EventReader<GamepadEvent>,
|
||||||
|
mut state: ResMut<InputState>,
|
||||||
|
) {
|
||||||
|
// handle keyboard
|
||||||
|
for (key_code, source, key) in KEYBOARD_KEYS {
|
||||||
|
if keyboard.pressed(*key_code) {
|
||||||
|
state.0.insert((source.clone(), *key));
|
||||||
|
} else {
|
||||||
|
state.0.remove(&(source.clone(), *key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle gamepads
|
||||||
|
for event in gamepad_event.iter() {
|
||||||
|
match event {
|
||||||
|
GamepadEvent(gamepad, GamepadEventType::ButtonChanged(button, value)) => {
|
||||||
|
if let Some((_, key)) = GAMEPAD_KEYS.iter().find(|(button_, _)| {
|
||||||
|
button == button_
|
||||||
|
}) {
|
||||||
|
if *value > 0.01 {
|
||||||
|
state.0.insert((Source::Gamepad(*gamepad), *key));
|
||||||
|
} else {
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), *key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamepadEvent(gamepad, GamepadEventType::AxisChanged(GamepadAxisType::DPadX, value)) |
|
||||||
|
GamepadEvent(gamepad, GamepadEventType::AxisChanged(GamepadAxisType::LeftStickX, value)) => {
|
||||||
|
if *value < -0.01 {
|
||||||
|
state.0.insert((Source::Gamepad(*gamepad), Key::Left));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Right));
|
||||||
|
} else if *value > 0.01 {
|
||||||
|
state.0.insert((Source::Gamepad(*gamepad), Key::Right));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Left));
|
||||||
|
} else {
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Left));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GamepadEvent(gamepad, GamepadEventType::AxisChanged(GamepadAxisType::DPadY, value)) |
|
||||||
|
GamepadEvent(gamepad, GamepadEventType::AxisChanged(GamepadAxisType::LeftStickY, value)) => {
|
||||||
|
if *value < -0.01 {
|
||||||
|
state.0.insert((Source::Gamepad(*gamepad), Key::Down));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Up));
|
||||||
|
} else if *value > 0.01 {
|
||||||
|
state.0.insert((Source::Gamepad(*gamepad), Key::Up));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Down));
|
||||||
|
} else {
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Up));
|
||||||
|
state.0.remove(&(Source::Gamepad(*gamepad), Key::Down));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use bevy::{
|
||||||
};
|
};
|
||||||
use heron::prelude::*;
|
use heron::prelude::*;
|
||||||
|
|
||||||
|
mod input;
|
||||||
mod camera;
|
mod camera;
|
||||||
mod map;
|
mod map;
|
||||||
mod player;
|
mod player;
|
||||||
|
@ -23,13 +24,15 @@ fn main() {
|
||||||
.add_plugin(PhysicsPlugin::default())
|
.add_plugin(PhysicsPlugin::default())
|
||||||
.insert_resource(Gravity::from(Vec3::new(0.0, -9.81, 0.0)))
|
.insert_resource(Gravity::from(Vec3::new(0.0, -9.81, 0.0)))
|
||||||
.insert_resource(ClearColor(Color::rgb(0.7, 0.7, 1.0)))
|
.insert_resource(ClearColor(Color::rgb(0.7, 0.7, 1.0)))
|
||||||
|
.add_startup_system(input::setup)
|
||||||
|
.add_system(input::handle.label("input"))
|
||||||
.add_startup_system(camera::setup)
|
.add_startup_system(camera::setup)
|
||||||
.add_system(camera::track_players)
|
.add_system(camera::track_players)
|
||||||
.add_startup_system(map::setup)
|
.add_startup_system(map::setup)
|
||||||
.add_system(map::build)
|
.add_system(map::build)
|
||||||
.add_system(map::collide)
|
.add_system(map::collide)
|
||||||
.add_startup_system(player::setup)
|
.add_system(player::spawn_player)
|
||||||
.add_system(player::input)
|
.add_system(player::input.after("input"))
|
||||||
.add_system(exit_on_escape)
|
.add_system(exit_on_escape)
|
||||||
// .add_system(log_collisions)
|
// .add_system(log_collisions)
|
||||||
.run();
|
.run();
|
||||||
|
|
154
src/player.rs
154
src/player.rs
|
@ -1,20 +1,37 @@
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use heron::prelude::*;
|
use heron::prelude::*;
|
||||||
|
use rand::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
input::{InputState, Key, Source as InputSource},
|
||||||
map::GroundContact,
|
map::GroundContact,
|
||||||
Layer,
|
Layer,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
|
input_source: InputSource,
|
||||||
rotation: f32,
|
rotation: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(
|
pub fn spawn_player(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
players: Query<&Player>,
|
||||||
|
input: Res<InputState>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
) {
|
) {
|
||||||
|
let input_source = match input.0.iter().find(|(input_source, key)|
|
||||||
|
if *key == Key::Jump {
|
||||||
|
! players.iter().any(|player| player.input_source == *input_source)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Some((input_source, _)) => input_source,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
println!("New input_source: {:?}", input_source);
|
||||||
|
|
||||||
let mesh1 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive0");
|
let mesh1 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive0");
|
||||||
let mesh2 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive1");
|
let mesh2 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive1");
|
||||||
let mesh3 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive2");
|
let mesh3 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Mesh0/Primitive2");
|
||||||
|
@ -23,7 +40,8 @@ pub fn setup(
|
||||||
let material2 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Material1");
|
let material2 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Material1");
|
||||||
let material3 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Material2");
|
let material3 = asset_server.load("Mini-Game Variety Pack/Models/Characters/gltf/character_duck.gltf#Material2");
|
||||||
|
|
||||||
let transform = Transform::from_xyz(0.0, 12.0, 0.0);
|
let mut rng = rand::thread_rng();
|
||||||
|
let transform = Transform::from_xyz(rng.gen_range(-3.0..3.0), 5.0, rng.gen_range(-3.0..3.0));
|
||||||
commands.spawn()
|
commands.spawn()
|
||||||
.insert(RigidBody::Dynamic)
|
.insert(RigidBody::Dynamic)
|
||||||
.insert(CollisionLayers::none()
|
.insert(CollisionLayers::none()
|
||||||
|
@ -36,12 +54,13 @@ pub fn setup(
|
||||||
})
|
})
|
||||||
.insert(PhysicMaterial {
|
.insert(PhysicMaterial {
|
||||||
restitution: 0.0,
|
restitution: 0.0,
|
||||||
density: 8.0,
|
density: 12.0,
|
||||||
friction: 1.0,
|
friction: 1.0,
|
||||||
})
|
})
|
||||||
.insert(RotationConstraints::lock())
|
.insert(RotationConstraints::lock())
|
||||||
.insert(Velocity::default())
|
.insert(Velocity::default())
|
||||||
.insert(Player {
|
.insert(Player {
|
||||||
|
input_source: input_source.clone(),
|
||||||
rotation: 0.0,
|
rotation: 0.0,
|
||||||
})
|
})
|
||||||
.insert(GroundContact::default())
|
.insert(GroundContact::default())
|
||||||
|
@ -73,70 +92,75 @@ pub fn setup(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(time: Res<Time>, input: Res<Input<KeyCode>>, mut players: Query<(&mut Velocity, &mut Player, &mut Transform, &GroundContact)>) {
|
pub fn input(
|
||||||
let x;
|
time: Res<Time>,
|
||||||
let z;
|
input: Res<InputState>,
|
||||||
let target_rotation;
|
mut players: Query<(&mut Velocity, &mut Player, &mut Transform, &GroundContact)>
|
||||||
match [KeyCode::Right, KeyCode::Left, KeyCode::Up, KeyCode::Down].map(|k| input.pressed(k)) {
|
) {
|
||||||
[false, false, false, true] => {
|
|
||||||
z = 1.0;
|
|
||||||
x = 0.0;
|
|
||||||
target_rotation = Some(0.0);
|
|
||||||
}
|
|
||||||
[false, true, false, true] => {
|
|
||||||
z = 0.7;
|
|
||||||
x = -0.7;
|
|
||||||
target_rotation = Some(1.75 * PI);
|
|
||||||
}
|
|
||||||
[false, true, false, false] => {
|
|
||||||
z = 0.0;
|
|
||||||
x = -1.0;
|
|
||||||
target_rotation = Some(1.5 * PI);
|
|
||||||
}
|
|
||||||
[false, true, true, false] => {
|
|
||||||
z = -0.7;
|
|
||||||
x = -0.7;
|
|
||||||
target_rotation = Some(1.25 * PI);
|
|
||||||
}
|
|
||||||
[false, false, true, false] => {
|
|
||||||
z = -1.0;
|
|
||||||
x = 0.0;
|
|
||||||
target_rotation = Some(1.0 * PI);
|
|
||||||
}
|
|
||||||
[true, false, true, false] => {
|
|
||||||
z = -0.7;
|
|
||||||
x = 0.7;
|
|
||||||
target_rotation = Some(0.75 * PI);
|
|
||||||
}
|
|
||||||
[true, false, false, false] => {
|
|
||||||
z = 0.0;
|
|
||||||
x = 1.0;
|
|
||||||
target_rotation = Some(0.5 * PI);
|
|
||||||
}
|
|
||||||
[true, false, false, true] => {
|
|
||||||
z = 0.7;
|
|
||||||
x = 0.7;
|
|
||||||
target_rotation = Some(0.25 * PI);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
z = 0.0;
|
|
||||||
x = 0.0;
|
|
||||||
target_rotation = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let y = if input.pressed(KeyCode::Space) {
|
|
||||||
1.6
|
|
||||||
} else if x != 0.0 || z != 0.0 {
|
|
||||||
// walk bobbing
|
|
||||||
0.3
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
const SPEED: f32 = 3.0;
|
const SPEED: f32 = 3.0;
|
||||||
let target_velocity = SPEED * Vec3::new(x, y, z);
|
|
||||||
for (mut velocity, mut player, mut transform, contact) in players.iter_mut() {
|
for (mut velocity, mut player, mut transform, contact) in players.iter_mut() {
|
||||||
|
let x;
|
||||||
|
let z;
|
||||||
|
let target_rotation;
|
||||||
|
match [Key::Right, Key::Left, Key::Up, Key::Down].map(|key| input.0.contains(&(player.input_source.clone(), key))) {
|
||||||
|
[false, false, false, true] => {
|
||||||
|
z = 1.0;
|
||||||
|
x = 0.0;
|
||||||
|
target_rotation = Some(0.0);
|
||||||
|
}
|
||||||
|
[false, true, false, true] => {
|
||||||
|
z = 0.7;
|
||||||
|
x = -0.7;
|
||||||
|
target_rotation = Some(1.75 * PI);
|
||||||
|
}
|
||||||
|
[false, true, false, false] => {
|
||||||
|
z = 0.0;
|
||||||
|
x = -1.0;
|
||||||
|
target_rotation = Some(1.5 * PI);
|
||||||
|
}
|
||||||
|
[false, true, true, false] => {
|
||||||
|
z = -0.7;
|
||||||
|
x = -0.7;
|
||||||
|
target_rotation = Some(1.25 * PI);
|
||||||
|
}
|
||||||
|
[false, false, true, false] => {
|
||||||
|
z = -1.0;
|
||||||
|
x = 0.0;
|
||||||
|
target_rotation = Some(1.0 * PI);
|
||||||
|
}
|
||||||
|
[true, false, true, false] => {
|
||||||
|
z = -0.7;
|
||||||
|
x = 0.7;
|
||||||
|
target_rotation = Some(0.75 * PI);
|
||||||
|
}
|
||||||
|
[true, false, false, false] => {
|
||||||
|
z = 0.0;
|
||||||
|
x = 1.0;
|
||||||
|
target_rotation = Some(0.5 * PI);
|
||||||
|
}
|
||||||
|
[true, false, false, true] => {
|
||||||
|
z = 0.7;
|
||||||
|
x = 0.7;
|
||||||
|
target_rotation = Some(0.25 * PI);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
z = 0.0;
|
||||||
|
x = 0.0;
|
||||||
|
target_rotation = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let y = if input.0.contains(&(player.input_source.clone(), Key::Jump)) {
|
||||||
|
1.4
|
||||||
|
} else if x != 0.0 || z != 0.0 {
|
||||||
|
// walk bobbing
|
||||||
|
0.4
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let target_velocity = SPEED * Vec3::new(x, y, z);
|
||||||
|
|
||||||
if contact.0 > 0 && velocity.linear.y.abs() < 0.2 {
|
if contact.0 > 0 && velocity.linear.y.abs() < 0.2 {
|
||||||
velocity.linear = target_velocity;
|
velocity.linear = target_velocity;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue