Using Iron Python, json, and Unity3d to make a modular game!


Why would I even find this important?

So here I was programming a game years ago, and I realized how flawed the concept of a fully random idle game was, it would never be feasible for one person, so I decided I would add modding support, and release the source.

So how did I even do it

As I said, the source is going to be open so ill just put in a few snippets of code, and other changes I made to do this.

Adding a Mod Manager

at first, I was going to use Mod Tool to create a modding system, but I ended up ruling this out do to the fact I couldn’t figure out how to create a mod for my own game.

A ton of research later I found Iron Python, this was exactly what I needed, a simpler language that was, so I thought, easier to implement.

Iron Python

So once I found Iron python I went straight to work, finding This Repo and adding it to my unity assets. Then did some research and got to the point where I could load a python file like this.

// create a scope and enging
engine = Python.CreateEngine();
scope = engine.CreateScope();
// load the engine, and import some libraries automatically
engine.CreateScriptSourceFromString("import clr\nclr.AddReference(\'IP.Lib\')\nclr.AddReference('IP.Game')").Execute();
// get the source of the python file
source = engine.CreateScriptSourceFromFile(mainFile);

// execute the script
// NOTE: this does nothing, except for define functions in the python scope
source.Execute(scope);

But where did I go from here, well I made a json class to define mod properties, and and files. this was to make it easier to figure out what python files I needed to source. And implemented a way of reading this and initializing prefabs in a vertical layout group.

public class JSONModData
{
    public string name;
    public string description;
    public string main_file;
    public string ui_file;
    public List<string> requires;
    public int chance;
}

This is all good, but how do i even call functions defined in the python file. after some googling, and getting annoyed because i couldn’t import assemblies I filially realized you need to import classes defined in the c# assembly in the python file. After that annoyance I wrote a quick function to run a function in the python file.

public dynamic Tick(dynamic data)
{
    data = scope.GetVariable<Func<object, object>>("Tick")(data);
    Debug.Log(engine.Operations.GetMember(data, "result"));
    return data;
}

public dynamic BulkTick(dynamic data, BigNumber ticks)
{
    data = scope.GetVariable<Func<object, object, object>>("bulkTick")(data, ticks);
    return data;
}

note that i create data object when a module in my game is created, which can occur multiple times. Now thats enough of the csharp code, how do i create a ui using this method. Answer: JSON, i created a few classes to define some basic ui elements: button; sliders; and text. Heres an example of a ui json file this one is the discount module.

{
  "buttons": [
    {
      "x": 214,
      "y": -3,
      "w": 50,
      "h": 25,
      "onClick": "upgradeClick",
      "enable": "upgradeAvail"
    }
  ],
  "sliders": [],
  "text": [
    {
      "x": 164,
      "y": -3,
      "w": 50,
      "h": 25,
      "dynamic_text": "discountText"
    },
    {
      "x": 5,
      "y": -3,
      "w": 159,
      "h": 25,
      "dynamic_text": "nameText"
    }
  ]
}

And respectively heres the code that loads the ui and returns the root game object:

    public GameObject ConstructUI(ModUI ui)
    {
        // the sliders in the ui
        sliders = new Dictionary<Slider, string>();
        // the text in the ui that can change
        dyntext = new Dictionary<Text, string>();
        // the buttons in the ui
        btns = new Dictionary<Button, string>();
        GameObject result = Instantiate(panelPrefab, transform);

        foreach (ModUIButton btn in ui.buttons)
        {
            GameObject button = Instantiate(ButtonPrefab, result.transform);
            button.GetComponent<RectTransform>().anchoredPosition = new Vector2(btn.x, btn.y);
            button.GetComponent<RectTransform>().sizeDelta = new Vector2(btn.w, btn.h);
            button.GetComponent<Button>().onClick.AddListener(() => mod.onClick(data, btn.onClick));
            btns.Add(button.GetComponent<Button>(), btn.enable);
        }

        foreach (ModUISlider sl in ui.sliders)
        {
            GameObject slider = Instantiate(sliderPrefab, result.transform);
            slider.GetComponent<RectTransform>().anchoredPosition = new Vector2(sl.x, sl.y);
            slider.GetComponent<RectTransform>().sizeDelta = new Vector2(sl.w, sl.h);
            sliders.Add(slider.GetComponent<Slider>(), sl.variable);
        }

        foreach (ModUIText txt in ui.text)
        {
            GameObject slider = Instantiate(textPrefab, result.transform);
            slider.GetComponent<RectTransform>().anchoredPosition = new Vector2(txt.x, txt.y);
            slider.GetComponent<RectTransform>().sizeDelta = new Vector2(txt.w, txt.h);
            if (txt.dynamic_text != null)
                dyntext.Add(slider.GetComponent<Text>(), txt.dynamic_text);
            else
                slider.GetComponent<Text>().text = txt.static_text;
        }

        return result;
    }

And finally the code that updates the ui:

    public override void UpdateDisplay()
    {
        foreach (Slider slider in sliders.Keys)
        {
            slider.value = mod.GetVar(data, sliders[slider]).mantissa;
        }

        foreach (Text textf in dyntext.Keys)
        {
            textf.text = mod.GetVar(data, dyntext[textf]);
        }

        foreach (Button btn in btns.Keys)
        {
            btn.interactable = mod.GetFunc(data, btns[btn]);
        }
    }

What Next

I plan on adding UI Popups, and a few other things. But the main thing i plan on doing is testing to see if this system is even as intuitive as i thought, if you would like to help please do. There is a source repo for an example mod Here, if you have any suggestions with it please comment them. The game will be available soon.

Get Infinite Progression

Download NowName your own price

Leave a comment

Log in with itch.io to leave a comment.