Update Oct. 27, 2019: The Unity landscape has changed since the initial post. I’ve revised a lot of the original post to reflect this.
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 extensive modding tools.
What follows is my current understanding 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.
I’ll first start with a few frameworks for Unity that could help with the whole modding process. Then I’ll describe the individual areas that need to be made moddable: data, models, textures, animations, audio, scripting, etc. I am NOT affiliated with any site or asset I link to below.
There are a few assets in Unity’s Asset Store that seem to handle a lot of the heavy lifting of making a game moddable for you:
I haven’t used either of these as I’m trying to rely on 3rd party assets less and less, but the few reviews they have seem to indicate they’re useful. They both claim to handle adding C# scripts as mods, which is something Unity does not natively do.
In addition, there is a website and service called mod.io, made by the team behind IndieDB and ModDB. While mod.io requires you to make your game moddable first, it allows you a place to host your mods and make them easily viewable/installable by users. I have not used this either, but may consider it when I’m at that stage of development.
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. There are many assets on the Unity Asset Store which handle the loading of each of the above-mentioned file formats.
After much Unity Asset store browsing and shopping, as well as testing, I personally settled on using JSON files. After reading countless XML vs JSON articles on the web, I settled on JSON for 4 reasons.
- Unity has a built-in JSON serializer – it does everything I need save for one thing: it does not serialize enums to strings. I’ve requested this a few times from Unity’s developers, but I don’t think it’s a priority for them. You can try handling this yourself during serialization or just document the integers which the enums are converted to, for modders.
- 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 Visual Studio Code, Notepad++, Brackets, Atom.
- It seems to be fairly popular, especially in web development.
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, which is 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. There are other free JSON frameworks, like Json.NET, LitJSON, etc, but 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 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 which handles creating the data in a Unity editor window (and handles serialization/deserialization for you), or even in Google Sheets 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 which uses the inspector, and 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 particular setup. You can use Unity’s AssetBundle Manager to quickly organize your bundles.
There is a new way of loading content in Unity called, Addressables. I want to note here that as of now, modding is very complicated to set up with Addressables. As you can see from this and this and this thread in Unity’s forum, using it in a way to support mods is not documented yet. I personally was not able to get mods to work via replacement without having to require modders to create additional content catalogs. I also found that Addressables was not offering me the customizability that I wanted without having to modify parts of its included code, so I’m sticking with using the AssetBundle API directly (Addressables currently uses AssetBundles underneath).
If you don’t want to create AssetBundles, other options include using runtime model importers available in the Asset Store, like .fbx, .obj and .gltf 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” or “gltf” 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 ImageConversion.LoadImage() method (or UnityWebRequest.GetTexture() if on the web), 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 4096×4096, 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.
The other method is using AssetBundles again, 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 or modder
- 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 modders to build 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, which can be found by 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 could 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.
I was able to implement runtime audio clip loading, and again there are a couple of options. We can use UnityWebRequestMultimedia.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 any modders can set compression/streaming settings appropriately for the clips, depending on their size. I’ll again have to provide documentation for this.
As mentioned in the “Frameworks” section above, there are some assets in the Asset Store which allow loading C# scripts at runtime. There are also assets which allow Lua scripts to be loaded at runtime, if you’re familiar with that language. I’m not sure if I personally am going to allow script modding, as 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 are my current solutions, which could change at any 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.