====== PyO3でLÖVEもどきを作る (2) ====== 作成日: 2023-08-14 (月) [[https://youtu.be/x0HpQJHWK1w|第9回 PyO3でLÖVEもどきを作る (2)]] ===== プロジェクト pyove ===== ==== Cargo.toml ==== [package] name = "pyove" version = "0.1.0" edition = "2021" [dependencies.pyo3] version = "0.19.2" features = ["auto-initialize", "macros"] ==== src/main.rs ==== use pyo3::prelude::*; use std::{env, path::Path, process::exit}; pub mod graphics; #[pyfunction] fn load() { println!("default load"); } #[pyfunction] fn draw() { println!("default draw"); } #[pyfunction] fn update(dt: f32) { println!("default update {}", dt); } #[pymodule] fn pyove(py: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_function(wrap_pyfunction!(load, m)?)?; m.add_function(wrap_pyfunction!(draw, m)?)?; m.add_function(wrap_pyfunction!(update, m)?)?; graphics::register_graphics_module(py, m)?; Ok(()) } fn main() -> PyResult<()> { // // コマンドライン引数のパース // let args: Vec = env::args().collect(); if args.len() != 2 { // コマンドライン引数がただ1つだけ与えられていなければ終了する eprintln!("Usage: {} GAMEDIR", args[0]); exit(1); } let gamedir = Path::new(&args[1]); let main_file = gamedir.join("main.py"); if !main_file.exists() { // main.pyが存在しなければ終了する eprintln!("Error: {} not exists.", main_file.to_str().unwrap()); exit(2); } // main.pyの内容を読み込む let main_code = std::fs::read_to_string(main_file)?; // // activate Python interpreter // // pyoveをモジュールとして登録 pyo3::append_to_inittab!(pyove); Python::with_gil(|py| { // ゲームディレクトリをインポートパスに追加する let sys_path_code = format!( "import sys; sys.path.append('{}')", gamedir.to_str().unwrap() ); py.run(&sys_path_code, None, None)?; // pyoveモジュールにアクセスしたいので、ここでインポートする let pyove = PyModule::import(py, "pyove")?; // main.pyを実行 py.run(&main_code, None, None)?; // main.pyでこの3つの関数は置き換え可能 let load_fn = pyove.getattr("load")?; let update_fn = pyove.getattr("update")?; let draw_fn = pyove.getattr("draw")?; // 試しにloadとupdateとdrawを実行してみる load_fn.call0()?; update_fn.call1((123,))?; draw_fn.call0()?; Ok(()) }) } ==== src/graphics.rs ==== // // このプロジェクトではset_colorとrectangle関数のみを提供する // use pyo3::prelude::*; #[pyfunction] #[pyo3(name = "setColor")] fn set_color(r: f32, g: f32, b: f32) { // 仮の処理 println!("setColor {} {} {}", r, g, b); } #[pyfunction] fn rectangle(mode: &str, x: i32, y: i32, w: i32, h: i32) { // 仮の処理 println!("rectangle {} {} {} {} {}", mode, x, y, w, h) } pub fn register_graphics_module(py: Python<'_>, parent_module: &PyModule) -> PyResult<()> { // see: https://pyo3.rs/v0.19.2/module#python-submodules // サブモジュールとしてgraphicsを登録する // graphicsに、setColorとrectangleを登録する let m = PyModule::new(py, "graphics")?; m.add_function(wrap_pyfunction!(set_color, m)?)?; m.add_function(wrap_pyfunction!(rectangle, m)?)?; parent_module.add_submodule(m)?; Ok(()) } ==== example/main.py ==== 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, 0.4, 0.4) pyove.graphics.rectangle("fill", x, y, w, h) # overwrite default module functions pyove.load = load pyove.draw = draw pyove.update = update === 参考 === オリジナルの動作は、こちらを参考にしてください。 * https://love2d.org/wiki/love