diff --git a/godot/assets/particle.png b/godot/assets/particle.png new file mode 100644 index 0000000..e217c17 Binary files /dev/null and b/godot/assets/particle.png differ diff --git a/godot/assets/particle.png.import b/godot/assets/particle.png.import new file mode 100644 index 0000000..62cb155 --- /dev/null +++ b/godot/assets/particle.png.import @@ -0,0 +1,41 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dawgsqbcveewg" +path.s3tc="res://.godot/imported/particle.png-5e5e4f76b889a93c8b085c65be799145.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://assets/particle.png" +dest_files=["res://.godot/imported/particle.png-5e5e4f76b889a93c8b085c65be799145.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/godot/scenes/map0.tscn b/godot/scenes/map0.tscn index cca864a..b94cf10 100644 --- a/godot/scenes/map0.tscn +++ b/godot/scenes/map0.tscn @@ -14,7 +14,7 @@ size = Vector2(100, 100) [sub_resource type="BoxShape3D" id="BoxShape3D_1cgct"] size = Vector3(100, 0.01, 100) -[node name="map_0" type="Map" unique_id=914577955 node_paths=PackedStringArray("spawner")] +[node name="map" type="Map" unique_id=914577955 node_paths=PackedStringArray("spawner")] spawner = NodePath("spawner") local_player = ExtResource("1_yjvui") remote_player = ExtResource("2_kcncr") @@ -33,8 +33,8 @@ material_override = SubResource("StandardMaterial3D_u8vuu") mesh = SubResource("PlaneMesh_1cgct") [node name="static_body_3d" type="StaticBody3D" parent="terrain" unique_id=203785193] -collision_layer = 3 -collision_mask = 3 +collision_layer = 7 +collision_mask = 7 [node name="collision_shape_3d" type="CollisionShape3D" parent="terrain/static_body_3d" unique_id=1466526153] shape = SubResource("BoxShape3D_1cgct") diff --git a/godot/scenes/player/local_player.tscn b/godot/scenes/player/local_player.tscn index 6a57b7c..7786b63 100644 --- a/godot/scenes/player/local_player.tscn +++ b/godot/scenes/player/local_player.tscn @@ -4,21 +4,19 @@ [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_oh8vq"] -[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh", "sound_emitter")] +[node name="player" type="LocalPlayer" unique_id=863055906 node_paths=PackedStringArray("camera", "soldier_mesh")] look_sensitivity = 1.5 camera = NodePath("camera_3d") move_speed = 5.0 soldier_mesh = NodePath("soldier_m") -sound_emitter = NodePath("") -collision_mask = 2 +collision_mask = 4 [node name="camera_3d" type="Camera3D" parent="." unique_id=1773137418] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8724979, -0.2644562) current = true [node name="soldier_m" parent="." unique_id=281995630 instance=ExtResource("1_jd651")] -transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5, 0, 0, -1.1592964) -visible = false +transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5, 0.15878421, 0, 0.19227558) [node name="collision_shape_3d2" type="CollisionShape3D" parent="." unique_id=442150782] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0310049, 0) diff --git a/godot/scenes/player/raygun.tscn b/godot/scenes/player/raygun.tscn index 6791072..4d8788a 100644 --- a/godot/scenes/player/raygun.tscn +++ b/godot/scenes/player/raygun.tscn @@ -1,13 +1,19 @@ [gd_scene format=3 uid="uid://cw7duhhjebypt"] [ext_resource type="PackedScene" uid="uid://cg81784ct8gf5" path="res://mesh/raygun.tscn" id="1_ckadf"] +[ext_resource type="PackedScene" uid="uid://ctslratcr8h6v" path="res://scenes/player/raygun_bullet.tscn" id="1_m84jv"] [ext_resource type="AudioStream" uid="uid://xxpkjhaovyd" path="res://assets/LaserWithRecoveryEdited.ogg" id="2_lxble"] -[node name="raygun" type="Weapon" unique_id=751705927 node_paths=PackedStringArray("sound_emitter")] +[node name="raygun" type="Weapon" unique_id=751705927 node_paths=PackedStringArray("sound_emitter", "bullet_source")] sound_emitter = NodePath("sound_emitter") +bullet_prefab = ExtResource("1_m84jv") +bullet_source = NodePath("bullet_source") [node name="Raygun" parent="." unique_id=1928455392 instance=ExtResource("1_ckadf")] [node name="sound_emitter" type="AudioStreamPlayer3D" parent="." unique_id=1572422640] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8420726, 0) stream = ExtResource("2_lxble") + +[node name="bullet_source" type="Node3D" parent="." unique_id=211401237] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.36108458, -1.490972) diff --git a/godot/scenes/player/raygun_bullet.tscn b/godot/scenes/player/raygun_bullet.tscn new file mode 100644 index 0000000..072ea11 --- /dev/null +++ b/godot/scenes/player/raygun_bullet.tscn @@ -0,0 +1,40 @@ +[gd_scene format=3 uid="uid://ctslratcr8h6v"] + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_3g557"] +particle_flag_align_y = true +radial_velocity_min = -500.0 +radial_velocity_max = -500.0 +scale_over_velocity_max = 1.0 + +[sub_resource type="Curve" id="Curve_dmbod"] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yxhor"] +emission_enabled = true +emission = Color(1, 0, 1, 1) +emission_energy_multiplier = 4.47 +use_particle_trails = true + +[sub_resource type="TubeTrailMesh" id="TubeTrailMesh_m77kh"] +material = SubResource("StandardMaterial3D_yxhor") +sections = 2 +section_length = 0.5 +section_rings = 5 +cap_top = false +cap_bottom = false +curve = SubResource("Curve_dmbod") + +[node name="raygun_bullet" type="Bullet" unique_id=1238833478 node_paths=PackedStringArray("emitter")] +emitter = NodePath("gpu_particles_3d") + +[node name="gpu_particles_3d" type="GPUParticles3D" parent="." unique_id=1330763726] +emitting = false +amount = 1 +lifetime = 3.8899999999999997 +one_shot = true +fixed_fps = 100 +trail_enabled = true +trail_lifetime = 0.5 +process_material = SubResource("ParticleProcessMaterial_3g557") +draw_pass_1 = SubResource("TubeTrailMesh_m77kh") diff --git a/godot/scenes/player/remote_player.tscn b/godot/scenes/player/remote_player.tscn index 7af7857..6350b11 100644 --- a/godot/scenes/player/remote_player.tscn +++ b/godot/scenes/player/remote_player.tscn @@ -4,11 +4,11 @@ [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1cgct"] -[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh", "sound_emitter")] +[node name="player" type="RemotePlayer" unique_id=845968872 node_paths=PackedStringArray("soldier_mesh")] move_speed = 5.0 soldier_mesh = NodePath("soldier_m") -sound_emitter = NodePath("") -collision_mask = 2 +collision_layer = 2 +collision_mask = 4 [node name="soldier_m" parent="." unique_id=281995630 instance=ExtResource("1_e2ekk")] transform = Transform3D(-0.5, 0, -4.371139e-08, 0, 0.5, 0, 4.371139e-08, 0, -0.5, 0, 0, 0) diff --git a/rust/src/game_manager.rs b/rust/src/game_manager.rs index d0cf691..68b5c20 100644 --- a/rust/src/game_manager.rs +++ b/rust/src/game_manager.rs @@ -335,12 +335,13 @@ impl Game { pub fn handle_shoot( &mut self, player_id: u16, + to: Vector3, transform: NetPlayerTransform, look_up: f32, deal_damage: bool, ) -> bool { if let Some(player) = self.find_player_mut(player_id) { - player.try_shoot(transform, look_up, deal_damage) + player.try_shoot(to, transform, look_up, deal_damage) } else { false } @@ -432,6 +433,7 @@ impl Player { pub fn try_shoot( &mut self, + to: Vector3, transform: NetPlayerTransform, look_up: f32, deal_damage: bool, @@ -441,7 +443,7 @@ impl Player { .dyn_bind_mut() .apply_rotation(transform.rotation.into()); character.dyn_bind_mut().apply_look_up(look_up); - character.dyn_bind_mut().try_shoot(true) + character.dyn_bind_mut().try_shoot(to, true) } else { false } diff --git a/rust/src/net/network_manager.rs b/rust/src/net/network_manager.rs index 6ac4b9c..1fbaded 100644 --- a/rust/src/net/network_manager.rs +++ b/rust/src/net/network_manager.rs @@ -227,10 +227,15 @@ impl INode for NetworkManager { game.bind_mut().handle_jump(player_id); } } - Package::Shoot(player_id, transform, look_up) => { + Package::Shoot(player_id, to, transform, look_up) => { if let Some(game) = &mut Game::singleton() { - game.bind_mut() - .handle_shoot(player_id, transform, look_up, false); + game.bind_mut().handle_shoot( + player_id, + to.into(), + transform, + look_up, + false, + ); } } _ => {} @@ -384,7 +389,7 @@ impl INode for NetworkManager { } } } - Package::Shoot(_, transform, look_up) => { + Package::Shoot(_, to, transform, look_up) => { if let Some(game) = &mut Game::singleton() { let player = game .bind() @@ -392,10 +397,14 @@ impl INode for NetworkManager { .cloned(); if let Some(player) = player { if game.bind_mut().handle_shoot( - player.id, transform, look_up, true, + player.id, + to.into(), + transform, + look_up, + true, ) { peer.broadcast_reliable(Package::Shoot( - player.id, transform, look_up, + player.id, to, transform, look_up, )); } } @@ -531,7 +540,7 @@ pub enum Package { Sync(HashMap), SelfSync(SyncPackage), Jump(u16), - Shoot(u16, NetPlayerTransform, f32), + Shoot(u16, NetVector3, NetPlayerTransform, f32), } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rust/src/player.rs b/rust/src/player.rs index a39afdb..2dc01dd 100644 --- a/rust/src/player.rs +++ b/rust/src/player.rs @@ -1,9 +1,9 @@ -use std::f32::consts::PI; +use std::{f32::consts::PI, ops::Neg}; use godot::{ classes::{ AudioStreamPlayer3D, Camera3D, CharacterBody3D, ICharacterBody3D, Input, InputEvent, - InputEventMouseMotion, input::MouseMode, + InputEventMouseMotion, PhysicsRayQueryParameters3D, input::MouseMode, }, obj::WithBaseField, prelude::*, @@ -43,7 +43,7 @@ pub trait IPlayer { fn get_look_up(&self) -> f32; fn get_transform(&self) -> NetPlayerTransform; fn jump(&mut self); - fn try_shoot(&mut self, deal_damage: bool) -> bool; + fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool; } pub trait PlayerMovement { @@ -154,10 +154,10 @@ impl IPlayer for LocalPlayer { fn apply_look_up(&mut self, _look: f32) {} - fn try_shoot(&mut self, deal_damage: bool) -> bool { + fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool { if self.shoot_cd <= 0. { if let Some(weapon) = &mut self.weapon { - weapon.bind_mut().shoot(Vector3::ZERO, deal_damage); + weapon.bind_mut().shoot(to, deal_damage); } self.shoot_cd = 0.2; true @@ -219,18 +219,56 @@ impl ICharacterBody3D for LocalPlayer { fn input(&mut self, event: Gd) { if self.locked { if event.is_action_pressed("shoot") { - if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { - match peer { - PeerKind::Client(peer, addr) => { - peer.send_reliable(addr, Shoot(0, self.get_transform(), self.look_up)); + let pos = if let Some(camera) = &self.camera { + let forward = camera.get_global_basis().col_c().neg(); + let ray = PhysicsRayQueryParameters3D::create( + camera.get_global_position(), + camera.get_global_position() + forward * 10000., + ); + if let Some(mut ray) = ray { + ray.set_collision_mask(0b110); + ray.set_hit_back_faces(false); + ray.set_hit_from_inside(false); + ray.set_collide_with_bodies(true); + if let Some(world) = self.base().get_world_3d() + && let Some(mut space) = world.get_direct_space_state() + { + let results = space.intersect_ray(&ray); + if let Some(position) = results.get("position") { + Some(Vector3::from_variant(&position)) + } else { + Some(camera.get_global_position() + forward * 100.) + } + } else { + None } - PeerKind::Server(peer, _) => { - if self.try_shoot(true) { - peer.broadcast_reliable(Shoot( - 0, - self.get_transform(), - self.look_up, - )); + } else { + None + } + } else { + None + }; + + godot_print!("{:?}", pos); + + if let Some(pos) = pos { + if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { + match peer { + PeerKind::Client(peer, addr) => { + peer.send_reliable( + addr, + Shoot(0, pos.into(), self.get_transform(), self.look_up), + ); + } + PeerKind::Server(peer, _) => { + if self.try_shoot(pos, true) { + peer.broadcast_reliable(Shoot( + 0, + pos.into(), + self.get_transform(), + self.look_up, + )); + } } } } @@ -343,10 +381,10 @@ impl IPlayer for RemotePlayer { self.look_up = look; } - fn try_shoot(&mut self, deal_damage: bool) -> bool { + fn try_shoot(&mut self, to: Vector3, deal_damage: bool) -> bool { if self.shoot_cd <= 0. { if let Some(weapon) = &mut self.weapon { - weapon.bind_mut().shoot(Vector3::ZERO, deal_damage); + weapon.bind_mut().shoot(to, deal_damage); } self.shoot_cd = 0.2; true diff --git a/rust/src/weapon.rs b/rust/src/weapon.rs index 8359126..14676b9 100644 --- a/rust/src/weapon.rs +++ b/rust/src/weapon.rs @@ -1,10 +1,17 @@ -use godot::{classes::AudioStreamPlayer3D, prelude::*}; +use godot::{ + classes::{AudioStreamPlayer3D, GpuParticles3D, PhysicsRayQueryParameters3D}, + prelude::*, +}; #[derive(GodotClass)] #[class(base=Node3D, init)] pub struct Weapon { #[export] sound_emitter: Option>, + #[export] + bullet_prefab: Option>, + #[export] + bullet_source: Option>, base: Base, } @@ -14,5 +21,50 @@ impl Weapon { if let Some(emitter) = &mut self.sound_emitter { emitter.play(); } + + if let Some(prefab) = &self.bullet_prefab { + let mut bullet = prefab.instantiate_as::(); + if let Some(mut parent) = self.base().find_parent("map") { + parent.add_child(&bullet); + if let Some(source) = &self.bullet_source { + bullet.set_global_position(source.get_global_position()); + } + } + bullet.bind_mut().target = Some(to); + } + } +} + +#[derive(GodotClass)] +#[class(base=Node3D, init)] +pub struct Bullet { + #[export] + pub emitter: Option>, + + pub target: Option, + + alive_for: f64, + + base: Base, +} + +#[godot_api] +impl INode3D for Bullet { + fn ready(&mut self) { + if let Some(emitter) = &mut self.emitter { + emitter.set_emitting(true); + } + } + + fn process(&mut self, delta: f64) { + self.alive_for += delta; + if self.alive_for > 0.05 { + let prev_pos = self.base().get_global_position(); + + if let Some(target) = self.target { + let new_pos = prev_pos.lerp(target, 0.5); + self.base_mut().set_position(new_pos); + } + } } }