Lightyear setup (#55)
This commit is contained in:
2280
Cargo.lock
generated
2280
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/*"]
|
members = ["crates/*"]
|
||||||
@@ -12,6 +11,7 @@ avian3d = { version = "0.3", default-features = false, features = [
|
|||||||
"bevy_scene",
|
"bevy_scene",
|
||||||
"bevy_picking", # todo: Consider if this one is necessary
|
"bevy_picking", # todo: Consider if this one is necessary
|
||||||
"parallel",
|
"parallel",
|
||||||
|
"serialize",
|
||||||
] }
|
] }
|
||||||
bevy = { version = "0.16.0", features = ["track_location"] }
|
bevy = { version = "0.16.0", features = ["track_location"] }
|
||||||
bevy-inspector-egui = { version = "0.31" }
|
bevy-inspector-egui = { version = "0.31" }
|
||||||
@@ -23,13 +23,23 @@ bevy_common_assets = { version = "0.13.0", features = ["ron"] }
|
|||||||
bevy_debug_log = "0.6.0"
|
bevy_debug_log = "0.6.0"
|
||||||
bevy_sprite3d = "5.0.0"
|
bevy_sprite3d = "5.0.0"
|
||||||
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
|
bevy_trenchbroom = { version = "0.8.1", features = ["avian"] }
|
||||||
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "ecfecc6243862bc2bc64dcadfd0efd21c766ab5b" }
|
happy_feet = { git = "https://github.com/rustunit/happy_feet.git", rev = "e4e57c50ba5b5e0be5166e9e4eb629dc1d616a0d" }
|
||||||
|
lightyear = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f", default-features = false, features = [
|
||||||
|
"interpolation",
|
||||||
|
"netcode",
|
||||||
|
"replication",
|
||||||
|
"std",
|
||||||
|
"steam",
|
||||||
|
"udp",
|
||||||
|
] }
|
||||||
|
lightyear_avian3d = { git = "https://github.com/cBournhonesque/lightyear.git", rev = "03cbf419a2c0595261b64420bc0332fc3fe1cc3f" }
|
||||||
nil = "0.14.0"
|
nil = "0.14.0"
|
||||||
rand = "=0.8.5"
|
rand = "=0.8.5"
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
shared = { path = "crates/shared" }
|
shared = { path = "crates/shared" }
|
||||||
steamworks = "0.11"
|
steamworks = "0.11"
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["lightyear/client"]
|
||||||
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
|
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -20,6 +21,7 @@ bevy_debug_log = { workspace = true }
|
|||||||
bevy_sprite3d = { workspace = true }
|
bevy_sprite3d = { workspace = true }
|
||||||
bevy_trenchbroom = { workspace = true }
|
bevy_trenchbroom = { workspace = true }
|
||||||
happy_feet = { workspace = true }
|
happy_feet = { workspace = true }
|
||||||
|
lightyear = { workspace = true }
|
||||||
nil = { workspace = true }
|
nil = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
ron = { workspace = true }
|
ron = { workspace = true }
|
||||||
|
|||||||
35
crates/client/src/client.rs
Normal file
35
crates/client/src/client.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use lightyear::{
|
||||||
|
netcode::Key,
|
||||||
|
prelude::{client::NetcodeConfig, *},
|
||||||
|
};
|
||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.add_systems(Startup, temp_connect_on_startup);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn temp_connect_on_startup(mut commands: Commands) -> Result {
|
||||||
|
let client_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25564);
|
||||||
|
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
|
||||||
|
let auth = Authentication::Manual {
|
||||||
|
server_addr,
|
||||||
|
client_id: 0,
|
||||||
|
private_key: Key::default(),
|
||||||
|
protocol_id: 0,
|
||||||
|
};
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
Name::from("Client"),
|
||||||
|
Client::default(),
|
||||||
|
LocalAddr(client_addr),
|
||||||
|
PeerAddr(server_addr),
|
||||||
|
Link::new(None),
|
||||||
|
ReplicationReceiver::default(),
|
||||||
|
client::NetcodeClient::new(auth, NetcodeConfig::default())?,
|
||||||
|
UdpIo::default(),
|
||||||
|
))
|
||||||
|
.trigger(Connect);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
mod client;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod steam;
|
mod steam;
|
||||||
mod ui;
|
mod ui;
|
||||||
@@ -16,8 +17,10 @@ use bevy_trenchbroom::prelude::*;
|
|||||||
use bevy_ui_gradients::UiGradientsPlugin;
|
use bevy_ui_gradients::UiGradientsPlugin;
|
||||||
use camera::MainCamera;
|
use camera::MainCamera;
|
||||||
use heads_database::HeadDatabaseAsset;
|
use heads_database::HeadDatabaseAsset;
|
||||||
|
use lightyear::prelude::client::ClientPlugins;
|
||||||
use loading_assets::AudioAssets;
|
use loading_assets::AudioAssets;
|
||||||
use shared::*;
|
use shared::*;
|
||||||
|
use std::time::Duration;
|
||||||
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -58,6 +61,9 @@ fn main() {
|
|||||||
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
|
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
|
||||||
);
|
);
|
||||||
app.add_plugins(PhysicsPlugins::default());
|
app.add_plugins(PhysicsPlugins::default());
|
||||||
|
app.add_plugins(ClientPlugins {
|
||||||
|
tick_duration: Duration::from_secs_f64(1.0 / 60.0),
|
||||||
|
});
|
||||||
app.add_plugins(Sprite3dPlugin);
|
app.add_plugins(Sprite3dPlugin);
|
||||||
app.add_plugins(TrenchBroomPlugins(
|
app.add_plugins(TrenchBroomPlugins(
|
||||||
TrenchBroomConfig::new("hedz").icon(None),
|
TrenchBroomConfig::new("hedz").icon(None),
|
||||||
@@ -87,9 +93,11 @@ fn main() {
|
|||||||
app.add_plugins(player::plugin);
|
app.add_plugins(player::plugin);
|
||||||
app.add_plugins(gates::plugin);
|
app.add_plugins(gates::plugin);
|
||||||
app.add_plugins(platforms::plugin);
|
app.add_plugins(platforms::plugin);
|
||||||
|
app.add_plugins(protocol::plugin);
|
||||||
app.add_plugins(movables::plugin);
|
app.add_plugins(movables::plugin);
|
||||||
app.add_plugins(billboards::plugin);
|
app.add_plugins(billboards::plugin);
|
||||||
app.add_plugins(aim::plugin);
|
app.add_plugins(aim::plugin);
|
||||||
|
app.add_plugins(client::plugin);
|
||||||
app.add_plugins(npc::plugin);
|
app.add_plugins(npc::plugin);
|
||||||
app.add_plugins(keys::plugin);
|
app.add_plugins(keys::plugin);
|
||||||
app.add_plugins(squish_animation::plugin);
|
app.add_plugins(squish_animation::plugin);
|
||||||
@@ -106,7 +114,7 @@ fn main() {
|
|||||||
app.add_plugins(hitpoints::plugin);
|
app.add_plugins(hitpoints::plugin);
|
||||||
app.add_plugins(cash_heal::plugin);
|
app.add_plugins(cash_heal::plugin);
|
||||||
app.add_plugins(debug::plugin);
|
app.add_plugins(debug::plugin);
|
||||||
app.add_plugins(utils::observers::plugin);
|
app.add_plugins(utils::plugin);
|
||||||
app.add_plugins(water::plugin);
|
app.add_plugins(water::plugin);
|
||||||
app.add_plugins(head_drop::plugin);
|
app.add_plugins(head_drop::plugin);
|
||||||
app.add_plugins(trail::plugin);
|
app.add_plugins(trail::plugin);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
use crate::GameState;
|
||||||
use bevy::{color::palettes::css::BLACK, prelude::*};
|
use bevy::{color::palettes::css::BLACK, prelude::*};
|
||||||
use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets};
|
use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets};
|
||||||
|
|
||||||
use crate::GameState;
|
|
||||||
|
|
||||||
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
|
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
|
||||||
#[states(scoped_entities)]
|
#[states(scoped_entities)]
|
||||||
enum PauseMenuState {
|
enum PauseMenuState {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["lightyear/server"]
|
||||||
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
|
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui", "shared/dbg"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@@ -19,6 +20,8 @@ bevy_debug_log = { workspace = true }
|
|||||||
bevy_sprite3d = { workspace = true }
|
bevy_sprite3d = { workspace = true }
|
||||||
bevy_trenchbroom = { workspace = true }
|
bevy_trenchbroom = { workspace = true }
|
||||||
happy_feet = { workspace = true }
|
happy_feet = { workspace = true }
|
||||||
|
lightyear = { workspace = true }
|
||||||
|
lightyear_avian3d = { workspace = true }
|
||||||
nil = { workspace = true }
|
nil = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
ron = { workspace = true }
|
ron = { workspace = true }
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ use bevy_sprite3d::Sprite3dPlugin;
|
|||||||
use bevy_trenchbroom::prelude::*;
|
use bevy_trenchbroom::prelude::*;
|
||||||
use bevy_ui_gradients::UiGradientsPlugin;
|
use bevy_ui_gradients::UiGradientsPlugin;
|
||||||
use heads_database::HeadDatabaseAsset;
|
use heads_database::HeadDatabaseAsset;
|
||||||
|
use lightyear::prelude::server::ServerPlugins;
|
||||||
use shared::*;
|
use shared::*;
|
||||||
|
use std::time::Duration;
|
||||||
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
||||||
|
|
||||||
|
mod server;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|
||||||
@@ -44,6 +48,9 @@ fn main() {
|
|||||||
bevy_debug_log::LogViewerPlugin::default()
|
bevy_debug_log::LogViewerPlugin::default()
|
||||||
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
|
.auto_open_threshold(bevy::log::tracing::level_filters::LevelFilter::OFF),
|
||||||
);
|
);
|
||||||
|
app.add_plugins(ServerPlugins {
|
||||||
|
tick_duration: Duration::from_secs_f32(1.0 / 60.0),
|
||||||
|
});
|
||||||
app.add_plugins(PhysicsPlugins::default());
|
app.add_plugins(PhysicsPlugins::default());
|
||||||
app.add_plugins(Sprite3dPlugin);
|
app.add_plugins(Sprite3dPlugin);
|
||||||
app.add_plugins(TrenchBroomPlugins(
|
app.add_plugins(TrenchBroomPlugins(
|
||||||
@@ -77,6 +84,8 @@ fn main() {
|
|||||||
app.add_plugins(movables::plugin);
|
app.add_plugins(movables::plugin);
|
||||||
app.add_plugins(billboards::plugin);
|
app.add_plugins(billboards::plugin);
|
||||||
app.add_plugins(aim::plugin);
|
app.add_plugins(aim::plugin);
|
||||||
|
app.add_plugins(protocol::plugin);
|
||||||
|
app.add_plugins(server::plugin);
|
||||||
app.add_plugins(npc::plugin);
|
app.add_plugins(npc::plugin);
|
||||||
app.add_plugins(keys::plugin);
|
app.add_plugins(keys::plugin);
|
||||||
app.add_plugins(squish_animation::plugin);
|
app.add_plugins(squish_animation::plugin);
|
||||||
@@ -92,7 +101,7 @@ fn main() {
|
|||||||
app.add_plugins(heads::plugin);
|
app.add_plugins(heads::plugin);
|
||||||
app.add_plugins(hitpoints::plugin);
|
app.add_plugins(hitpoints::plugin);
|
||||||
app.add_plugins(cash_heal::plugin);
|
app.add_plugins(cash_heal::plugin);
|
||||||
app.add_plugins(utils::observers::plugin);
|
app.add_plugins(utils::plugin);
|
||||||
app.add_plugins(water::plugin);
|
app.add_plugins(water::plugin);
|
||||||
app.add_plugins(head_drop::plugin);
|
app.add_plugins(head_drop::plugin);
|
||||||
app.add_plugins(trail::plugin);
|
app.add_plugins(trail::plugin);
|
||||||
|
|||||||
45
crates/server/src/server.rs
Normal file
45
crates/server/src/server.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use lightyear::prelude::{
|
||||||
|
server::{NetcodeConfig, NetcodeServer, ServerUdpIo},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
use shared::utils::commands::IsServer;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.init_resource::<IsServer>();
|
||||||
|
|
||||||
|
app.add_systems(Startup, start_server);
|
||||||
|
app.add_observer(handle_new_client);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_new_client(
|
||||||
|
trigger: Trigger<OnAdd, Connected>,
|
||||||
|
mut commands: Commands,
|
||||||
|
id: Query<&PeerAddr>,
|
||||||
|
) -> Result {
|
||||||
|
let id = id.get(trigger.target())?;
|
||||||
|
|
||||||
|
info!("Client connected on IP: {}", id.ip());
|
||||||
|
|
||||||
|
commands
|
||||||
|
.entity(trigger.target())
|
||||||
|
.insert(ReplicationSender::default());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_server(mut commands: Commands) -> Result {
|
||||||
|
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 25565);
|
||||||
|
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
Name::from("Server"),
|
||||||
|
LocalAddr(server_addr),
|
||||||
|
ServerUdpIo::default(),
|
||||||
|
NetcodeServer::new(NetcodeConfig::default()),
|
||||||
|
))
|
||||||
|
.trigger(server::Start);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ bevy_debug_log = { workspace = true }
|
|||||||
bevy_sprite3d = { workspace = true }
|
bevy_sprite3d = { workspace = true }
|
||||||
bevy_trenchbroom = { workspace = true }
|
bevy_trenchbroom = { workspace = true }
|
||||||
happy_feet = { workspace = true }
|
happy_feet = { workspace = true }
|
||||||
|
lightyear = { workspace = true }
|
||||||
nil = { workspace = true }
|
nil = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
ron = { workspace = true }
|
ron = { workspace = true }
|
||||||
|
|||||||
@@ -58,11 +58,15 @@ fn on_trigger_arrow(
|
|||||||
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||||
t.translation += t.forward().as_vec3() * 2.;
|
transform.translation += transform.forward().as_vec3() * 2.;
|
||||||
|
|
||||||
let damage = heads_db.head_stats(state.head).damage;
|
let damage = heads_db.head_stats(state.head).damage;
|
||||||
commands.spawn((Name::new("projectile-arrow"), ArrowProjectile { damage }, t));
|
commands.spawn((
|
||||||
|
Name::new("projectile-arrow"),
|
||||||
|
ArrowProjectile { damage },
|
||||||
|
transform,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
|
|||||||
@@ -6,12 +6,17 @@ use crate::{
|
|||||||
hitpoints::Hit,
|
hitpoints::Hit,
|
||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
|
protocol::GltfSceneRoot,
|
||||||
tb_entities::EnemySpawn,
|
tb_entities::EnemySpawn,
|
||||||
utils::{auto_rotate::AutoRotation, global_observer, sprite_3d_animation::AnimationTimer},
|
utils::{
|
||||||
|
auto_rotate::AutoRotation, commands::CommandExt, global_observer,
|
||||||
|
sprite_3d_animation::AnimationTimer,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||||
|
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
const MAX_SHOT_AGES: f32 = 15.;
|
const MAX_SHOT_AGES: f32 = 15.;
|
||||||
@@ -58,8 +63,6 @@ fn on_trigger_missile(
|
|||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
assets: Res<GameAssets>,
|
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
|
||||||
) {
|
) {
|
||||||
let state = trigger.event().0;
|
let state = trigger.event().0;
|
||||||
|
|
||||||
@@ -76,33 +79,32 @@ fn on_trigger_missile(
|
|||||||
|
|
||||||
let head = heads_db.head_stats(state.head);
|
let head = heads_db.head_stats(state.head);
|
||||||
|
|
||||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||||
t.translation += t.forward().as_vec3() * 2.0;
|
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||||
|
|
||||||
let mesh = assets.projectiles[format!("{}.glb", head.projectile).as_str()].clone();
|
commands
|
||||||
let asset = gltf_assets.get(&mesh).unwrap();
|
.spawn((
|
||||||
|
Name::new("projectile-missile"),
|
||||||
commands.spawn((
|
CurverProjectile {
|
||||||
Name::new("projectile-missile"),
|
time: time.elapsed_secs(),
|
||||||
CurverProjectile {
|
damage: head.damage,
|
||||||
time: time.elapsed_secs(),
|
},
|
||||||
damage: head.damage,
|
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||||
},
|
CollisionLayers::new(
|
||||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
LayerMask(GameLayer::Projectile.to_bits()),
|
||||||
CollisionLayers::new(
|
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||||
LayerMask(GameLayer::Projectile.to_bits()),
|
),
|
||||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
Sensor,
|
||||||
),
|
CollisionEventsEnabled,
|
||||||
Sensor,
|
Visibility::default(),
|
||||||
CollisionEventsEnabled,
|
transform,
|
||||||
Visibility::default(),
|
children![(
|
||||||
t,
|
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||||
children![(
|
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
),],
|
||||||
SceneRoot(asset.scenes[0].clone()),
|
))
|
||||||
),],
|
.insert_server(Replicate::to_clients(NetworkTarget::All));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enemy_hit(
|
fn enemy_hit(
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ fn on_trigger_gun(
|
|||||||
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
state.rot.mul_quat(Quat::from_rotation_y(PI))
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||||
t.translation += t.forward().as_vec3() * 2.0;
|
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Name::new("projectile-gun"),
|
Name::new("projectile-gun"),
|
||||||
@@ -114,7 +114,7 @@ fn on_trigger_gun(
|
|||||||
Sensor,
|
Sensor,
|
||||||
CollisionEventsEnabled,
|
CollisionEventsEnabled,
|
||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
t,
|
transform,
|
||||||
Children::spawn(Spawn(Gizmo {
|
Children::spawn(Spawn(Gizmo {
|
||||||
handle: gizmo_assets.add({
|
handle: gizmo_assets.add({
|
||||||
let mut g = GizmoAsset::default();
|
let mut g = GizmoAsset::default();
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ use crate::{
|
|||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
|
protocol::GltfSceneRoot,
|
||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
utils::{
|
utils::{
|
||||||
explosions::Explosion, global_observer, sprite_3d_animation::AnimationTimer, trail::Trail,
|
commands::CommandExt, explosions::Explosion, global_observer,
|
||||||
|
sprite_3d_animation::AnimationTimer, trail::Trail,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||||
|
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
const MAX_SHOT_AGES: f32 = 15.;
|
const MAX_SHOT_AGES: f32 = 15.;
|
||||||
@@ -56,8 +59,6 @@ fn on_trigger_missile(
|
|||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
assets: Res<GameAssets>,
|
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
|
||||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||||
) {
|
) {
|
||||||
let state = trigger.event().0;
|
let state = trigger.event().0;
|
||||||
@@ -75,51 +76,50 @@ fn on_trigger_missile(
|
|||||||
|
|
||||||
let head = heads_db.head_stats(state.head);
|
let head = heads_db.head_stats(state.head);
|
||||||
|
|
||||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||||
t.translation += t.forward().as_vec3() * 2.0;
|
transform.translation += transform.forward().as_vec3() * 2.0;
|
||||||
|
|
||||||
let mesh = assets.projectiles["missile.glb"].clone();
|
commands
|
||||||
let asset = gltf_assets.get(&mesh).unwrap();
|
.spawn((
|
||||||
|
Name::new("projectile-missile"),
|
||||||
commands.spawn((
|
MissileProjectile {
|
||||||
Name::new("projectile-missile"),
|
time: time.elapsed_secs(),
|
||||||
MissileProjectile {
|
damage: head.damage,
|
||||||
time: time.elapsed_secs(),
|
},
|
||||||
damage: head.damage,
|
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||||
},
|
CollisionLayers::new(
|
||||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
LayerMask(GameLayer::Projectile.to_bits()),
|
||||||
CollisionLayers::new(
|
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||||
LayerMask(GameLayer::Projectile.to_bits()),
|
|
||||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
|
||||||
),
|
|
||||||
Sensor,
|
|
||||||
CollisionEventsEnabled,
|
|
||||||
Visibility::default(),
|
|
||||||
t,
|
|
||||||
children![
|
|
||||||
(
|
|
||||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse())
|
|
||||||
.with_scale(Vec3::splat(0.04)),
|
|
||||||
SceneRoot(asset.scenes[0].clone()),
|
|
||||||
),
|
),
|
||||||
(
|
Sensor,
|
||||||
Trail::new(
|
CollisionEventsEnabled,
|
||||||
12,
|
Visibility::default(),
|
||||||
LinearRgba::rgb(1., 0.0, 0.),
|
transform,
|
||||||
LinearRgba::rgb(0.9, 0.9, 0.)
|
children![
|
||||||
)
|
(
|
||||||
.with_pos(t.translation),
|
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse())
|
||||||
Gizmo {
|
.with_scale(Vec3::splat(0.04)),
|
||||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
GltfSceneRoot::Projectile("missile".to_string()),
|
||||||
line_config: GizmoLineConfig {
|
),
|
||||||
width: 10.,
|
(
|
||||||
|
Trail::new(
|
||||||
|
12,
|
||||||
|
LinearRgba::rgb(1., 0.0, 0.),
|
||||||
|
LinearRgba::rgb(0.9, 0.9, 0.)
|
||||||
|
)
|
||||||
|
.with_pos(transform.translation),
|
||||||
|
Gizmo {
|
||||||
|
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||||
|
line_config: GizmoLineConfig {
|
||||||
|
width: 10.,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
..default()
|
)
|
||||||
},
|
],
|
||||||
)
|
))
|
||||||
],
|
.insert_server(Replicate::to_clients(NetworkTarget::All));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ use crate::{
|
|||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
|
protocol::GltfSceneRoot,
|
||||||
sounds::PlaySound,
|
sounds::PlaySound,
|
||||||
utils::{
|
utils::{
|
||||||
auto_rotate::AutoRotation, explosions::Explosion, global_observer,
|
auto_rotate::AutoRotation, commands::CommandExt, explosions::Explosion, global_observer,
|
||||||
sprite_3d_animation::AnimationTimer,
|
sprite_3d_animation::AnimationTimer,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -15,10 +16,12 @@ use avian3d::prelude::*;
|
|||||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||||
use bevy_ballistic::launch_velocity;
|
use bevy_ballistic::launch_velocity;
|
||||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||||
|
use lightyear::prelude::{NetworkTarget, Replicate};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Serialize, Deserialize, PartialEq)]
|
||||||
struct ThrownProjectile {
|
pub struct ThrownProjectile {
|
||||||
impact_animation: bool,
|
impact_animation: bool,
|
||||||
damage: u32,
|
damage: u32,
|
||||||
}
|
}
|
||||||
@@ -50,8 +53,6 @@ fn on_trigger_thrown(
|
|||||||
trigger: Trigger<TriggerThrow>,
|
trigger: Trigger<TriggerThrow>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
assets: Res<GameAssets>,
|
|
||||||
gltf_assets: Res<Assets<Gltf>>,
|
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
) {
|
) {
|
||||||
let state = trigger.event().0;
|
let state = trigger.event().0;
|
||||||
@@ -76,8 +77,6 @@ fn on_trigger_thrown(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let head = heads_db.head_stats(state.head);
|
let head = heads_db.head_stats(state.head);
|
||||||
let mesh = assets.projectiles[format!("{}.glb", head.projectile).as_str()].clone();
|
|
||||||
let asset = gltf_assets.get(&mesh).unwrap();
|
|
||||||
|
|
||||||
//TODO: projectile db?
|
//TODO: projectile db?
|
||||||
let explosion_animation = !matches!(state.head, 8 | 16);
|
let explosion_animation = !matches!(state.head, 8 | 16);
|
||||||
@@ -102,9 +101,10 @@ fn on_trigger_thrown(
|
|||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
Sensor,
|
Sensor,
|
||||||
))
|
))
|
||||||
|
.insert_server(Replicate::to_clients(NetworkTarget::All))
|
||||||
.with_child((
|
.with_child((
|
||||||
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
AutoRotation(Quat::from_rotation_x(0.4) * Quat::from_rotation_z(0.3)),
|
||||||
SceneRoot(asset.scenes[0].clone()),
|
GltfSceneRoot::Projectile(head.projectile.clone()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,11 +114,11 @@ fn update_player_aim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(e) = &aim_target.0 {
|
if let Some(e) = &aim_target.0
|
||||||
if commands.get_entity(*e).is_err() {
|
&& commands.get_entity(*e).is_err()
|
||||||
aim_target.0 = None;
|
{
|
||||||
return;
|
aim_target.0 = None;
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_target != aim_target.0 {
|
if new_target != aim_target.0 {
|
||||||
@@ -166,11 +166,11 @@ fn update_npc_aim(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(e) = &aim_target.0 {
|
if let Some(e) = &aim_target.0
|
||||||
if commands.get_entity(*e).is_err() {
|
&& commands.get_entity(*e).is_err()
|
||||||
aim_target.0 = None;
|
{
|
||||||
return;
|
aim_target.0 = None;
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_target != aim_target.0 {
|
if new_target != aim_target.0 {
|
||||||
|
|||||||
@@ -140,16 +140,16 @@ fn sync(
|
|||||||
target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>,
|
target_data: Query<(&Hitpoints, &ActiveHeads), With<Npc>>,
|
||||||
) {
|
) {
|
||||||
let mut new_state = None;
|
let mut new_state = None;
|
||||||
if let Some(e) = player_target.iter().next().and_then(|target| target.0) {
|
if let Some(e) = player_target.iter().next().and_then(|target| target.0)
|
||||||
if let Ok((hp, heads)) = target_data.get(e) {
|
&& let Ok((hp, heads)) = target_data.get(e)
|
||||||
let head = heads.current().expect("target must have a head on");
|
{
|
||||||
new_state = Some(UiHeadState {
|
let head = heads.current().expect("target must have a head on");
|
||||||
head: head.head,
|
new_state = Some(UiHeadState {
|
||||||
health: hp.health(),
|
head: head.head,
|
||||||
ammo: 1.,
|
health: hp.health(),
|
||||||
reloading: None,
|
ammo: 1.,
|
||||||
});
|
reloading: None,
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_state != target.head {
|
if new_state != target.head {
|
||||||
|
|||||||
@@ -10,17 +10,14 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use avian3d::{math::*, prelude::*};
|
use avian3d::{math::*, prelude::*};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use happy_feet::{
|
use happy_feet::prelude::{
|
||||||
KinematicVelocity,
|
Character, CharacterDrag, CharacterGravity, CharacterMovement, CharacterPlugins,
|
||||||
ground::{Grounding, GroundingConfig},
|
GroundFriction, Grounding, GroundingConfig, KinematicVelocity, MoveInput, SteppingBehaviour,
|
||||||
prelude::{
|
SteppingConfig,
|
||||||
Character, CharacterDrag, CharacterFriction, CharacterGravity, CharacterMovement,
|
|
||||||
CharacterPlugin, MoveInput, SteppingBehaviour, SteppingConfig,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins(CharacterPlugin::default());
|
app.add_plugins(CharacterPlugins::default());
|
||||||
|
|
||||||
app.register_type::<MovementSpeedFactor>();
|
app.register_type::<MovementSpeedFactor>();
|
||||||
|
|
||||||
@@ -180,7 +177,7 @@ impl CharacterControllerBundle {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
character_controller: Character { up: Dir3::Y },
|
character_controller: Character,
|
||||||
collider,
|
collider,
|
||||||
move_input: MoveInput::default(),
|
move_input: MoveInput::default(),
|
||||||
movement_factor: MovementSpeedFactor(1.0),
|
movement_factor: MovementSpeedFactor(1.0),
|
||||||
@@ -197,7 +194,7 @@ struct MovementConfig {
|
|||||||
step: SteppingConfig,
|
step: SteppingConfig,
|
||||||
ground: GroundingConfig,
|
ground: GroundingConfig,
|
||||||
gravity: CharacterGravity,
|
gravity: CharacterGravity,
|
||||||
friction: CharacterFriction,
|
friction: GroundFriction,
|
||||||
drag: CharacterDrag,
|
drag: CharacterDrag,
|
||||||
settings: ControllerSettings,
|
settings: ControllerSettings,
|
||||||
}
|
}
|
||||||
@@ -208,16 +205,19 @@ const RUNNING_MOVEMENT_CONFIG: MovementConfig = MovementConfig {
|
|||||||
acceleration: 40.0,
|
acceleration: 40.0,
|
||||||
},
|
},
|
||||||
step: SteppingConfig {
|
step: SteppingConfig {
|
||||||
max_height: 0.25,
|
max_vertical: 0.25,
|
||||||
|
max_horizontal: 0.4,
|
||||||
behaviour: SteppingBehaviour::Grounded,
|
behaviour: SteppingBehaviour::Grounded,
|
||||||
|
max_substeps: 8,
|
||||||
},
|
},
|
||||||
ground: GroundingConfig {
|
ground: GroundingConfig {
|
||||||
max_angle: PI / 4.0,
|
max_angle: PI / 4.0,
|
||||||
max_distance: 0.2,
|
max_distance: 0.2,
|
||||||
snap_to_surface: true,
|
snap_to_surface: true,
|
||||||
|
up_direction: Dir3::Y,
|
||||||
},
|
},
|
||||||
gravity: CharacterGravity(vec3(0.0, -60.0, 0.0)),
|
gravity: CharacterGravity(Some(vec3(0.0, -60.0, 0.0))),
|
||||||
friction: CharacterFriction(10.0),
|
friction: GroundFriction(10.0),
|
||||||
drag: CharacterDrag(0.0),
|
drag: CharacterDrag(0.0),
|
||||||
settings: ControllerSettings {
|
settings: ControllerSettings {
|
||||||
jump_force: 25.0,
|
jump_force: 25.0,
|
||||||
@@ -231,16 +231,19 @@ const FLYING_MOVEMENT_CONFIG: MovementConfig = MovementConfig {
|
|||||||
acceleration: 300.0,
|
acceleration: 300.0,
|
||||||
},
|
},
|
||||||
step: SteppingConfig {
|
step: SteppingConfig {
|
||||||
max_height: 0.25,
|
max_vertical: 0.25,
|
||||||
|
max_horizontal: 0.4,
|
||||||
behaviour: SteppingBehaviour::Never,
|
behaviour: SteppingBehaviour::Never,
|
||||||
|
max_substeps: 8,
|
||||||
},
|
},
|
||||||
ground: GroundingConfig {
|
ground: GroundingConfig {
|
||||||
max_angle: 0.0,
|
max_angle: 0.0,
|
||||||
max_distance: -1.0,
|
max_distance: -1.0,
|
||||||
snap_to_surface: false,
|
snap_to_surface: false,
|
||||||
|
up_direction: Dir3::Y,
|
||||||
},
|
},
|
||||||
gravity: CharacterGravity(Vec3::ZERO),
|
gravity: CharacterGravity(Some(Vec3::ZERO)),
|
||||||
friction: CharacterFriction(0.0),
|
friction: GroundFriction(0.0),
|
||||||
drag: CharacterDrag(10.0),
|
drag: CharacterDrag(10.0),
|
||||||
settings: ControllerSettings {
|
settings: ControllerSettings {
|
||||||
jump_force: 0.0,
|
jump_force: 0.0,
|
||||||
|
|||||||
@@ -89,14 +89,14 @@ impl ActiveHeads {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(head) = head {
|
if let Some(head) = head
|
||||||
if head.health < head.health_max {
|
&& head.health < head.health_max
|
||||||
head.health = head
|
{
|
||||||
.health
|
head.health = head
|
||||||
.saturating_add(heal_amount)
|
.health
|
||||||
.clamp(0, head.health_max);
|
.saturating_add(heal_amount)
|
||||||
healed = true;
|
.clamp(0, head.health_max);
|
||||||
}
|
healed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ pub mod npc;
|
|||||||
pub mod physics_layers;
|
pub mod physics_layers;
|
||||||
pub mod platforms;
|
pub mod platforms;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
|
pub mod protocol;
|
||||||
pub mod sounds;
|
pub mod sounds;
|
||||||
pub mod steam;
|
pub mod steam;
|
||||||
pub mod tb_entities;
|
pub mod tb_entities;
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ fn move_active(
|
|||||||
let t = (elapsed - active.start_time) / active.duration;
|
let t = (elapsed - active.start_time) / active.duration;
|
||||||
transform.rotation = active.start.rotation.lerp(active.target.rotation, t);
|
transform.rotation = active.start.rotation.lerp(active.target.rotation, t);
|
||||||
} else {
|
} else {
|
||||||
*transform = active.target;
|
transform.translation = active.target.translation;
|
||||||
|
transform.rotation = active.target.rotation;
|
||||||
commands.entity(e).remove::<(ActiveMovable, Movable)>();
|
commands.entity(e).remove::<(ActiveMovable, Movable)>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ fn on_spawn_check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (e, spawn, transform) in query.iter() {
|
for (e, spawn, transform) in query.iter() {
|
||||||
if let Some(order) = spawn.spawn_order {
|
if let Some(order) = spawn.spawn_order
|
||||||
if order > spawning.spawn_index {
|
&& order > spawning.spawn_index
|
||||||
continue;
|
{
|
||||||
}
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = names[&spawn.head];
|
let id = names[&spawn.head];
|
||||||
|
|||||||
41
crates/shared/src/protocol.rs
Normal file
41
crates/shared/src/protocol.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use crate::{global_observer, loading_assets::GameAssets};
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use lightyear::prelude::AppComponentExt;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.register_component::<Transform>();
|
||||||
|
app.register_component::<GltfSceneRoot>();
|
||||||
|
|
||||||
|
global_observer!(app, spawn_gltf_scene_roots);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Reflect, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub enum GltfSceneRoot {
|
||||||
|
Projectile(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_gltf_scene_roots(
|
||||||
|
trigger: Trigger<OnAdd, GltfSceneRoot>,
|
||||||
|
mut commands: Commands,
|
||||||
|
gltf_roots: Query<&GltfSceneRoot>,
|
||||||
|
assets: Res<GameAssets>,
|
||||||
|
gltfs: Res<Assets<Gltf>>,
|
||||||
|
) -> Result {
|
||||||
|
let root = gltf_roots.get(trigger.target())?;
|
||||||
|
|
||||||
|
let (gltf, index) = match root {
|
||||||
|
GltfSceneRoot::Projectile(addr) => (
|
||||||
|
assets.projectiles[format!("{addr}.glb").as_str()].clone(),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let gltf = gltfs.get(&gltf).unwrap();
|
||||||
|
|
||||||
|
let scene = gltf.scenes[index].clone();
|
||||||
|
|
||||||
|
commands.entity(trigger.target()).insert(SceneRoot(scene));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
21
crates/shared/src/utils/commands.rs
Normal file
21
crates/shared/src/utils/commands.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use bevy::ecs::{
|
||||||
|
bundle::Bundle, resource::Resource, system::EntityCommands, world::EntityWorldMut,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Resource)]
|
||||||
|
pub struct IsServer;
|
||||||
|
|
||||||
|
pub trait CommandExt {
|
||||||
|
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'w> CommandExt for EntityCommands<'w> {
|
||||||
|
fn insert_server(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
|
self.queue(|mut entity: EntityWorldMut| {
|
||||||
|
if entity.world().contains_resource::<IsServer>() {
|
||||||
|
entity.insert(bundle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
pub mod auto_rotate;
|
pub mod auto_rotate;
|
||||||
pub mod billboards;
|
pub mod billboards;
|
||||||
|
pub mod commands;
|
||||||
pub mod explosions;
|
pub mod explosions;
|
||||||
pub mod observers;
|
pub mod observers;
|
||||||
pub mod sprite_3d_animation;
|
pub mod sprite_3d_animation;
|
||||||
pub mod squish_animation;
|
pub mod squish_animation;
|
||||||
pub mod trail;
|
pub mod trail;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
pub(crate) use observers::global_observer;
|
pub(crate) use observers::global_observer;
|
||||||
|
|
||||||
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.add_plugins(observers::plugin);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user