blueloveTH 2 年 前
コミット
ebffd6fc5e
4 ファイル変更215 行追加28 行削除
  1. 189 0
      docs/unity/bindings.md
  2. 3 0
      docs/unity/index.yml
  3. 19 0
      docs/unity/introduction.md
  4. 4 28
      docs/unity/vm.md

+ 189 - 0
docs/unity/bindings.md

@@ -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 - 0
docs/unity/index.yml

@@ -0,0 +1,3 @@
+label: Unity Plugin
+icon: code
+order: 0

+ 19 - 0
docs/unity/introduction.md

@@ -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.
+!!!

+ 4 - 28
docs/unity/index.md → docs/unity/vm.md

@@ -1,25 +1,9 @@
 ---
 ---
-label: Unity Plugin
-icon: code
-order: 0
+label: Virtual Machine
+icon: dot
+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
@@ -144,12 +128,4 @@ The `VM` class provides a sandboxed Python environment and a set of APIs for int
 + `bool debug = false`
 + `bool debug = false`
 
 
     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.
-
-## Bindings
-
-1
-
-## Examples
-
-1
+    You can set it to `true` to enable debug messages, or `false` to disable them.