mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
...
This commit is contained in:
parent
9ed48bd6a9
commit
ebffd6fc5e
189
docs/unity/bindings.md
Normal file
189
docs/unity/bindings.md
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
---
|
||||||
|
label: Bindings
|
||||||
|
icon: dot
|
||||||
|
order: 10
|
||||||
|
---
|
||||||
|
|
||||||
|
Bindings are methods and variables that are defined in C# and can be accessed from Python.
|
||||||
|
We provide two types of bindings: static bindings and dynamic bindings.
|
||||||
|
|
||||||
|
## Static Bindings
|
||||||
|
|
||||||
|
Static bindings wrap a C# class or struct and expose its methods and variables to Python.
|
||||||
|
This is the most common way to define bindings.
|
||||||
|
Static bindings are initialized at compile time.
|
||||||
|
|
||||||
|
### Manual Static Bindings
|
||||||
|
|
||||||
|
Manual static bindings directly create a Python equivalent of `def f(a, b, *args)` in C#.
|
||||||
|
To use it, you need to create a class that inherits from `PyTypeObject`.
|
||||||
|
And implement some abstract methods to specify the name and type of the Python type.
|
||||||
|
For example, to make `UnityEngine.Vector2` available in Python, you can write a `PyVector2Type`
|
||||||
|
class like the following.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PyVector2Type: PyTypeObject{
|
||||||
|
// The name of the type in Python
|
||||||
|
public override string name => "Vector2";
|
||||||
|
|
||||||
|
// The corresponding C# type
|
||||||
|
public override System.Type type => typeof(Vector2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, you need to define each method and variable to be exposed to Python,
|
||||||
|
by using `[PythonBinding]` attribute.
|
||||||
|
|
||||||
|
!!!
|
||||||
|
We assume that you have necessary knowledge about
|
||||||
|
[Python's data model](https://docs.python.org/3/reference/datamodel.html).
|
||||||
|
Such as magic methods, `__new__`, `__init__`, `__add__` and so on.
|
||||||
|
Otherwise, you may have trouble understanding the following code.
|
||||||
|
!!!
|
||||||
|
|
||||||
|
Let's define a magic method `__add__`, it is used to implement the `+` operator in Python.
|
||||||
|
With `__add__`, `Vector2` object in Python can be added with another `Vector2` object.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PyVector2Type: PyTypeObject{
|
||||||
|
public override string name => "Vector2";
|
||||||
|
public override System.Type type => typeof(Vector2);
|
||||||
|
|
||||||
|
[PythonBinding]
|
||||||
|
public static object __add__(VM vm, Vector2 self, object other){
|
||||||
|
// If the other object is not a Vector2, return NotImplemented
|
||||||
|
if(!(other is Vector2)) return VM.NotImplemented;
|
||||||
|
// Otherwise, return the result of addition
|
||||||
|
return self + (Vector2)other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is easy to understand, right?
|
||||||
|
Let's see another example, `__mul__`, it is used to implement the `*` operator in Python.
|
||||||
|
`Vector2` object in C# can be multiplied with a `float` object in Python.
|
||||||
|
The following code shows this usage.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Vector2 a = new Vector2(1, 2);
|
||||||
|
Vector2 b = a * 2.0f;
|
||||||
|
Vector2 c = 2.0f * a;
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, things are slightly different from `__add__`.
|
||||||
|
Because the `float` operand can be on the left or right side of the `*` operator.
|
||||||
|
In this case, you need to define `__mul__` and `__rmul__` at the same time.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PyVector2Type: PyTypeObject{
|
||||||
|
public override string name => "Vector2";
|
||||||
|
public override System.Type type => typeof(Vector2);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
[PythonBinding]
|
||||||
|
public static object __mul__(VM vm, Vector2 self, object other){
|
||||||
|
if(!(other is float)) return VM.NotImplemented;
|
||||||
|
return self * (float)other;
|
||||||
|
}
|
||||||
|
|
||||||
|
[PythonBinding]
|
||||||
|
public static object __rmul__(VM vm, Vector2 self, object other){
|
||||||
|
if(!(other is float)) return VM.NotImplemented;
|
||||||
|
return self * (float)other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Finally, let's implement the constructor of `Vector2`.
|
||||||
|
`__new__` magic method must be defined.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PyVector2Type: PyTypeObject{
|
||||||
|
public override string name => "Vector2";
|
||||||
|
public override System.Type type => typeof(Vector2);
|
||||||
|
|
||||||
|
[PythonBinding]
|
||||||
|
public static object __new__(VM vm, PyTypeObject cls, params object[] args){
|
||||||
|
if(args.Length == 0) return new Vector2();
|
||||||
|
if(args.Length == 2){
|
||||||
|
float x = vm.PyCast<float>(args[0]);
|
||||||
|
float y = vm.PyCast<float>(args[1]);
|
||||||
|
return new Vector2(x, y);
|
||||||
|
}
|
||||||
|
vm.TypeError("Vector2.__new__ takes 0 or 2 arguments");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we use `params object[] args` to tell the bindings that the constructor can take any number of arguments.
|
||||||
|
It is equivalent to `def __new__(cls, *args)` in Python.
|
||||||
|
Note that Python does not support method overloading.
|
||||||
|
So we manually check the number of arguments and their types to determine which constructor to call.
|
||||||
|
|
||||||
|
For fields, we can form a Python property by defining a getter and a setter.
|
||||||
|
By using `[PythonBinding(BindingType.Getter)]` and `[PythonBinding(BindingType.Setter)]` attributes.
|
||||||
|
|
||||||
|
!!!
|
||||||
|
However, this has certain limitations for value types. Because `Vector2` is a struct, it is passed by value.
|
||||||
|
So our setter will not be able to modify the original `Vector2` object.
|
||||||
|
!!!
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PyVector2Type: PyTypeObject{
|
||||||
|
public override string name => "Vector2";
|
||||||
|
public override System.Type type => typeof(Vector2);
|
||||||
|
|
||||||
|
[PythonBinding(BindingType.Getter)]
|
||||||
|
public static object x(VM vm, Vector2 self) => self.x;
|
||||||
|
|
||||||
|
[PythonBinding(BindingType.Setter)]
|
||||||
|
public static void x(VM vm, Vector2 self, object value) => self.x = vm.PyCast<float>(value);
|
||||||
|
|
||||||
|
[PythonBinding(BindingType.Getter)]
|
||||||
|
public static object y(VM vm, Vector2 self) => self.y;
|
||||||
|
|
||||||
|
[PythonBinding(BindingType.Setter)]
|
||||||
|
public static void y(VM vm, Vector2 self, object value) => self.y = vm.PyCast<float>(value);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have done all the above, you must register the type to the VM.
|
||||||
|
And set the returned object into a module.
|
||||||
|
Here we set it into `builtins` module, so that it can be accessed from anywhere.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var type = vm.RegisterType(new PyVector2Type());
|
||||||
|
vm.builtins.attr["Vector2"] = type;
|
||||||
|
```
|
||||||
|
|
||||||
|
To summarize, manual static bindings provide detailed control for exposing a C# class to Python.
|
||||||
|
You decide which methods and variables to expose, and how to expose them.
|
||||||
|
This is our recommended way to define bindings. Also it is the most performant way.
|
||||||
|
|
||||||
|
### Automatic Static Bindings
|
||||||
|
|
||||||
|
Automatic static bindings use C# reflection to automatically generate bindings for a C# class.
|
||||||
|
It is convenient for testing and prototyping, but it is slow and unsafe since the user can access any member of the class.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var type = vm.RegisterAutoType<Vector2>();
|
||||||
|
vm.builtins.attr["Vector2"] = type;
|
||||||
|
```
|
||||||
|
|
||||||
|
That's all you need to do. The `RegisterAutoType<T>` method will automatically generate bindings for `Vector2`.
|
||||||
|
|
||||||
|
|
||||||
|
## Dynamic Bindings
|
||||||
|
|
||||||
|
Dynamic bindings allows you to add a single C# lambda function to an object at runtime.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
delegate object NativeFuncC(VM vm, object[] args);
|
||||||
|
```
|
||||||
|
|
||||||
|
+ `CSharpLambda BindFunc(PyObject obj, string name, int argc, NativeFuncC f)`
|
||||||
|
|
||||||
|
You can use `BindFunc` to achieve this.
|
3
docs/unity/index.yml
Normal file
3
docs/unity/index.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
label: Unity Plugin
|
||||||
|
icon: code
|
||||||
|
order: 0
|
19
docs/unity/introduction.md
Normal file
19
docs/unity/introduction.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
label: Introduction
|
||||||
|
icon: dot
|
||||||
|
order: 30
|
||||||
|
---
|
||||||
|
|
||||||
|
# Welcome to PocketPyUnity
|
||||||
|
|
||||||
|
PocketPyUnity is a C# plugin that allows you to do Python scripting in [Unity](https://unity.com/).
|
||||||
|
It provides a sandboxed Python environment, adding dynamic capabilities to your game,
|
||||||
|
which can be used for dynamic game logic, modding, hot fixing, and more.
|
||||||
|
|
||||||
|
The virtual machine is written in **pure C#**,
|
||||||
|
which means you can fully control the internal state of the Python interpreter.
|
||||||
|
|
||||||
|
!!!
|
||||||
|
PocketPyUnity is designed for game scripting, not for scientific computing.
|
||||||
|
You cannot use it to run NumPy, OpenCV, or any other CPython extension modules.
|
||||||
|
!!!
|
@ -1,25 +1,9 @@
|
|||||||
---
|
---
|
||||||
label: Unity Plugin
|
label: Virtual Machine
|
||||||
icon: code
|
icon: dot
|
||||||
order: 0
|
order: 20
|
||||||
---
|
---
|
||||||
|
|
||||||
# Welcome to PocketPyUnity
|
|
||||||
|
|
||||||
PocketPyUnity is a C# plugin that allows you to do Python scripting in [Unity](https://unity.com/).
|
|
||||||
It provides a sandboxed Python environment, adding dynamic capabilities to your game,
|
|
||||||
which can be used for dynamic game logic, modding, hot fixing, and more.
|
|
||||||
|
|
||||||
The virtual machine is written in **pure C#**,
|
|
||||||
which means you can fully control the internal state of the Python interpreter.
|
|
||||||
|
|
||||||
!!!
|
|
||||||
PocketPyUnity is designed for game scripting, not for scientific computing.
|
|
||||||
You cannot use it to run NumPy, OpenCV, or any other CPython extension modules.
|
|
||||||
!!!
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
The `VM` class provides a sandboxed Python environment and a set of APIs for interacting with it.
|
The `VM` class provides a sandboxed Python environment and a set of APIs for interacting with it.
|
||||||
|
|
||||||
### Construction
|
### Construction
|
||||||
@ -145,11 +129,3 @@ The `VM` class provides a sandboxed Python environment and a set of APIs for int
|
|||||||
|
|
||||||
A flag that controls whether to print debug messages to Unity console.
|
A flag that controls whether to print debug messages to Unity console.
|
||||||
You can set it to `true` to enable debug messages, or `false` to disable them.
|
You can set it to `true` to enable debug messages, or `false` to disable them.
|
||||||
|
|
||||||
## Bindings
|
|
||||||
|
|
||||||
1
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
1
|
|
Loading…
x
Reference in New Issue
Block a user