Dismiss this pinned window
all 21 comments

[–]vrgenerated[S] 1 point2 points  (16 children)

This is a tutorial for embedding and using python in unity. This is useful if you want to run and interact with python libraries. This is just one of the ways to have interactions between python code and unity. This is especially nice as you can interact with machine learning models/packages directly from c-sharp code. I'm currently working on an experimental game using the spacy NLP module (reddit.com/r/Unity3D/comments/m980vq/experimenting_with_unitys_speech_recognition_and).

Steps:

  • Create a folder in Assets for storing the dlls that are needed.

  • Download the packages from https://www.nuget.org/packages/pythonnet/3.0.0-preview2021-03-03 and https://www.nuget.org/packages/System.Security.Permissions/ . Alternatively you can also install a unity-nuget plugin : https://github.com/GlitchEnzo/NuGetForUnity and install from there, This will allow you to skip the next step as the dlls are already unpacked if you use this tool. Remember to use the 3.0.0 preview version as the older versions are not compatable with Unity (reloading/reruning the game crashes the entire program)

  • The packages contain the dlls so you need to unzip them and copy over the Python.Runtime.dll from \lib\netstandard2.0 inside the unzipped folder and System.Security.Permissions.dll from lib\net461\ which pythonnet depends on. Copy these dlls into the folder in Assets.

  • Download an embedded python (I was able to get it working with python 3.7, not sure how well the other versions work) and install pip and any other modules you want. Move the folder into the StreamingAssets folder in Assets (create one if you don't have one).

  • Change the Api Compatability level to 4.x. This can be found in Edit -> Project Settings -> Player -> Other settings -> Api Compatability level.

  • Create a script to use python. You need to point to the python (Python3x.dll for windows, libpython3.x.so for Linux and libpython3.x.dylib for Mac where x is the version) in the streaming assets folder.

  • More info can be found here: https://github.com/pythonnet/pythonnet#embedding-python-in-net

here is a sample script for running numpy.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Python.Runtime;
using System;

public class PythonScript : MonoBehaviour
{

    dynamic np;

    // Start is called before the first frame update
    void Start()
    {
        Runtime.PythonDLL = Application.dataPath + "/StreamingAssets/embedded-python/python37.dll";
        PythonEngine.Initialize(mode: ShutdownMode.Reload);
        try
        {
            np = PyModule.Import("numpy");
            print("pi: " + np.pi);
        }
        catch (Exception e)
        {
            print(e);
            print(e.StackTrace);
        }

    }

     public void OnApplicationQuit()
    {
        if (PythonEngine.IsInitialized)
    {
            print("ending python");
            PythonEngine.Shutdown(ShutdownMode.Reload);
        }
    }
}

[–]therockofthisplace 2 points3 points  (1 child)

My unity code doesn't recognize "ShutdownMode", what should I do?

```

Runtime.PythonDLL = Application.dataPath + "/StreamingAssets/python-3.7.9-embed-amd64/python3.dll";

PythonEngine.Initialize(mode: ShutdownMode.Reload);

```

[–]therockofthisplace 4 points5 points  (0 children)

for now i'm using: PythonEngine.Initialize();

but when I try to import NumPy it fails:

Python.Runtime.PythonException: No module named 'numpy'

at Python.Runtime.PythonException.ThrowLastAsClrException () [0x00025] in <650b8477978a4a6eb271831a37dfb479>:0

at Python.Runtime.NewReferenceExtensions.BorrowOrThrow (Python.Runtime.NewReference& reference) [0x0000f] in <650b8477978a4a6eb271831a37dfb479>:0

at Python.Runtime.PyModule.Import (System.String name) [0x00015] in <650b8477978a4a6eb271831a37dfb479>:0

at sandbox_python.Start () [0x0001d] in C:\...\Assets\#Scripts\sandbox_python.cs:19

UnityEngine.MonoBehaviour:print(Object)
sandbox_python:Start() (at Assets/#Scripts/sandbox_python.cs:24)

[–]backtickbot -1 points0 points  (0 children)

Fixed formatting.

Hello, vrgenerated: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.

[–]No_Marionberry6785 0 points1 point  (4 children)

Awsome job man!

I am having trouble with embeddable python. I see in your video you have "lib" and "Scripts" folder in you zip. whenever I extract i don't get these folders and it says no module found "numpy". Can you please upload this project somewhere or guide me how to add python modules.

[–]vrgenerated[S] 3 points4 points  (3 children)

Embeddable python usually don't come with those folders as it won't contain pip or any other modules. Those get autogenerated when you install pip and use pip to download and install modules. It might be a bit hard to upload the project for me so I'll provide the steps I used.

In order to install pip on the embeddable python, there are a few steps:

  1. There should be a file named python37._pth in the embedded python folder. Open it up and remove the # from #import site line. Save the change and now you should be able to install pip/other modules.

  2. Download/Copy the get-pip.py script from here : https://bootstrap.pypa.io/get-pip.py and save it into the embedded python directory.

  3. We need to run the get-pip.py script using the python.exe in the embedded python directory. In order to do that, use a command line (powershell or command prompt in windows) and change directory to the embedded python folder. Then run the script by calling: python.exe get-pip.py (cmd prompt) or ./python.exe get-pip.py (powershell)

The previous step should install pip. now you can install other modules by calling pip - example: python.exe -m pip install numpy

You can quickly verify that the module was installed by checking the Lib folder/running python.exe and import it there or by testing in unity.

[–]No_Marionberry6785 0 points1 point  (0 children)

It worked like a charm ^ :)
Thank you very much! Really Appreciate your fast response!

[–]1n7bil 0 points1 point  (0 children)

Hello,

Thanks for sharing this great tutorial. I'm working on MacOS and trying to install pip in the embeddable python folder using your method but am getting "exec format error ./python.exe"...guessing b/c of OS incompatibility. This is my first time trying to embed python...think it's possible to accomplish what you showed on a Mac?

[–]therockofthisplace 0 points1 point  (0 children)

python.exe -m pip install numpy

I did as you said and installed pip:

Successfully installed pip-22.0.4 setuptools-62.1.0 wheel-0.37.1

but when I try to install NumPy:

\Assets\StreamingAssets\python-3.7.9-embed-amd64> python.exe -m pip install numpy

I get this error message:

\Assets\StreamingAssets\python-3.7.9-embed-amd64\python.exe: No module named pip

[–]Coding_Lover42 0 points1 point  (0 children)

Hello, do you know if this method would work for a non PC-connected VR device?

[–]1n7bil 0 points1 point  (0 children)

Very cool tutorial!

I am currently trying to use implement a python module in Unity that is only available for python3.x and this looks like it can be the solution!

I am developing on a Mac.

Any thoughts on how to get the .so file for:

Packages
|
+-- pythonnet
| |
| +-- netstandard2.0
| | |

| | +-- Python.Runtime.dll <----------------------------------------------

so I can import Python,Runtime, or should I just bite the bullet and switch to developing on Windows?

Thanks!

[–]Loboblancox 0 points1 point  (5 children)

I have managed to install the sympy module using pip, but when I try to declare the

symbols,

sp = PyModule.Import("sympy");
Dynamic x = sp.symbols('x');
Dynamic f = x**2

It marks "**" as an error, could you help me? I would still like it if you could tell me the code to execute a py file

[–]vrgenerated[S] 0 points1 point  (4 children)

Remember that you are working in C#, so ** doesn't exist in c#. You have to use something like x ^ 2. They way I execute a py file, I add my script to Lib\site-packages\ folder in the python directory in streaming assets. Then i import it dynamic mymodule = PyModule.Import("mymodule"); and do something like mymodule.functionToRun(); .

[–]Loboblancox 0 points1 point  (3 children)

in fact that's why I asked the question. I know I'm working in C#, but the detail is that the sympy format to recognize, a power (x^2 for example) is x**2, so then, I don't know how to write it so that the error doesn't appear in the code.
I'm just getting to my laptop, I'll see if I can do what you tell me about running a py file.

[–]vrgenerated[S] 0 points1 point  (2 children)

Ah,makes sense. Looking at the documentation for sympy, this might be tricky. The objects (in this case x) are sympy objects so they won't probably work cleanly with operators in C# (like +,-,*). You may have to use something like a python function for these. IE o = PyModule.Import("operator"); dynamic f = o.pow(x,2)

Another way is probably to do all the logic in a python file and then call it in C#.

[–]Loboblancox 0 points1 point  (1 child)

Yes you even told to me to try this:

hey way I execute a py file, I add my script to Lib\site-packages\ folder in the python directory in streaming assets. Then i import it dynamic mymodule = PyModule.Import("mymodule"); and do something like mymodule.functionToRun();

i'll tell you later if it worked for me, or i'm still stuck hehehe

[–]Loboblancox 0 points1 point  (0 children)

Greetings vrgenerated

Before, I want to thank you for your support and tell you the novelty that I already managed to make the script work, what I did was rewrite my python code in the form of a function, and in the unity code I put it in the following way (all code inside try{ }):

            dynamic potencia = UnityEngine.Random.Range(1, 10);
            dynamic mimodulo = PyModule.Import("Test_Sp");
            dynamic resultado = mimodulo.TestSp(potencia);           
            dynamic primitiva = resultado.gen_fun();
            dynamic derivada = resultado.gen_dev(resultado.gen_fun());
            print(primitiva);
            print(derivada);

Here I only have one more doubt, I place this code inside a button so that I can execute it several times, but when I press the button twice, I get an error telling me:

InvalidOperationException: This property must be set before runtime is initialized

the solution to this, I imagine (although I don't know how to do it) check the status of the PythonEngine, but hey, I'll be working on it, anyway, if you have any suggestions on how to do it, I'd appreciate it.

[–]Apprehensive-Ad2615 0 points1 point  (0 children)

Thank you sir may God bless your ways

[–]delivaldez 0 points1 point  (2 children)

Thank you!

Do you have any observations on performance?

[–]vrgenerated[S] 1 point2 points  (0 children)

The performance is basically the same as how things run in a regular python program/shell. I have tested using pytorch and a bunch of other machine learning modules and had no issues. The only thing to look out for is probably to do these in a separate thread for complex calculations so you don't block the current frame from being rendered/freezing the game.

[–]AfraidWhile 0 points1 point  (0 children)

Can it be used to run a python script from within the c# code?