youtube:python-mixing-010
差分
このページの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:// | ||