差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
| youtube:python-mixing-010 [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1 | youtube:python-mixing-010 [2024/02/29 11:56] (現在) – [PyO3でLÖVEもどきを作る (3)] freemikan | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| + | ====== PyO3でLÖVEもどきを作る (3) ====== | ||
| + | |||
| + | 作成日: 2023-08-16 (水) | ||
| + | |||
| + | [[https:// | ||
| + | |||
| + | ===== プロジェクト pyove ===== | ||
| + | |||
| + | ==== Cargo.toml ==== | ||
| + | |||
| + | <file toml> | ||
| + | [package] | ||
| + | name = " | ||
| + | version = " | ||
| + | edition = " | ||
| + | |||
| + | [dependencies] | ||
| + | sdl2 = " | ||
| + | |||
| + | [dependencies.pyo3] | ||
| + | version = " | ||
| + | features = [" | ||
| + | </ | ||
| + | |||
| + | ==== src/main.rs ==== | ||
| + | |||
| + | <file rust> | ||
| + | pub mod graphics; | ||
| + | pub mod pyovemodule; | ||
| + | pub mod sdl2global; | ||
| + | |||
| + | use pyo3:: | ||
| + | use pyo3:: | ||
| + | use sdl2:: | ||
| + | use sdl2global:: | ||
| + | use std:: | ||
| + | use std::{env, path::Path, process:: | ||
| + | |||
| + | fn main() -> PyResult< | ||
| + | // | ||
| + | // コマンドライン引数のパース | ||
| + | // | ||
| + | let args: Vec< | ||
| + | if args.len() != 2 { | ||
| + | // コマンドライン引数がただ1つだけ与えられていなければ終了する | ||
| + | eprintln!(" | ||
| + | exit(1); | ||
| + | } | ||
| + | |||
| + | let gamedir = Path:: | ||
| + | let main_file = gamedir.join(" | ||
| + | if !main_file.exists() { | ||
| + | // main.pyが存在しなければ終了する | ||
| + | eprintln!(" | ||
| + | exit(2); | ||
| + | } | ||
| + | |||
| + | // main.pyの内容を読み込む | ||
| + | let main_code = std:: | ||
| + | |||
| + | // activate Python interpreter | ||
| + | Python:: | ||
| + | // ゲームディレクトリをインポートパスに追加する | ||
| + | let sys = PyModule:: | ||
| + | sys.getattr(" | ||
| + | .getattr(" | ||
| + | .call1((gamedir.to_str().unwrap(), | ||
| + | |||
| + | // pyoveをモジュールとして登録 | ||
| + | let pyove = pyovemodule:: | ||
| + | let py_modules: &PyDict = sys.getattr(" | ||
| + | py_modules.set_item(" | ||
| + | |||
| + | // main.pyを実行 | ||
| + | py.run(& | ||
| + | |||
| + | // これら3つの関数はPython側のコードで置き換え可能 | ||
| + | let user_load = pyove.getattr(" | ||
| + | let user_update = pyove.getattr(" | ||
| + | let user_draw = pyove.getattr(" | ||
| + | |||
| + | user_load.call0()?; | ||
| + | |||
| + | // ゲームループの準備 | ||
| + | let mut event_pump = sdl_event_pump(); | ||
| + | let nanosecs_per_update = Duration:: | ||
| + | let clock = Instant:: | ||
| + | let mut last_update = clock.elapsed(); | ||
| + | |||
| + | // ゲームループ | ||
| + | ' | ||
| + | let start = clock.elapsed(); | ||
| + | |||
| + | // SDLイベント処理 | ||
| + | for event in event_pump.poll_iter() { | ||
| + | match event { | ||
| + | Event::Quit { .. } => break ' | ||
| + | _ => {} | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // アップデート | ||
| + | let delta_time = (start - last_update).as_secs_f32(); | ||
| + | user_update.call1((delta_time, | ||
| + | |||
| + | // 描画 | ||
| + | sdl_render_set_draw_color((0, | ||
| + | sdl_render_clear(); | ||
| + | user_draw.call0()?; | ||
| + | sdl_render_present(); | ||
| + | |||
| + | // スリープでフレームレートを同期を強制 | ||
| + | let passed = clock.elapsed() - start; | ||
| + | if passed < nanosecs_per_update { | ||
| + | std:: | ||
| + | } | ||
| + | last_update = start; | ||
| + | } | ||
| + | |||
| + | Ok(()) | ||
| + | }) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== src/ | ||
| + | |||
| + | <file rust> | ||
| + | use crate:: | ||
| + | use pyo3:: | ||
| + | |||
| + | # | ||
| + | fn load() { | ||
| + | println!(" | ||
| + | } | ||
| + | |||
| + | # | ||
| + | fn draw() { | ||
| + | println!(" | ||
| + | } | ||
| + | |||
| + | # | ||
| + | fn update(dt: f32) { | ||
| + | println!(" | ||
| + | } | ||
| + | |||
| + | pub fn create_pyove(py: | ||
| + | let m = PyModule:: | ||
| + | m.add_function(wrap_pyfunction!(load, | ||
| + | m.add_function(wrap_pyfunction!(draw, | ||
| + | m.add_function(wrap_pyfunction!(update, | ||
| + | graphics:: | ||
| + | Ok(m) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== src/ | ||
| + | |||
| + | <file rust> | ||
| + | // | ||
| + | // このプロジェクトではset_colorとrectangle関数のみを提供する | ||
| + | // | ||
| + | use crate:: | ||
| + | use pyo3:: | ||
| + | use sdl2:: | ||
| + | |||
| + | # | ||
| + | #[pyo3(name = " | ||
| + | //# | ||
| + | fn set_color(r: | ||
| + | // extract normalized color values | ||
| + | let ri = (r * 255.0) as u8; | ||
| + | let gi = (g * 255.0) as u8; | ||
| + | let bi = (b * 255.0) as u8; | ||
| + | sdl_render_set_draw_color((ri, | ||
| + | } | ||
| + | |||
| + | # | ||
| + | fn rectangle(mode: | ||
| + | let rect = Rect:: | ||
| + | if mode == " | ||
| + | sdl_render_fill_rect(rect); | ||
| + | } else if mode == " | ||
| + | sdl_render_draw_rect(rect); | ||
| + | } else { | ||
| + | eprintln!(" | ||
| + | } | ||
| + | } | ||
| + | |||
| + | pub fn register_graphics_module(py: | ||
| + | // see: https:// | ||
| + | // サブモジュールとしてgraphicsを登録する | ||
| + | // graphicsに、setColorとrectangleを登録する | ||
| + | let m = PyModule:: | ||
| + | m.add_function(wrap_pyfunction!(set_color, | ||
| + | m.add_function(wrap_pyfunction!(rectangle, | ||
| + | parent_module.add_submodule(m)?; | ||
| + | Ok(()) | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== src/ | ||
| + | |||
| + | <file rust> | ||
| + | use sdl2:: | ||
| + | use std:: | ||
| + | |||
| + | thread_local! { | ||
| + | static SDL_CONTEXT: | ||
| + | static CANVAS: RefCell< | ||
| + | } | ||
| + | |||
| + | fn init_sdl() -> Result< | ||
| + | let sdl_context = sdl2:: | ||
| + | Ok(RefCell:: | ||
| + | } | ||
| + | |||
| + | fn init_canvas() -> Result< | ||
| + | let video_subsystem = SDL_CONTEXT.with(|sdl| sdl.borrow().video())?; | ||
| + | |||
| + | let window = video_subsystem | ||
| + | .window(" | ||
| + | .position_centered() | ||
| + | .opengl() | ||
| + | .build() | ||
| + | .map_err(|e| e.to_string())?; | ||
| + | |||
| + | let canvas = window.into_canvas().build().map_err(|e| e.to_string())?; | ||
| + | |||
| + | Ok(RefCell:: | ||
| + | } | ||
| + | |||
| + | pub fn sdl_event_pump() -> EventPump { | ||
| + | SDL_CONTEXT.with(|sdl| sdl.borrow().event_pump()).unwrap() | ||
| + | } | ||
| + | |||
| + | pub fn sdl_render_clear() { | ||
| + | CANVAS.with(|canvas| canvas.borrow_mut().clear()); | ||
| + | } | ||
| + | |||
| + | pub fn sdl_render_present() { | ||
| + | CANVAS.with(|canvas| canvas.borrow_mut().present()); | ||
| + | } | ||
| + | |||
| + | pub fn sdl_render_set_draw_color< | ||
| + | where | ||
| + | C: Into< | ||
| + | { | ||
| + | CANVAS.with(|canvas| canvas.borrow_mut().set_draw_color(color)); | ||
| + | } | ||
| + | |||
| + | pub fn sdl_render_draw_rect(rect: | ||
| + | CANVAS | ||
| + | .with(|canvas| canvas.borrow_mut().draw_rect(rect)) | ||
| + | .unwrap(); | ||
| + | } | ||
| + | |||
| + | pub fn sdl_render_fill_rect(rect: | ||
| + | CANVAS | ||
| + | .with(|canvas| canvas.borrow_mut().fill_rect(rect)) | ||
| + | .unwrap(); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== example/ | ||
| + | |||
| + | <file python> | ||
| + | import pyove | ||
| + | #import foo # test import availability | ||
| + | |||
| + | # Python requires global scoped variable definitions | ||
| + | x, y, w, h = 0, 0, 0, 0 | ||
| + | |||
| + | def load(): | ||
| + | global x, y, w, h | ||
| + | x, y, w, h = 20, 20, 60, 20 | ||
| + | |||
| + | def update(dt): | ||
| + | global w, h | ||
| + | w += 1 | ||
| + | h += 1 | ||
| + | |||
| + | def draw(): | ||
| + | pyove.graphics.setColor(0, | ||
| + | pyove.graphics.rectangle(" | ||
| + | |||
| + | # overwrite default module functions | ||
| + | pyove.load = load | ||
| + | pyove.draw = draw | ||
| + | pyove.update = update | ||
| + | </ | ||
| + | |||
| + | === 参考 === | ||
| + | |||
| + | オリジナルの動作は、こちらを参考にしてください。 | ||
| + | |||
| + | * https:// | ||
