How can I work around not being able to export functions with lifetimes when using wasm-bindgen?
我正在尝试编写一个可以在浏览器中运行的简单游戏,并且由于浏览器,rust和wasm-bindgen的限制,我很难建模一个游戏循环。
浏览器中的典型游戏循环遵循以下一般模式:
1 2 3 4 5 | function mainLoop() { update(); draw(); requestAnimationFrame(mainLoop); } |
如果我要在rust / wasm-bindgen中建模这个确切的模式,它将看起来像这样:
1 2 3 4 5 | let main_loop = Closure::wrap(Box::new(move || { update(); draw(); window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal }) as Box<FnMut()>); |
与javascript不同,我无法从自身内部引用
有人建议的另一种方法是遵循"人生博弈"示例中说明的模式。从高层次上讲,它涉及导出包含游戏状态并包含公共
我很难找到解决这些限制的方法,并且正在寻找建议。
对此建模最简单的方法可能是将
但是,在Rust中,您还可以利用以下事实:实际上并未捕获任何变量的闭包大小为零,这意味着该闭包的
1 2 3 4 5 6 7 8 9 | #[wasm_bindgen] pub fn main_loop() { update(); draw(); let window = ...; let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>); window.request_animation_frame(closure.as_ref().unchecked_ref()); closure.forget(); // not actually leaking memory } |
如果状态中存在生命周期,那么很遗憾,这与返回JS不兼容,因为当您一路返回JS事件循环时,所有WebAssembly堆栈框架都会弹出,这意味着任何生命周期都将失效。这意味着您的游戏状态在
我是Rust的新手,但这是解决相同问题的方法。
您可以通过从
由于我一直在使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | pub struct MyGame { ... should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately. } ... let window = web_sys::window().expect("should have a window in this context"); let application_reference = Rc::new(RefCell::new(MyGame::new())); let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback. let application_reference = application_reference.clone(); let request_animation_frame_callback = Closure::wrap(Box::new(move || { let mut application = application_reference.borrow_mut(); application.should_request_render = true; application.handle_animation_frame(); // handle_animation_frame being your main loop. }) as Box<FnMut()>); let window = window.clone(); move || { window .request_animation_frame( request_animation_frame_callback.as_ref().unchecked_ref(), ) .unwrap(); } }; request_animation_frame(); // fire the first request immediately let timer_closure = Closure::wrap( Box::new(move || { // move both request_animation_frame and application_reference here. let mut application = application_reference.borrow_mut(); if application.should_request_render { application.should_request_render = false; request_animation_frame(); } }) as Box<FnMut()> ); window.set_interval_with_callback_and_timeout_and_arguments_0( timer_closure.as_ref().unchecked_ref(), 25, // minimum ms per frame )?; timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page |
您可以在游戏状态下将