diff --git a/Cargo.lock b/Cargo.lock
index c1bea20..0e54e24 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3052,7 +3052,7 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "rlviser"
-version = "0.7.5"
+version = "0.7.6"
dependencies = [
"ahash",
"bevy",
diff --git a/Cargo.toml b/Cargo.toml
index f7e3bb4..1e03e25 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-version = "0.7.5"
+version = "0.7.6"
name = "rlviser"
edition = "2021"
publish = false
diff --git a/README.md b/README.md
index ead9b5b..1e686fc 100644
--- a/README.md
+++ b/README.md
@@ -40,9 +40,13 @@ You can also choose to use the integrated support in [RLGym 2.0](https://github.
| `Space` | Move up |
| `Left Shift` | Move down |
| `R` | State set ball towards goal |
+| `P` | Toggle pause/play |
+| `+` | Increase game speed +0.5x |
+| `-` | Decrease game speed -0.5x |
+| `=` | Set game speed to 1x |
| `Left click`1 | Drag cars and ball |
- 1 - Requires the menu toggled ON to free the cursor, you can drag cars and the ball to move them in the world. Requires the agent on the other side to support state setting.
+1 - Requires the menu toggled ON to free the cursor, you can drag cars and the ball to move them in the world. Requires the agent on the other side to support state setting.
## Modes
diff --git a/src/settings/gui.rs b/src/settings/gui.rs
index 730e21b..8daa485 100644
--- a/src/settings/gui.rs
+++ b/src/settings/gui.rs
@@ -85,26 +85,26 @@ fn advance_time(mut last_packet_send: ResMut, time: Res) {
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Resource)]
-struct Options {
- vsync: bool,
- uncap_fps: bool,
- fps_limit: f64,
- fps: (usize, [f32; 120]),
- ball_cam: bool,
- stop_day: bool,
- daytime: f32,
- day_speed: f32,
- msaa: usize,
- camera_state: PrimaryCamera,
- show_time: bool,
- ui_scale: f32,
- shadows: usize,
- game_speed: f32,
- paused: bool,
- mouse_sensitivity: f32,
- allow_rendering: bool,
- packet_smoothing: usize,
- calc_ball_rot: bool,
+pub struct Options {
+ pub vsync: bool,
+ pub uncap_fps: bool,
+ pub fps_limit: f64,
+ pub fps: (usize, [f32; 120]),
+ pub ball_cam: bool,
+ pub stop_day: bool,
+ pub daytime: f32,
+ pub day_speed: f32,
+ pub msaa: usize,
+ pub camera_state: PrimaryCamera,
+ pub show_time: bool,
+ pub ui_scale: f32,
+ pub shadows: usize,
+ pub game_speed: f32,
+ pub paused: bool,
+ pub mouse_sensitivity: f32,
+ pub allow_rendering: bool,
+ pub packet_smoothing: usize,
+ pub calc_ball_rot: bool,
}
impl Default for Options {
diff --git a/src/udp.rs b/src/udp.rs
index 265455c..39b93a2 100644
--- a/src/udp.rs
+++ b/src/udp.rs
@@ -7,6 +7,7 @@ use crate::{
renderer::{RenderGroups, RenderMessage, UdpRendererPlugin},
rocketsim::{CarInfo, GameMode, GameState, Team},
settings::{
+ gui::Options,
options::{BallCam, CalcBallRot, GameSpeed, PacketSmoothing, ShowTime, UiOverlayScale},
state_setting::UserCarStates,
},
@@ -586,6 +587,7 @@ fn start_udp_recv_handler(socket: UdpSocket, commands: &mut Commands) {
}
fn apply_udp_updates(
+ time: Res,
socket: Res,
udp_updates: Res,
game_speed: Res,
@@ -596,9 +598,12 @@ fn apply_udp_updates(
mut packet_updated: ResMut,
mut render_groups: ResMut,
mut packet_time_elapsed: ResMut,
+ mut last_packet_time_elapsed: ResMut,
mut speed_update: EventWriter,
mut paused_update: EventWriter,
) {
+ packet_time_elapsed.tick(time.delta());
+
let mut new_game_state = None;
for update in udp_updates.try_iter() {
@@ -633,9 +638,11 @@ fn apply_udp_updates(
match new_game_state {
Some(new_state) => {
+ last_packet_time_elapsed.push(packet_time_elapsed.0.elapsed_secs());
+ packet_time_elapsed.reset();
+
game_states.advance(*packet_smoothing, new_state, calc_ball_rot.0);
packet_updated.0 = true;
- packet_time_elapsed.reset();
}
None => {
packet_updated.0 = false;
@@ -1294,10 +1301,11 @@ fn interpolate_calc_next_ball_rot(mut states: ResMut) {
}
fn interpolate_packets(
- mut states: ResMut,
+ time: Res,
game_speed: Res,
+ last_packet_time_elapsed: Res,
+ mut states: ResMut,
mut packet_time_elapsed: ResMut,
- time: Res,
) {
if game_speed.paused {
return;
@@ -1305,10 +1313,8 @@ fn interpolate_packets(
packet_time_elapsed.tick(time.delta());
- let total_time_delta = (states.next.tick_count - states.last.tick_count) as f32 / states.next.tick_rate;
- let delta_time = packet_time_elapsed.elapsed_secs() * game_speed.speed;
-
- let lerp_amount = delta_time / total_time_delta;
+ let delta_time = packet_time_elapsed.elapsed_secs();
+ let lerp_amount = delta_time / last_packet_time_elapsed.avg();
states.current.ball.pos = states.last.ball.pos.lerp(states.next.ball.pos, lerp_amount);
@@ -1330,7 +1336,12 @@ fn interpolate_packets(
}
}
-fn listen(socket: Res, key: Res>, mut game_states: ResMut) {
+fn listen(
+ socket: Res,
+ key: Res>,
+ mut game_states: ResMut,
+ mut options: ResMut,
+) {
let mut changed = false;
if key.just_pressed(KeyCode::KeyR) {
changed = true;
@@ -1344,6 +1355,28 @@ fn listen(socket: Res, key: Res>, mut game_stat
game_states.next.ball.vel = vel;
}
+ if key.just_pressed(KeyCode::KeyP) {
+ options.paused = !options.paused;
+ }
+
+ let shift_pressed = key.pressed(KeyCode::ShiftLeft) || key.pressed(KeyCode::ShiftRight);
+
+ if key.just_pressed(KeyCode::NumpadAdd) || (shift_pressed && key.just_pressed(KeyCode::Equal)) {
+ options.game_speed = if options.game_speed < 0.5 {
+ 0.5
+ } else {
+ (options.game_speed + 0.5).min(10.)
+ };
+ }
+
+ if key.just_pressed(KeyCode::NumpadSubtract) || (!shift_pressed && key.just_pressed(KeyCode::Minus)) {
+ options.game_speed = (options.game_speed - 0.5).max(0.1);
+ }
+
+ if key.just_pressed(KeyCode::NumpadEqual) || (!shift_pressed && key.just_pressed(KeyCode::Equal)) {
+ options.game_speed = 1.;
+ }
+
if changed {
socket.send(SendableUdp::State(game_states.next.clone())).unwrap();
}
@@ -1387,6 +1420,28 @@ impl GameStates {
#[derive(Resource, Default, DerefMut, Deref)]
struct PacketTimeElapsed(Stopwatch);
+#[derive(Resource, Default)]
+struct LastPacketTimesElapsed {
+ times: [f32; 15],
+ len: usize,
+}
+
+impl LastPacketTimesElapsed {
+ fn push(&mut self, time: f32) {
+ if self.len == self.times.len() {
+ self.times.rotate_left(1);
+ self.times[self.len - 1] = time;
+ } else {
+ self.times[self.len] = time;
+ self.len += 1;
+ }
+ }
+
+ fn avg(&self) -> f32 {
+ self.times.iter().take(self.len).sum::() / self.len as f32
+ }
+}
+
pub struct RocketSimPlugin;
impl Plugin for RocketSimPlugin {
@@ -1396,6 +1451,7 @@ impl Plugin for RocketSimPlugin {
.insert_resource(GameStates::default())
.insert_resource(DirectorTimer(Timer::new(Duration::from_secs(12), TimerMode::Repeating)))
.insert_resource(PacketTimeElapsed::default())
+ .insert_resource(LastPacketTimesElapsed::default())
.insert_resource(PacketUpdated::default())
.insert_resource(GameMode::default())
.add_plugins(UdpRendererPlugin)