Very Slow and Choppy Camera Controller in Rust - rust

I am currently working on simple Rendering in Rust using the OpenGL Wrapper Glium.
I have a simple 3D model and a Camera ready set up and started working on a fitting camera controller. I'm using device_query to check for keyboard input and if it's a Specific Keyboard input I just change the x and y coordinates. This works, however it works really bad, inconstantly and choppy.
My Event loop:
event_loop.run(move |event, _, control_flow| {
let next_frame_time =
std::time::Instant::now() + std::time::Duration::from_nanos(16_666_667);
####### This is where I change the coordinates ########
x = keyboardinput::keyboard_input_x(x);
y = keyboardinput::keyboard_input_y(y);
*control_flow = glutin::event_loop::ControlFlow::WaitUntil(next_frame_time);
match event {
glutin::event::Event::WindowEvent { event, .. } => match event {
glutin::event::WindowEvent::CloseRequested => {
*control_flow = glutin::event_loop::ControlFlow::Exit;
return;
}
_ => return,
},
glutin::event::Event::NewEvents(cause) => match cause {
glutin::event::StartCause::ResumeTimeReached { .. } => (),
glutin::event::StartCause::Init => (),
_ => return,
},
_ => return,
}
This is my Keyboard input rs file:
use device_query::{DeviceQuery, DeviceState, Keycode};
pub fn keyboard_input_x(mut x: f32) -> f32 {
let device_state = DeviceState::new();
let keys: Vec<Keycode> = device_state.get_keys();
for key in keys.iter() {
println!("Pressed key: {:?}", key);
if key == &Keycode::W {
x -= 0.03;
return x;
}
if key == &Keycode::S {
x += 0.03;
return x;
}
}
return x;
}
pub fn keyboard_input_y(mut y: f32) -> f32 {
let device_state = DeviceState::new();
let keys: Vec<Keycode> = device_state.get_keys();
for key in keys.iter() {
println!("Pressed key: {:?}", key);
if key == &Keycode::Space {
y += 0.03;
return y;
}
if key == &Keycode::LControl {
y -= 0.03;
return y;
}
}
return y;
}
What can I do the make the camera as smooth as possible?

Instead of directly updating your x/y position in the input handler, create two variables holding your x velocity and y velocity (xv/yv), and then in your input handler do something like this:
bool space_pressed = false;
for key in keys.iter() {
if key == &Keycode::Space {
space_pressed = true;
}
}
if space_pressed {
yv = 1.0; // Move up.
} else {
yv = 0.0; // Don't move.
}
And in your main loop where you compute each frame, count the amount of time since the last frame in seconds (often called delta-t or dt), and integrate your x velocity and y velocity to a new position:
// Update camera position.
x += xv * dt;
y += yv * dt;
Now when you hold space you will move up at a constant velocity, rather than unpredictably and choppy.

Related

A program that used to work fine suddenly started crashing due to stack overflow

I made a simple chess engine in Rust. It looks 2 moves ahead (2 moves for itself and 2 for its opponent). The program worked just fine. I run it many times (30+ times at least) and it worked fine. Suddenly, without making any changes to the code, the program started crashing due to stack overflow. I did use recursion, but I only went 4 moves deep.
This is the important part of the code. The variable trenutna_rekursija keeps track of how many recursive calls to the function there was. When trenutna_rekursija reaches broj_rekursija (and broj_rekursija is 4), the recursive calls stop, I call the function which evaluates the chess position by counting material.
As you can see, I increment trenutna_rekursija by 1 when making another recursive call:
let (vrednost_poteza, najbolji_potez) = tabla_nakon_poteza.izracunaj_rekursivno(
&Some(najbolja_opcija_za_sad),
!ja_volim_vise,
broj_rekursija,
trenutna_rekursija + 1,
izracunate_table,
);
If you are wondering what is hashmap variable called izracunate_table doing there when it is not used. I forgot to remove it, I tried to optimize the algorithm with the hashmap, I didn't succeed, I forgot to remove it so I needlessly pass the hashmap argument around. It is doing nothing.
pub fn najbolji_potez_i_njegova_evaluacija(&self, dubina: u8) -> (Option<Potez_bits>, f32) {
let protivnik_je_beli: bool = !self.beli_je_na_potezu();
let ja_sam_beli: bool = self.beli_je_na_potezu();
let mut najbolji_potez: Option<Potez_bits> = None;
let mut najbolja_evaluacija: f32 = vrednost_mata(ja_sam_beli);
let legalni_potezi: Vec<Potez_bits> = self.svi_legalni_potezi();
for potez in legalni_potezi {
let tabla: Tabla = self.tabla_nakon_poteza_bits(&potez);
let (vrednost_poteza, _) = tabla.izracunaj_rekursivno(&None, protivnik_je_beli, dubina, 1, &mut HashMap::new())/* .unwrap() */ ;
if ovo_je_najbolji_potez(najbolja_evaluacija, vrednost_poteza, ja_sam_beli) {
najbolji_potez = Some(potez);
najbolja_evaluacija = vrednost_poteza;
}
}
(najbolji_potez, najbolja_evaluacija)
}
pub fn izracunaj_rekursivno(
&self,
vrednost_koju_protivnik_ima_u_dzepu: &Option<f32>,
ja_volim_vise: bool,
broj_rekursija: u8,
trenutna_rekursija: u8,
izracunate_table: &mut HashMap<Tabla, f32>,
) -> (f32, bool) {
if trenutna_rekursija >= broj_rekursija {
let sopstvena_evaluacija: f32 =
self.nerekursivno_evaluiraj_poziciju(&self.to_nekompresirana_tabla());
// return Some(sopstvena_evaluacija);
if protivnik_se_zajebo(
vrednost_koju_protivnik_ima_u_dzepu,
sopstvena_evaluacija,
ja_volim_vise,
) {
return (sopstvena_evaluacija, false);
} else {
return (sopstvena_evaluacija, true);
}
}
let ja_sam_beli: bool = self.beli_je_na_potezu();
let legalni_potezi: Vec<Potez_bits> = self.svi_legalni_potezi();
let broj_legalnih_poteza: usize = legalni_potezi.len();
if broj_legalnih_poteza == 0 {
if self.igrac_je_u_sahu(&self.to_nekompresirana_tabla()) {
// return Some(vrednost_mata(ja_volim_vise))
return (vrednost_mata(ja_volim_vise), true);
} else {
if protivnik_se_zajebo(vrednost_koju_protivnik_ima_u_dzepu, 0.0, ja_volim_vise) {
return (0.0, false);
} else {
return (0.0, true);
}
}
}
if self.pre_koliko_poteza_je_50_move_rule_pomeren() >= 50 {
if protivnik_se_zajebo(vrednost_koju_protivnik_ima_u_dzepu, 0.0, ja_volim_vise) {
return (0.0, false);
} else {
return (0.0, true);
}
}
let mut najbolja_opcija_za_sad: f32 = vrednost_mata(ja_sam_beli);
for legalan_potez in legalni_potezi {
let tabla_nakon_poteza: Tabla = self.tabla_nakon_poteza_bits(&legalan_potez);
let vrednost_table: Option<&f32> = izracunate_table.get(&tabla_nakon_poteza);
let (vrednost_poteza, najbolji_potez) = tabla_nakon_poteza.izracunaj_rekursivno(
&Some(najbolja_opcija_za_sad),
!ja_volim_vise,
broj_rekursija,
trenutna_rekursija + 1,
izracunate_table,
);
if najbolji_potez {
najbolja_opcija_za_sad = vrednost_poteza;
}
if protivnik_se_zajebo(
vrednost_koju_protivnik_ima_u_dzepu,
najbolja_opcija_za_sad,
ja_volim_vise,
) {
return (najbolja_opcija_za_sad, false);
}
}
(najbolja_opcija_za_sad, true)
}
Here is the full code on GitHub if you are interested in running it yourself. I am still prototyping, code is nowhere near done. If you wish to run it, first go to the main.rs file and in the main function remove all functions called from main except for odigraj_partiju(true, 4);.
If odigraj_partiju(true, 4); is commented out, then remove the comments.
That's the function that you want to run (other functions, such as socket_proba communicate moves via TCP sockets).

egui glfw input behaving strangely

I am trying to integrate egui into a custom engine. In particular I am dealing with the input handling, like mouse and keyboard.
I used this repo as a reference, but its using opengl and I am using vulkan through ash.
To get things working I merely copied the event handling function into my won project:
pub struct DebugGui
{
pub ctx : egui::Context,
textures : Vec::<GpuDataDescriptor>,
raw_input : egui::RawInput,
pub pointer_pos: Pos2,
pub modifiers: egui::Modifiers,
shader : ShaderHandle,
}
impl DebugGui
{
pub fn init<RenderContext>(
render_context : &mut RenderContext,
window_size : (u32, u32)
) -> DebugGui
where RenderContext : GpuResourceCreation
{
let ctx = egui::Context::default();
let (width, height) = window_size;
let raw_input = egui::RawInput
{
screen_rect : Some(egui::Rect::from_points(&[
Default::default(),
[width as f32, height as f32].into(),
])),
pixels_per_point : Some(1.0),
..Default::default()
};
DebugGui
{
ctx,
shader : render_context.add_shader_from_memory(
&vec!["debug_ui.vert".to_string(), "debug_ui.frag".to_string()],
&vec![IMGUI_VERT_SHADER.to_string(), IMGUI_FRAG_SHADER.to_string()]),
textures : Vec::new(),
raw_input,
pointer_pos : Pos2::default(),
modifiers : egui::Modifiers::default(),
}
}
pub fn handle_event(&mut self, event: &glfw::WindowEvent)
{
match event {
FramebufferSize(width, height) => {
self.raw_input.screen_rect = Some(Rect::from_min_size(
Pos2::new(0f32, 0f32),
egui::vec2(*width as f32, *height as f32)
/ self.raw_input.pixels_per_point.unwrap(),
))
}
MouseButton (mouse_btn, glfw::Action::Press, _) =>
self.raw_input.events.push(egui::Event::PointerButton {
pos: self.pointer_pos,
button: match mouse_btn {
glfw::MouseButtonLeft => egui::PointerButton::Primary,
glfw::MouseButtonRight => egui::PointerButton::Secondary,
glfw::MouseButtonMiddle => egui::PointerButton::Middle,
_ => unreachable!(),
},
pressed: true,
modifiers: self.modifiers,
}),
MouseButton (mouse_btn, glfw::Action::Release, _) =>
self.raw_input.events.push(egui::Event::PointerButton {
pos: self.pointer_pos,
button: match mouse_btn {
glfw::MouseButtonLeft => egui::PointerButton::Primary,
glfw::MouseButtonRight => egui::PointerButton::Secondary,
glfw::MouseButtonMiddle => egui::PointerButton::Middle,
_ => unreachable!(),
},
pressed: false,
modifiers: self.modifiers,
}),
CursorPos(x, y) => {
self.pointer_pos = pos2(
*x as f32 / self.raw_input.pixels_per_point.unwrap(),
*y as f32 / self.raw_input.pixels_per_point.unwrap(),
);
self
.raw_input
.events
.push(egui::Event::PointerMoved(self.pointer_pos))
}
Key(keycode, _scancode, glfw::Action::Release, keymod) => {
use glfw::Modifiers as Mod;
if let Some(key) = translate_virtual_key_code(*keycode) {
self.modifiers = Modifiers {
alt: (*keymod & Mod::Alt == Mod::Alt),
ctrl: (*keymod & Mod::Control == Mod::Control),
shift: (*keymod & Mod::Shift == Mod::Shift),
// TODO: GLFW doesn't seem to support the mac command key
// mac_cmd: keymod & Mod::LGUIMOD == Mod::LGUIMOD,
command: (*keymod & Mod::Control == Mod::Control),
..Default::default()
};
self.raw_input.events.push(Event::Key {
key,
pressed: false,
modifiers: self.modifiers,
});
}
}
Key(keycode, _scancode, glfw::Action::Press | glfw::Action::Repeat, keymod) => {
use glfw::Modifiers as Mod;
if let Some(key) = translate_virtual_key_code(*keycode) {
self.modifiers = Modifiers {
alt: (*keymod & Mod::Alt == Mod::Alt),
ctrl: (*keymod & Mod::Control == Mod::Control),
shift: (*keymod & Mod::Shift == Mod::Shift),
command: (*keymod & Mod::Control == Mod::Control),
..Default::default()
};
if self.modifiers.command && key == egui::Key::X {
self.raw_input.events.push(egui::Event::Cut);
} else if self.modifiers.command && key == egui::Key::C {
self.raw_input.events.push(egui::Event::Copy);
} else if self.modifiers.command && key == egui::Key::V {
todo!()
} else {
self.raw_input.events.push(Event::Key {
key,
pressed: true,
modifiers: self.modifiers,
});
}
}
}
Char(c) => {
self.raw_input.events.push(Event::Text(c.to_string()));
}
Scroll (x, y) => {
self.raw_input.events.push(egui::Event::Scroll(vec2(*x as f32, *y as f32)));
}
_ => {}
}
}
And I am calling it inside the glfw event loop:
for (_, event) in glfw::flush_messages(&self.events)
{
debug_gui.handle_event(&event);
}
It's very glitchy for some reason.
With this basic example, if I hover my mouse over the arrow it flickers like crazy, I cannot resize the quad of the widget and if I click on the click me button it will call the body a few hundred times before stopping.
It seems part of the issue is that you need to reset the raw input every frame, like this:
pub fn handle_event(&mut self, event: &glfw::WindowEvent)
{
let (width, height) = self.window_size;
self.raw_input = egui::RawInput
{
screen_rect : Some(egui::Rect::from_points(&[
Default::default(),
[width as f32, height as f32].into(),
])),
pixels_per_point : Some(1.0),
..Default::default()
};
}
But for some reason i cannot resize teh widget I made.

Tic Tac Toe - Minimax

I'm trying to build a tic-tac-toe game using minimax algorithm with rust. And I'm stuck. I tried to write a rust code based on the psudeo code on the wikipedia page. https://en.wikipedia.org/wiki/Minimax. However, it didn't work. Ai always makes the first possible move. I would be glad if you could help me.
In main.rs
fn main() {
let mut g = Game::new();
while g.game_state() == Game_State::Continuous {
g.print();
println!("{}", minimax(&g));
if g.turn == Player::Cross {
g.take_input();
}
else {
g = best_move(&g);
}
}
g.print();
if let Game_State::Win(Player::None) = g.game_state() {
println!("Draw");
}
else {
g.print_winner();
}
}
In ai.rs
pub fn child_nodes(game: &Game) -> Vec<Game> {
let mut children: Vec<Game> = Vec::new();
for r in 0..3 {
for c in 0..3 {
if game.grid[r][c] == Player::None {
let mut child = game.clone();
child.grid[r][c] = game.turn;
child.turn = reverse_player(child.turn);
children.push(child);
}
}
}
return children;
}
pub fn minimax(game: &Game) -> isize {
match game.game_state() {
Game_State::Win(winner) => to_scor(winner),
Game_State::Continuous => {
use std::cmp::{min, max};
let children_vec = child_nodes(&game);
let mut score: isize;
if game.turn == Player::Cross {
score = -2;
for i in &children_vec {
score = max(score, minimax(i));
}
}
else {
score = 2;
for i in &children_vec {
score = min(score, minimax(i));
}
}
return score;
}
}
}
pub fn best_move(game: &Game) -> Game {
let children = child_nodes(game);
let mut values: Vec<isize> = Vec::new();
for i in 0..children.len() {
values.push(minimax(&children[i]));
}
let mut index: usize = 0;
let iter = values.iter().enumerate();
if game.turn == Player::Cross {
if let Option::Some(t) = iter.max() {
index = t.0;
}
}
else if game.turn == Player::Circle {
if let Option::Some(t) = iter.min() {
index = t.0;
}
}
let best_pos = children[index];
best_pos
}
pub fn to_scor(x: Player) -> isize {
match x {
Player::Cross => 1,
Player::Circle => -1,
Player::None => 0
}
}
.enumerate() returns an iterator over tuples, and .max() and .min() on an iterator of tuples will compare the tuples - that is, (1, x) is always considered to be less than (2, y) for any values of x and y. This can be demonstrated with this snippet:
fn main() {
let v = vec![3, 1, 2, 5, 3, 6, 7, 2];
println!("{:?}", v.iter().enumerate().min());
println!("{:?}", v.iter().enumerate().max());
}
which prints:
Some((0, 3))
Some((7, 2))
which are just the first and last elements of the list (and not the minimum or maximum elements).
However, as shown here, you can use max_by to use your own function to compare the tuples.

What does "match ref" mean in rust?

fn main () {
let x: &Option<_> = &Some(90);
match x {
// no ref
&Some(y) => {
print!("{}", y);
},
&None => {
},
}
match x {
// ref
&Some(ref y) => {
print!("{}", y);
},
&None => {
},
}
}
// What's the difference between the two?
ref is inverse of & on the left side of match. In other words:
let ref a = x;
Is the same as
let a = &x;
The reason for that syntax is that with structural matching it is not always possible to use 2nd form:
struct Foo(usize);
let Foo(ref a) = x;

How to assign 2 variables simultaneously in Rust?

I'm currently teaching myself Rust with a roguelike tutorial, and I'm attempting to get a key press to move a character diagonally which would mean player_x -=1, player_y -= 1 for up left.
No matter which way I try to arrange the code, I keep getting error messages from the compiler. I couldn't find any example of this anywhere in the documentation or on GitHub.
Key { code: Escape, .. } => return true, // exit game
// movement keys
Key { code: Up, .. } => *player_y -= 1,
Key { code: Down, .. } => *player_y += 1,
Key { code: Left, .. } => *player_x -= 1,
Key { code: Right, .. } => *player_x += 1,
Key { printable: 'k', .. } => *player_y -= 1,
Key { printable: 'k', .. } => *player_x -= 1,
_ => {}
You can kind of see what I'm trying to do here, but this throws an error message saying the pattern is unreachable, how would I fix this code?
Here's the full code, it's fairly small, not sure what would be alone necessary to compile:
extern crate tcod;
extern crate input;
use tcod::console::*;
use tcod::colors;
// actual size of the window
const SCREEN_WIDTH: i32 = 80;
const SCREEN_HEIGHT: i32 = 50;
const LIMIT_FPS: i32 = 20; // 20 frames-per-second maximum
fn handle_keys(root: &mut Root, player_x: &mut i32, player_y: &mut i32) -> bool {
use tcod::input::Key;
use tcod::input::KeyCode::*;
let key = root.wait_for_keypress(true);
match key {
Key { code: Enter, alt: true, .. } => {
// Alt+Enter: toggle fullscreen
let fullscreen = root.is_fullscreen();
root.set_fullscreen(!fullscreen);
}
Key { code: Escape, .. } => return true, // exit game
// movement keys
Key { code: Up, .. } => *player_y -= 1,
Key { code: Down, .. } => *player_y += 1,
Key { code: Left, .. } => *player_x -= 1,
Key { code: Right, .. } => *player_x += 1,
Key { printable: 'k', ..} => *player_y -= 1,
Key { printable: 'k', ..} => *player_x -= 1,
_ => {},
}
false
}
fn main() {
let mut root = Root::initializer()
.font("terminal8x8_gs_tc.png", FontLayout::Tcod)
.font_type(FontType::Greyscale)
.size(SCREEN_WIDTH, SCREEN_HEIGHT)
.title("Rust/libtcod tutorial")
.init();
tcod::system::set_fps(LIMIT_FPS);
let mut player_x = SCREEN_WIDTH / 2;
let mut player_y = SCREEN_HEIGHT / 2;
while !root.window_closed() {
root.set_default_foreground(colors::WHITE);
root.put_char(player_x, player_y, '#', BackgroundFlag::None);
root.flush();
root.put_char(player_x, player_y, ' ', BackgroundFlag::None);
// handle keys and exit game if needed
let exit = handle_keys(&mut root, &mut player_x, &mut player_y);
if exit {
break
}
}
}
How to assign 2 variables simultaneously in Rust?
You cannot. You can bind two variables at once:
let (a, b) = (1, 2);
If you aren't trying to create new bindings, you need to have two assignment statements:
let mut a = 1;
let mut b = 2;
a = 3;
b = 4;
In your case, for a match statement, you need to introduce a block:
let key = 42;
let mut a = 1;
let mut b = 2;
match key {
0 => {
a += 1;
b -= 1;
}
_ => {
a -= 10;
b *= 100;
}
}
You could also have the match expression evaluate to a tuple, which you then create new bindings for and apply them afterwards:
let key = 42;
let mut x = 1;
let mut y = 2;
let (d_x, d_y) = match key {
0 => (1, -1),
_ => (10, 10),
};
x += d_x;
y += d_y;
I strongly recommend reading The Rust Programming Language instead of trying to learn Rust by intuition or trial and error. It has an entire chapter on the match statement.
See also:
How to swap two variables?
The second printable: 'k' is unreachable, since the first will match instead. What you want is doing both assignments in the same arm of the match, like this:
Key { printable: 'k', .. } => {
*player_y -= 1;
*player_x -= 1;
}

Resources