diff --git a/client/danmaku!/player.gd b/client/danmaku!/player.gd index 16dfbee..a357ab9 100644 --- a/client/danmaku!/player.gd +++ b/client/danmaku!/player.gd @@ -4,14 +4,61 @@ var nakama_client: NakamaClient var nakama_session: NakamaSession var nakama_socket: NakamaSocket +const SERVER_WIDTH = 90.0 +const SERVER_HEIGHT = 160.0 + +var predicted_tick = 0 +var delta_counter = 0 +var lerp_factor := 0.0 +var bullets = [] + func _ready() -> void: print("Attempting auth.") await attempt_auth() print("Attempting to create debug match.") await create_and_join_debug_match() + nakama_socket.received_match_state.connect(self._on_match_state) func _process(delta: float) -> void: - pass + delta_counter += delta + + if delta_counter >= 0.05: + predicted_tick += 1 + delta_counter = 0 # Reset counter + lerp_factor = delta_counter / 0.05 # Normalize factor between 0 and 1 + + for bullet in bullets: + var prev_pos = bullet.get_current_pos(predicted_tick) + var next_pos = bullet.get_current_pos(predicted_tick + 1) + var interpolated_pos = prev_pos.lerp(next_pos, lerp_factor) + + bullet.position = world_to_screen(interpolated_pos) + + #var screen_size = get_viewport().size + #bullets = bullets.filter(func(bullet): + # return bullet.position.x >= 0 and bullet.position.x <= screen_size.x and \ + # bullet.position.y >= 0 and bullet.position.y <= screen_size.y + #) + + +func _on_match_state(p_state : NakamaRTAPI.MatchData): + match p_state.op_code: + 2: # Spawn bullet + var data = JSON.parse_string(p_state.data) + var bullet = DanmakuBullet.new() + bullet.setup_bullet( + int(data["class"]), + int(data["tick"]), + data["x"], + data["y"], + data["vel_x"], + data["vel_y"]) + bullet.position = world_to_screen(bullet.get_current_pos(int(data["tick"]))) + bullet.texture = load("res://test-bullet.png") + add_child(bullet) + bullets.append(bullet) + delta_counter = 0 + predicted_tick = int(data["tick"]) func attempt_auth() -> void: nakama_client = Nakama.create_client("defaultkey", "127.0.0.1", 7350, "http") @@ -36,3 +83,13 @@ func create_and_join_debug_match() -> void: if debug_br_match.is_exception(): print("An error occurred when joining debug BR match: %s" % response) return + +func world_to_screen(server_pos: Vector2) -> Vector2: + var screen_size = get_viewport().size + var scale_x = screen_size.x / SERVER_WIDTH + var scale_y = screen_size.y / SERVER_HEIGHT + + var client_x = server_pos.x * scale_x + var client_y = server_pos.y * scale_y + + return Vector2(client_x, client_y) diff --git a/client/danmaku-shared.gdextension b/client/danmaku-shared.gdextension index 55a725c..bd94421 100644 --- a/client/danmaku-shared.gdextension +++ b/client/danmaku-shared.gdextension @@ -4,11 +4,11 @@ compatibility_minimum = 4.1 reloadable = true [libraries] -linux.debug.x86_64 = "res://../godot_wrapper/target/debug/libgodot_wrapper.so" -linux.release.x86_64 = "res://../godot_wrapper/target/release/libgodot_wrapper.so" -windows.debug.x86_64 = "res://../godot_wrapper/target/debug/godot_wrapper.dll" -windows.release.x86_64 = "res://../godot_wrapper/target/release/godot_wrapper.dll" -macos.debug = "res://../godot_wrapper/target/debug/libgodot_wrapper.dylib" -macos.release = "res://../godot_wrapper/target/release/libgodot_wrapper.dylib" -macos.debug.arm64 = "res://../godot_wrapper/target/debug/libgodot_wrapper.dylib" -macos.release.arm64 = "res://../godot_wrapper/target/release/libgodot_wrapper.dylib" \ No newline at end of file +linux.debug.x86_64 = "res://../godot-extension/target/release/libgodot_extension.so" +linux.release.x86_64 = "res://../godot-extension/target/release/libgodot_extension.so" +windows.debug.x86_64 = "res://../godot-extension/target/release/godot_extension.dll" +windows.release.x86_64 = "res://../godot-extension/target/release/godot_extension.dll" +macos.debug = "res://../godot-extension/target/release/libgodot_extension.dylib" +macos.release = "res://../godot-extension/target/release/libgodot_extension.dylib" +macos.debug.arm64 = "res://../godot-extension/target/release/libgodot_extension.dylib" +macos.release.arm64 = "res://../godot-extension/target/release/libgodot_extension.dylib" \ No newline at end of file diff --git a/client/project.godot b/client/project.godot index fcad4b3..5d3f579 100644 --- a/client/project.godot +++ b/client/project.godot @@ -18,3 +18,9 @@ config/icon="res://icon.svg" [autoload] Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd" + +[importer_defaults] + +animation_library={ +"animation/fps": 120.0 +} diff --git a/client/test-bullet.png b/client/test-bullet.png new file mode 100644 index 0000000..6e2028c Binary files /dev/null and b/client/test-bullet.png differ diff --git a/godot-extension/src/bullet.rs b/godot-extension/src/bullet.rs new file mode 100644 index 0000000..659135c --- /dev/null +++ b/godot-extension/src/bullet.rs @@ -0,0 +1,41 @@ +use godot::prelude::*; +use shared::bullet::Bullet; +use godot::classes::{ Sprite2D, ISprite2D }; + +#[derive(GodotClass)] +#[class(base=Sprite2D)] +struct DanmakuBullet { + bullet_state: Bullet, + + base: Base, +} + +#[godot_api] +impl ISprite2D for DanmakuBullet { + fn init(base: Base) -> Self { + Self { + bullet_state: Bullet::new(0, 0, 0.0, 0.0, [0.0, 0.0]), + base, + } + } +} + +#[godot_api] +impl DanmakuBullet { + #[func] + fn setup_bullet(&mut self, class: u8, spawn_time: i64, spawn_x: f64, spawn_y: f64, param_x: f64, param_y: f64) { + self.bullet_state = Bullet::new(class, spawn_time, spawn_x, spawn_y, [param_x, param_y]); + } + + #[func] + fn get_current_pos(&self, tick: i64) -> Vector2 { + let (x, y) = self.bullet_state.get_current_pos(tick); + Vector2::new(x as f32, y as f32) + } + + #[func] + fn beyond_kill_boundary(&self, tick: i64) -> bool { + self.bullet_state.beyond_kill_boundary(tick) + } + +} \ No newline at end of file diff --git a/godot-extension/src/godot.rs b/godot-extension/src/godot.rs deleted file mode 100644 index 2f06edb..0000000 --- a/godot-extension/src/godot.rs +++ /dev/null @@ -1,37 +0,0 @@ -use godot::prelude::*; - -use shared::bullet::Bullet; - -#[derive(GodotClass)] -#[class(base=RefCounted)] -struct DanmakuBullet { - bullet_state: Bullet, - - base: Base, -} - -#[godot_api] -impl BulletWrapper { - #[func] - fn new(class: u8, spawn_time: i64, spawn_x: f64, spawn_y: f64, parameters: [f64; 2]) -> Self { - Self { - bullet_state: Bullet::new(class, spawn_time, spawn_x, spawn_y, parameters), - base: Default::default(), - } - } - - #[func] - fn get_current_pos(&self, tick: i64) -> (f64, f64) { - self.bullet_state.get_current_pos(tick) - } - - #[func] - fn beyond_kill_boundary(&self, tick: i64) -> bool { - self.bullet_state.beyond_kill_boundary(tick) - } -} - -struct BulletExtension; - -#[gdextension] -unsafe impl ExtensionLibrary for BulletExtension {} \ No newline at end of file diff --git a/godot-extension/src/lib.rs b/godot-extension/src/lib.rs index db44891..129f005 100644 --- a/godot-extension/src/lib.rs +++ b/godot-extension/src/lib.rs @@ -1 +1,7 @@ -mod godot; \ No newline at end of file +mod bullet; + +use godot::prelude::*; +struct Danmaku; + +#[gdextension] +unsafe impl ExtensionLibrary for Danmaku {} \ No newline at end of file diff --git a/server/main.go b/server/main.go index 52bec89..c52d5b3 100644 --- a/server/main.go +++ b/server/main.go @@ -154,17 +154,19 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger } // Test bullet spawning - if tick%20 == 0 { + if tick%1 == 0 { for _, v := range lobbyState.presences { vel := rand.Float64()*(STAGE_WIDTH/float64(lobbyState.tickRate)) + 1.0 + vel_x_sign := 2*rand.Intn(2) - 1 + vel_y_sign := 2*rand.Intn(2) - 1 bullet := C.new_bullet( C.uint8_t(BULLET_LINEAR), C.int64_t(tick), C.double(STAGE_WIDTH*rand.Float64()), C.double(STAGE_HEIGHT*rand.Float64()), - C.double(vel), - C.double(vel), + C.double(float64(vel_x_sign)*vel), + C.double(float64(vel_y_sign)*vel), ) var x, y C.double @@ -175,8 +177,8 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger "tick": tick, "x": float64(x), "y": float64(y), - "vel_x": vel, - "vel_y": vel, + "vel_x": float64(vel_x_sign) * vel, + "vel_y": float64(vel_y_sign) * vel, } data, err := json.Marshal(bulletData)