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
Infinite Progression
The g-mod of idle games!
Status | Released |
Author | Prestosilver |
Genre | Simulation |
Tags | 2D, Endless, GitHub, Idle, Incremental, Minimalist, Moddable, Singleplayer |
Languages | English |
Accessibility | High-contrast |
Leave a comment
Log in with itch.io to leave a comment.