I love games that are moddable. I don’t usually mod games until after I’ve experienced the vanilla version of the game the way the devs intended. If I do use or create a mod, it’s usually to balance the game a certain way, or to improve on or add features to enhance my enjoyability of the game.
I used to wonder why many game developers didn’t make their games moddable, or made very limited parts of the game moddable. Why wouldn’t they make customization available to their customers? Wouldn’t it help with their sales (if they didn’t consider themselves competing with mods)? How hard could it be to make a game more moddable?
Turns out, I was very naive. It’s hard and very time consuming. I apologize to all the devs I’ve silently complained about. It’s not impossible of course, but it requires developing your entire game around the concept. I’ve probably spent more time trying to make my game moddable than developing the gameplay. It literally would be SO MUCH EASIER to not make a moddable game, especially in the Unity engine which is designed to make heavy use of the inspector. But in designing around the idea of mods, I’ve learned quite a lot about development and the Unity engine.
There are many companies who’ve created moddable games successfully with Unity, like Kerbal Space Program and Cities: Skylines. From what I understand, they created proprietary tools or scripts for mod creation. As a lone budding indie developer, I currently don’t have the expertise and technical know-how to create modding tools. I just want to create a moddable game without too much fuss. Well, to do so does require a lot of fuss!
What follows is my journey of trying different avenues of making a moddable Unity game, and the solutions I’m sticking with after much trial and error. This is a general overview, and won’t go much into code examples, as there are many tutorials on the internet for that.
There are many different aspects of a game that can be made moddable: data, models, textures, animations, audio, scripting, etc. Let’s start with data.
To make the data part of a game moddable, you have to expose the data to the users. If they make changes to the data, you have to be able to load those changes into the game at runtime. There are many ways of doing this. You could use .ini or .txt files, and use some kind of parser to read in the data at runtime. You can also use formats like XML or JSON to do the same. There are still yet other ways, like using a database such as SQLite or even Google Sheets, and using a 3rd party or proprietary system to convert the database to usable data for your game. Essentially, whatever you choose should be easily creatable and editable.
After much Unity Asset store browsing and shopping, as well as testing, I decided to settle on using JSON files. Is it the best format? That depends on your needs and workflow. You may find one of the other solutions above (or one not mentioned) better for you. After reading countless XML vs JSON articles on the web, I settled on JSON for 4 reasons.
- It has a concise, simple, human readable format that should prove easy for users to modify, especially if it’s output in pretty format.
- In certain situations, it seems like json can be faster to read and parse than XML.
- It can be easily editable using free text editors like Notepad++, Brackets, Atom, and the new Visual Studio Code. Paid editors with free or limited versions: SublimeText, Codeanywhere
- It seems to be growing in popularity, especially in web development.
Ok, so how do you actually create and read JSON files? Update Feb. 19, 2016: Unity now has its own JSON framework which is fast but limited to the types that Unity can serialize. There are many other free JSON frameworks, like Json.NET, LitJSON, etc. They may not be designed specifically for use with Unity, and depending on the platform you’re developing for, may not work correctly. If you’re not happy with Unity’s built-in solution, there are a ton of JSON solutions in the Unity Asset Store, for example Json.NET (paid) and FullSerializer (free). Just do a search in the store for “json” or even “xml” if that’s what you’ve decided to use. You can also search for “ini” or “SQL” as well.
If you create a class in Unity with some data in it, you can use one of these JSON frameworks to serialize the class object to a string. Then you use methods from System.IO like File.WriteAllText() to write the data to a file. To read it back in, you use something like File.ReadAllText(), and deserialize the result back to an object of the original class. For specifics, see the documentation of the framework you’re using.
Ok great, we can create files and load them back in to the system. But other than explicitly initializing the class members in code, how do we create and edit the data in a nice, easy way, to then be serialized and exported to a file? There are solutions for that on the Asset Store as well, like Game Data Editor (there is a free version as well), which handles creating the data in a Unity editor window (and handles serialization/deserialization for you), or G2U which lets you use Google Sheets to create and edit your data, and can export it into JSON format if you’d like, which you would then read into your game.
I decided to go the way of using a Unity-provided solution: ScriptableObjects. One use of ScriptableObjects is as an in-editor database solution whose data can be shared at runtime. But I’m using it slightly differently in that I’m serializing the data from the ScriptableObject to a file (in the StreamingAssets folder) in the editor, and reading data back into a class from a file at runtime. There are many learning resources on the Unity website for how to use ScriptableObjects, so if you’re not familiar with them, please see those tutorials. I’m using the handy asset Odin from the AssetStore to quickly create inspector UIs for the ScriptableObject databases using simple attributes, for example buttons in the inspector to serialize the data to a file.
Using this system of entering and editing data in the inspector of a ScriptableObject, pressing a button to serialize the data to a file, and implementing some runtime GameObject managers to deserialize the data from files back into the ScriptableObjects was working perfectly. I have JSON files for configuration data, and all kinds of unit and weapon data. Ah, but what if a user wants to have multiple mods? If different mods edit the same JSON files, won’t they overwrite each other? Ok, now we need to create some modding directories.
Using two different Unity provided directories, we can handle this easily. The solution I came up with is to provide all my JSON files that come with the game in Unity’s StreamingAssets folder. Users can mod those files, but then we would lose the original data. So using Unity’s PersistentData folder (which varies on different platforms), we can allow mod folders which parallel the structure of the StreamingAssets directory. The game checks first to see which mod folder is the one we want to use, then checks if a specific file exists in the mod folder. If not, we load the original one from the StreamingAssets directory. Nice.
Originally, I wanted to create mod folders within the game directory, but that doesn’t work well on different platforms. Some directories which the game may be installed to may not be writable at runtime, and thus we have to use Application.persistentDataPath.
Models and Animations
Ok, now for models (meshes) and animations. Fortunately with the latest versions of Unity, we can use AssetBundles to load content very fast. Users can create their own AssetBundles for inclusion into a game, provided they are set up in a specific way for a particular game. This is the route I’m taking with meshes, but you’ll have to document everything for your particlar setup. You can use Unity’s AssetBundle Manager to quickly organize your bundles.
If you don’t want to create AssetBundles, other options include using runtime model importers available in the Asset Store, like .fbx and .obj file importers. The problem with .obj files are that they can’t include animations, so if you’re not including animations with your model, you’re ok. Do a search on the Asset Store for “fbx” or “obj” and “import” to see various options.
Since I’m not using characters in my current game, I decided to develop animations within Unity, as opposed to creating them in a separate modeling program like Maya, Max, Modo, or Blender. The solution I came up with is to create animations using the Mecanim system and Animations window, and include those animations as an AssetBundle. This way, users can create their own animations as AssetBundles, and provided they are set up in the same way, I can load them into the game at runtime. What will be required, though, is good documentation on how to do it once the game is released.
Now onto textures. Well, this is a little tricky and requires trade-offs. From what I can tell, there are two main ways of doing this. AssetBundles, and loading images as files. Using Unity’s WWW.LoadImageIntoTexture() or Texture2D.LoadImage() methods, we can load an image (.jpeg or .png) from the file system. Using workarounds to create temporary textures which are replaced by these methods, we can specify the attributes and compression of the textures. The problem with this is that loading textures, especially larger sizes like 4028×4028, results in VERY LONG load times. I noticed that there is a much larger use of graphics memory with loading textures this way as well. Some special care is also needed for dealing with normal maps when loading images from files. Unity recently added asynchronous texture importing, so at least that is available if you need it.
The other method is using AssetBundles, which allows you to specify the import settings and compression in the Inspector. Loading these AssetBundles at runtime is extremely fast, as the textures are already processed in a way that Unity can use. But users can’t simply open AssetBundles and edit the contents inside. So, to sum up:
Loading .jpeg or .png files from the file system:
- Allows users to edit or replace textures easily
- Results in very long load times for larger textures unless done asynchronously, and may use more graphics memory,
- Requires workarounds for normal maps
Loading AssetBundles from the file system:
- Easily set texture import and compression settings in the inspector by the developer and user
- Very fast loading times
- Very difficult for users to modify provided textures, unless you use 3rd party tools to extract AssetBundle contents (see below)
- Somewhat difficult for users to create their own texture AssetBundles for use in-game.
At the time this article is written, there are a couple of 3rd party solutions to extract the contents of AssetBundles. One is called “disunity” on GitHub, and others can be found searching “assetbundle extractor” on Google. I haven’t tried any of them yet, but I’m hoping by the time my game is released, these tools will be more robust, or Unity will provide its own solution (not likely, as other game developers may not want their proprietary assets being extracted from AssetBundles).
There is another solution to the difficulty of extracting assets from AssetBundles. Just make available or distribute those assets separately, meaning, if I have a texture packed into an AssetBundle, I’ll just make the same texture available outiside of the AssetBundle. It won’t ever be used by the game, and will increase the distribution size of the game, but at least the user will have access to it.
I chose the AssetBundle route, because the long load times otherwise was too unbearable during testing, and I believe that any additional work should be put on the developer and modders, rather than making players wait for load times.
Update Feb. 19, 2016: Previously I had not worked on the audio side yet. I have just implemented runtime audio clip loading, and again there are a couple of options. We can use WWW.GetAudioClip to load clips in their raw or compressed formats like .wav, .ogg, or .mp3, or again use AssetBundles. I went the AssetBundle route again, so I and the users can set compression settings appropriately for the clips. I’ll again have to provide documentation for this.
This is a whole other ballgame from what I can tell. There are some games which allow this in the Unity engine. There seem to be solutions revolving around using C# scripts or Lua to accomplish this. I currently am not planning to include this, as it will take a lot of time for me to delve into the subject, find frameworks to allow it, and implement it fully, and I don’t even know if anyone would even want to do script mods for my game. I will probably revisit this idea in the future if it makes sense to do so, especially after I decide how my mission scripting will be done.
There are many different ways to include modding support. What I described above is my current solution (keep in mind this is from an inexperienced dev somewhat new to the whole thing), but this could change over time. One thing for sure is that if going the AssetBundle route, I’ll have to provide detailed documentation for users on how to create AssetBundles to be used correctly with my game.
As there are so many different solutions, each comes with advantages and disadvantages for the developer and user. It’s a balancing act in terms of time, usability, and productiveness. I just wanted to say “THANK YOU” to the developers of all the games I’ve ever played with modding support. After stepping into your shoes, I truly appreciate the time and effort required.