Introduzione
Consideriamo un Game Loop pattern del genere:
input();
update();
draw();
La funzione input
legge l'input dell'utente, la funzione update
gestisce le business rules, e draw
disegna a schermo.
Spesso e volentieri, vorremmo modificare il comportamento degli oggetti, ma scavare nell'update
, trovare le classi e le funzioni che cerchiamo, non è un procedimento immediato e non vorremmo ricompilare ogni volta che ciò accade. La soluzione migliore, secondo me, consiste nell'allegare uno script, eseguibile a tempo di esecuzione, ai nostri oggetti. Per farlo, dobbiamo caricare gli script e darli in pasto ad un interprete Python.
Possiamo creare un interprete Python semplicemente istanziando la classe pyspot::Interpreter
, che di default aggiunge <cwd>/script/
al proprio path:
#include <pyspot/Interpreter.h>
namespace pst = pyspot;
pst::Interpreter interpreter;
Ora, consideriamo un semplice script hello.py
, contenente due metodi:
#!/usr/bin/python
def say_hello():
return “Hello!”
def say_hello_to( name ):
return “Hello %s!” % name
Possiamo caricarlo come modulo Python istanziando un pyspot::Module
, al ché possiamo facilmente invocarne i metodi attraverso Module::Invoke
.
pst::Module hello { "hello" };
pst::Object result { hello.Invoke( "say_hello" ) };
Fico, no?
Ma come facciamo a passare degli argomenti al metodo say_hello_to
?
L'interprete Python si aspetta un parametro args
come tupla python, quindi, in questo caso, dobbiamo costruire una tupla contenente una singola stringa Python. Con PySpot ciò è estremamente semplice:
pst::String name { "Nanni" };
pst::Tuple args { name };
pst::Object result { hello.Invoke( "say_hello_to", args ) };
Come avrete già notato, il tipo di ritorno di un metodo Python è pyspot::Object
(aka Python Object Handle). È responsabilità del programmatore sapere il tipo esatto restituito da uno specifico metodo Python, ed interpretare il risultato nel modo giusto.
Esempio
Quindi, se volessimo fare uno script per delle gocce di pioggia? Ci piacerebbe indubbiamente scrivere roba del genere in un file drop.py
:
#!/usr/bin/python
import sunspot
CLOUD_Y = 128.0
GROUND_Y = 0.0
DROP_SPEED = 42.0
def init( drop ):
drop.transform.position.y = CLOUD_Y
def update( drop, delta ):
drop.transform.position.y -= DROP_SPEED * delta
if drop.transform.position.y <= GROUND_Y:
drop.enabled = false
Nello script, drop
e delta
rappresentano oggetti passati dal nostro engine, ma - attenzione - Python accetta solo oggetti Python come argomenti per i suoi metodi, quindi dobbiamo creare dei moduli Python custom, e implementarli in C/C++. Ci servono moduli per le componenti, entità, ed ogni parte dell'engine che vorremmo esporre come API. Focca la bindella, è un sacco di codice da scrivere.
Scioltissimi! PySpot ha degli script fighissimi in grado di generare queste classi per voi da semplici descrizioni json.
Già non state più nella pelle? Nel prossimo post mettiamo le mani sull'Extension Generator! 😮