1
votes

I am trying to create a requestAnimationFrame loop, which will call game.render() for every frame. I am following this tutorial - https://rustwasm.github.io/wasm-bindgen/examples/request-animation-frame.html


struct Game {
    state: GameState,
    ctx: web_sys::CanvasRenderingContext2d
}

impl Game {
    fn render(self: Game) {
        self.ctx.begin_path();
        self.ctx.arc(self.state.x, 50.0, 40.0, 0.0, 2.0 * std::f64::consts::PI);
        self.ctx.stroke();
    }
}

#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {

    let game = init();

    let f = Rc::new(RefCell::new(None));
    let g = f.clone();


    *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        game.render();
        request_animation_frame(f.borrow().as_ref().unwrap());
    }) as Box<dyn FnMut()>));

    request_animation_frame(g.borrow().as_ref().unwrap());
    Ok(())
}


fn init() -> Game {
    let doc = document();
    let canvas = doc.create_element("canvas").unwrap();
    canvas.set_attribute("width", "800px").unwrap();
    canvas.set_attribute("height", "800px").unwrap();
    canvas.set_id("fp-canvas");
    body().append_child(&canvas).expect("Could not attach canvas");

    Game {
        ctx: context(),
        state: GameState {
            x: 3.0
        }
    }
}

But it gives following error -


error[E0277]: expected a `std::ops::FnMut<()>` closure, found `[closure@src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
  --> src/lib.rs:89:42
   |
89 |       *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
   |  __________________________________________^
90 | |         game.render();
91 | |         request_animation_frame(f.borrow().as_ref().unwrap());
92 | |     }) as Box<dyn FnMut()>));
   | |______^ expected an `FnMut<()>` closure, found `[closure@src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
   |
   = help: the trait `std::ops::FnMut<()>` is not implemented for `[closure@src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]`
   = note: wrap the `[closure@src/lib.rs:89:51: 92:6 game:Game, f:std::rc::Rc<std::cell::RefCell<std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>>>]` in a closure with no arguments: `|| { /* code */ }
   = note: required for the cast to the object type `dyn std::ops::FnMut()`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `fighting-pixel`.

To learn more, run the command again with --verbose.

If I comment out game.render() it compiles. But I want to keep a state which will be updated to create an animation. What I am doing wrong? Why Closure doesn't allow to call struct methods?

Thanks in advance for your help.

2

2 Answers

2
votes

I found the issue. The Game struct would be like -

impl Game {
    fn render(self: &Game) {
        self.ctx.begin_path();
        self.ctx.arc(self.state.x, 50.0, 40.0, 0.0, 2.0 * std::f64::consts::PI);
        self.ctx.stroke();
    }
}

Forgot to put & symbol for self. Thanks Pauan#6666 from Discord channel #wg-wasm for pointing that out.

0
votes

Shimul comment worked for me. I share here my game start_loop blocking function.

where the insteresting line is at the end:

draw_scene(&self.state, &self.gl_context).unwrap();
impl GameGl { pub fn start_loop(mut self) -> Result<(), JsValue> {
    // Render loop
    // Dont ask me
    let f = Rc::new(RefCell::new(None));
    let g = f.clone();
    const FPS_THROTTLE: f64 = 1000.0 / 60.0; // milliseconds / frames
    let mut previous: f64 = js_sys::Date::now();
    *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
        request_animation_frame(f.borrow().as_ref().unwrap());

        // Get time (miliseconds)
        let now = js_sys::Date::now();

        // Clause: must work or sleeep ?
        // The current rotation angle 1 rad/sec
        if now < previous + FPS_THROTTLE {
           return ();
        }

        // Update time
        let delta_time = now - previous;
        previous = now;

        // Update game
        self.state.cube_rotation += delta_time as f32 * 0.001;

        // Draw
        draw_scene(&self.state, &self.gl_context).unwrap();
            //&self.gl, &self.program_info, &self.texture, &buffers, &state).unwrap();

    }) as Box<dyn FnMut()>));

    console::log_1(&"Requesting animation frame".into());
    request_animation_frame(g.borrow().as_ref().unwrap());
    //let program_info = 
    Ok(())
}}