Estoy tratando de crear un bucle requestAnimationFrame, que llamará a game.render() para cada fotograma. Estoy siguiendo este 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
        }
    }
}

Pero da el siguiente 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.

Si comento game.render() se compila. Pero quiero mantener un estado que se actualizará para crear una animación. ¿Que estoy haciendo mal? ¿Por qué Closure no permite llamar a métodos de estructura?

Gracias de antemano por su ayuda.

1
Shimul Chowdhury 27 oct. 2019 a las 12:19

2 respuestas

La mejor respuesta

Encontré el problema. La estructura del juego sería como:

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();
    }
}

Olvidé poner el símbolo & para self. Gracias Pauan # 6666 del canal Discord # wg-wasm por señalar eso.

2
Shimul Chowdhury 28 oct. 2019 a las 06:38

comentario de Shimul funcionó para mí. Comparto aquí mi función de bloqueo start_loop del juego.

Donde la línea interesante está al final:

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(())
}}
0
Tinmarino 26 feb. 2021 a las 12:59