Difference between revisions of "How to create mod for unity game"

From Nexus Mods Wiki
Jump to: navigation, search
m
(this text editor sucks so much it keeps resetting my formatting and adding/deleting characters for no reason whenever i save changes)
 
(15 intermediate revisions by 6 users not shown)
Line 2: Line 2:
 
== Introduction ==
 
== Introduction ==
  
 
+
Note, this tutorial is more focused on creating scripts than content, but you can still use it to make a new game object or texture. Unity runs the C# programming language, so creating a mod isn't as hardcore as it seems. You don't need to be a super programmer, just understand the basics of programming. The hardest part will be digging into the game's code to find the necessary functions you want to call/modify.
<syntaxhighlight lang="Python">
 
Check highlight plugin
 
</syntaxhighlight>
 
 
 
To begin with, these mods are more focused on creating scripts than content. But still make a new game object or texture, you can. Unity works on the csharp language, so creating a mod may seem hardcore, but you do not need to be a super programmer, it is enough to know the basic knowledge of programming. It will be even more difficult to dig into the game code in search of the necessary game functions.
 
  
 
== Project creation ==
 
== Project creation ==
  
#Download and install Microsoft Visual Studio Community 2017 with C#  
+
#Download and install '''Microsoft Visual Studio Community 2017''' with '''C#'''
#Open the project creation and select 'Class Library (NET Framework)'. Change name to 'TestMod'. Open the project properties and target platform should be 'Net Framework 3.5' or 'Net Framework 4.0' for the newer Unity version.&nbsp;If&nbsp;game is made for .net&nbsp;3.5, UMM must be installed as Doorstop method.  
+
#Open the project creation and select '''Class Library (NET Framework)'''. Change name to '''TestMod'''. Open the project properties and target platform should be '''Net Framework 3.5''' or '''Net Framework 4.0''' for the newer Unity version.&nbsp;If the game is made for .net&nbsp;3.5, UMM must be installed as '''Doorstop method'''.  
#You need to add references to game files 'Assembly-CSharp.dll', 'Assembly-CSharp-firstpass.dll', 'UnityEngine.dll', 'UnityEngine.UI.dll' which are located in 'Managed' folder and to UMM files 'UnityModManager.dll', '0Harmony.dll' which are located in 'Managed/UnityModManager' folder. If the unity version is 2017 or higher you need to add additional files 'UnityEngine.CoreModule.dll', 'UnityEngine.IMGUIModule.dll'. Now we can use the game and unity mod manager functions. ''Note: [https://www.nexusmods.com/site/mods/21/ Unity Mod Manager] should already be installed.''
+
#Add references to these game file located in the 'Managed' folder. (Note: [https://www.nexusmods.com/site/mods/21/ UnityModManager] should already be installed.)
 
+
#*'''Assembly-CSharp.dll'''
&nbsp;
+
#*'''Assembly-CSharp-firstpass.dll'''  
 +
#*'''UnityEngine.dll'''  
 +
#*'''UnityEngine.UI.dll'''  
 +
#If the unity version is 2017 or higher, also add these additional files.
 +
#*'''UnityEngine.CoreModule.dll'''
 +
#*'''UnityEngine.IMGUIModule.dll''' 
 +
#Add references to these UMM files located in the 'Managed/UnityModManager' folder.
 +
#*'''UnityModManager.dll'''
 +
#*'''0Harmony.dll''' 
 +
#Now we can use the game and unity mod manager functions.  
  
 
== Information file ==
 
== Information file ==
  
Create an information file 'Info.json' so that the mod manager can read it and determine which files need to load when starting the game. The file is written in json and must be placed in the 'Mods'&nbsp;folder and one more folder e.g. '\Steam\steamapps\common\YourGame\Mods\TestMod\Info.json'.
+
Create an information file Info.json so that the mod manager can read it and determine which files need to load when starting the game. The file is written in json and must be placed in the Mods folder and one more folder e.g. \Steam\steamapps\common\YourGame\Mods\TestMod\Info.json.
 
<pre>{
 
<pre>{
 
   "Id": "TestMod",
 
   "Id": "TestMod",
Line 28: Line 32:
 
   "GameVersion": "1.0.0",
 
   "GameVersion": "1.0.0",
 
   "Requirements": ["SomeMod-1.0.0", "AnotherMod"],
 
   "Requirements": ["SomeMod-1.0.0", "AnotherMod"],
 +
  "LoadAfter": ["SomeMod", "AnotherMod"],
 
   "AssemblyName": "TestMod.dll",
 
   "AssemblyName": "TestMod.dll",
 
   "EntryMethod": "TestMod.Main.Load"
 
   "EntryMethod": "TestMod.Main.Load"
Line 39: Line 44:
 
}</pre>
 
}</pre>
  
''Id ''- Unique string. It is desirable to match the folder name. (<u>Required</u>)<br/> ''DisplayName ''- Name or Title. (Optional)<br/> ''Author ''- (Optional)<br/> ''Version ''- Needs&nbsp;for dependent mods and to checking for updates&nbsp;(Format must be 'x.x.x'). (<u>Required</u>)<br/> ''ManagerVersion ''- Minimum required version of the mod manager (Format must be 'x.x.x'). (<u>Recommended</u>)<br/> ''GameVersion'' - Minimum required version of&nbsp;game.&nbsp;Works&nbsp;if&nbsp;game supports this option. (Format must be 'x.x.x') (Optional)<br/> ''Requirements ''- Minimum required version of mods or just other required mod. (Optional)<br/> ''AssemblyName ''- Filename we are creating. Default like 'Id' (e.g. TestMod.dll). (Optional)<br/> ''EntryMethod ''- A function that will be called by the mod manager at load game. (<u>Required</u>)<br/> ''HomePage ''- Web address. (Optional)<br/> ''Repository ''- Web address to check for updates. (Optional)
+
*'''Id '''- Unique string. It is desirable to match the folder name. (<u>Required</u>)  
 +
*'''DisplayName '''- Name or Title. <u>(Optional)</u>  
 +
*'''Author '''- The creator of the mod. <u>(Optional)</u>  
 +
*'''Version'''- Needs&nbsp;for dependent mods and to checking for updates&nbsp;(Format must be 'x.x.x'). (<u>Required</u>)  
 +
*'''ManagerVersion'''- Minimum required version of the mod manager (Format must be 'x.x.x'). (<u>Recommended</u>)  
 +
*'''GameVersion '''- Minimum required version of&nbsp;game.&nbsp;Works&nbsp;if&nbsp;game supports this option. (Format must be 'x.x.x') <u>(Optional)</u>  
 +
*'''Requirements'''- Minimum required version of mods or just other required mod. <u>(Optional)</u>  
 +
*'''LoadAfter&nbsp;'''- List of mods that will be loaded first. <u>(Optional)</u> [0.22.5]
 +
*'''AssemblyName'''- Filename we are creating. Default like 'Id' (e.g. TestMod.dll). <u>(Optional)</u>  
 +
*'''EntryMethod '''- A function that will be called by the mod manager at load game. (<u>Required</u>)  
 +
*'''HomePage '''- Web address. <u>(Optional)</u>  
 +
*'''Repository '''- Web address to check for updates. <u>(Optional)</u>
  
 
== Assembly file ==
 
== Assembly file ==
  
The mod manager supports several variants of the Entry functions. Use only one.&nbsp;Let's call it <u>Load</u>.&nbsp;The name must be the same as in the ''EntryMethod''.
+
The mod manager supports several variants of the Entry functions. Use only one.&nbsp;Let's call it Load.&nbsp;The name must be the same as in the EntryMethod.
 
<pre>using UnityEngine;
 
<pre>using UnityEngine;
 
using UnityModManagerNet;
 
using UnityModManagerNet;
Line 109: Line 125:
 
}</pre>
 
}</pre>
  
== Details of ModEntry ==
+
== Details of ModEntry (Mod Entry) ==
  
''Info ''- Contains all fields from the 'Info.json' file.<br/> ''Path ''- The path to the mod folder e.g. '\Steam\steamapps\common\YourGame\Mods\TestMod\'.<br/> ''Active ''- Active or inactive.<br/> ''Logger ''- Writes logs to the 'Log.txt' file.<br/> ''OnToggle ''- The presence of this function will let the mod manager know that the mod can be safely disabled during the game.<br/> ''OnGUI ''- Called to draw UI.<br/> ''OnSaveGUI ''- Called while saving.<br/> ''OnUpdate ''- Called by MonoBehaviour.Update.<br/> ''OnLateUpdate ''- Called by MonoBehaviour.LateUpdate.<br/> ''OnFixedUpdate ''- Called by MonoBehaviour.FixedUpdate.<br/> ''OnShowGUI&nbsp;''- Called when opening mod GUI.<br/> ''OnHideGUI&nbsp;''- Called when closing mod GUI.
+
*'''Info '''- Contains all fields from the 'Info.json' file.  
 +
*'''Path'''- The path to the mod folder e.g. '\Steam\steamapps\common\YourGame\Mods\TestMod\'.  
 +
*'''Active '''- Active or inactive.  
 +
*'''Logger '''- Writes logs to the 'Log.txt' file.  
 +
*'''OnToggle '''The presence of this function will let the mod manager know that the mod can be safely disabled during the game.  
 +
*'''OnGUI '''- Called to draw UI.  
 +
*'''OnSaveGUI '''- Called while saving.  
 +
*'''OnUpdate '''- Called by MonoBehaviour.Update.  
 +
*'''OnLateUpdate '''- Called by MonoBehaviour.LateUpdate.  
 +
*'''OnFixedUpdate '''- Called by MonoBehaviour.FixedUpdate.  
 +
*'''OnShowGUI&nbsp;'''- Called when opening mod GUI.  
 +
*'''OnHideGUI&nbsp;'''- Called when closing mod GUI.  
  
 
== Examples ==
 
== Examples ==
  
A simple example of how to bind any action to keys.
+
A simple example of how to bind any action to keys. Note: With the new Unity Engine update, some games may require you to reference UnityEngine.InputLegacyModule before you can use the UnityEngine.Input function as usual.
 
<pre>using UnityEngine;
 
<pre>using UnityEngine;
 
using UnityModManagerNet;
 
using UnityModManagerNet;
Line 183: Line 210:
 
using Harmony;
 
using Harmony;
 
using UnityModManagerNet;
 
using UnityModManagerNet;
 +
using System.Reflection
  
 
namespace TestMod
 
namespace TestMod
Line 189: Line 217:
 
     {
 
     {
 
         public static bool enabled;
 
         public static bool enabled;
         public static ModEntry mod;
+
         public static UnityModManager.ModEntry mod;
  
 
         static bool Load(UnityModManager.ModEntry modEntry)
 
         static bool Load(UnityModManager.ModEntry modEntry)
Line 231: Line 259:
 
}</pre>
 
}</pre>
  
Now the function 'Application.loadedLevelName' will always return "New Level Name" string. Similarly, we can change any value in the game.<br/> Be sure to wrap the patch with a Try-Catch block so as not to interrupt an original function in case of errors. Errors can occur if a game code changes after updates.
+
Now the function 'Application.loadedLevelName' will always return "New Level Name" string. Similarly, we can change any value in the game.<br/> Be sure to wrap the patch with a Try-Catch block so as not to interrupt an original function in case of errors. Errors can occur if a game code changes after updates.<br/> Harmony 2 requires a minimum ManagerVersion 0.22.0.
 +
 
 +
== Loading custom textures or predefined assets. ==
 +
 
 +
In the Unity editor, use&nbsp;[https://docs.unity3d.com/ru/2019.4/Manual/AssetBundlesIntro.html AssetBundles]&nbsp;to create an asset file. The editor version used to create that asset bundle should not be higher than the Unity version in&nbsp;a game. Copy it to the mod folder, now you can load it using the code. These functions will require the additional libraries '''UnityEngine.AssetBundleModule.dll'''&nbsp;and '''UnityEngine.ImageConversionModule.dll.'''
 +
<pre>var assets = AssetBundle.LoadFromFile(Path.Combine(modEntry.Path, "modname.assets"));
 +
var go = assets.LoadAsset<GameObject>("go name");
 +
var tex = assets.LoadAsset<Texture2D>("texture name");</pre>
 +
 
 +
Can also be loaded&nbsp;jpg and png directly, but in this case they will be uncompressed&nbsp;and take much more video memory.
 +
<pre>var tex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
 +
tex.LoadImage(File.ReadAllBytes(filePath));
 +
tex.wrapMode = TextureWrapMode.Clamp;</pre>
  
 
== Finally ==
 
== Finally ==
  
Last, compile this code and copy the 'TestMod.dll' file to the 'Info.json' file. After starting the game, you will see messages in the 'Log.txt' file. Also detailed log can be found in 'YourGame\YourGame_Data\output_log.txt' or 'c:\Users%USERNAME%\AppData\LocalLow\YourGame\output_log.txt'.
+
Last, compile this code by going to '''Build > Build Solution (Ctrl + Shift + B) '''and copy the '''TestMod.dll '''file to the Info.json file. After starting the game, you will see messages in the '''Log.txt''' file. Also detailed log can be found in '''YourGame\YourGame_Data\output_log.txt '''or '''C:\Users%USERNAME%\AppData\LocalLow\YourGame\output_log.txt'''.
  
 
== Additionally ==
 
== Additionally ==
  
 
You can explore the game code using the&nbsp;[https://github.com/0xd4d/dnSpy dnspy]&nbsp;tool. Also available source code of my&nbsp;[https://github.com/newman55/unity-mods mods].
 
You can explore the game code using the&nbsp;[https://github.com/0xd4d/dnSpy dnspy]&nbsp;tool. Also available source code of my&nbsp;[https://github.com/newman55/unity-mods mods].
 +
 +
== Runtime Unity Editor ==
 +
 +
In-game inspector, editor and interactive console for applications made with Unity3D game engine. It's designed for debugging and modding Unity games, but can also be used as a universal trainer.&nbsp;[https://github.com/ManlyMarco/RuntimeUnityEditor RuntimeUnityEditor]
  
 
[[Category:Unity Mod Manager]]
 
[[Category:Unity Mod Manager]]

Latest revision as of 06:43, 13 August 2023

Introduction

Note, this tutorial is more focused on creating scripts than content, but you can still use it to make a new game object or texture. Unity runs the C# programming language, so creating a mod isn't as hardcore as it seems. You don't need to be a super programmer, just understand the basics of programming. The hardest part will be digging into the game's code to find the necessary functions you want to call/modify.

Project creation

  1. Download and install Microsoft Visual Studio Community 2017 with C#
  2. Open the project creation and select Class Library (NET Framework). Change name to TestMod. Open the project properties and target platform should be Net Framework 3.5 or Net Framework 4.0 for the newer Unity version. If the game is made for .net 3.5, UMM must be installed as Doorstop method.
  3. Add references to these game file located in the 'Managed' folder. (Note: UnityModManager should already be installed.)
    • Assembly-CSharp.dll
    • Assembly-CSharp-firstpass.dll
    • UnityEngine.dll
    • UnityEngine.UI.dll
  4. If the unity version is 2017 or higher, also add these additional files.
    • UnityEngine.CoreModule.dll
    • UnityEngine.IMGUIModule.dll
  5. Add references to these UMM files located in the 'Managed/UnityModManager' folder.
    • UnityModManager.dll
    • 0Harmony.dll
  6. Now we can use the game and unity mod manager functions.

Information file

Create an information file Info.json so that the mod manager can read it and determine which files need to load when starting the game. The file is written in json and must be placed in the Mods folder and one more folder e.g. \Steam\steamapps\common\YourGame\Mods\TestMod\Info.json.

{
  "Id": "TestMod",
  "DisplayName": "Test Mod",
  "Author": "username",
  "Version": "1.0.0",
  "ManagerVersion": "1.0.0",
  "GameVersion": "1.0.0",
  "Requirements": ["SomeMod-1.0.0", "AnotherMod"],
  "LoadAfter": ["SomeMod", "AnotherMod"],
  "AssemblyName": "TestMod.dll",
  "EntryMethod": "TestMod.Main.Load"
}

OR short variant

{
  "Id": "TestMod",
  "Version": "1.0.0",
  "EntryMethod": "TestMod.Main.Load"
}
  • Id - Unique string. It is desirable to match the folder name. (Required)
  • DisplayName - Name or Title. (Optional)
  • Author - The creator of the mod. (Optional)
  • Version- Needs for dependent mods and to checking for updates (Format must be 'x.x.x'). (Required)
  • ManagerVersion- Minimum required version of the mod manager (Format must be 'x.x.x'). (Recommended)
  • GameVersion - Minimum required version of game. Works if game supports this option. (Format must be 'x.x.x') (Optional)
  • Requirements- Minimum required version of mods or just other required mod. (Optional)
  • LoadAfter - List of mods that will be loaded first. (Optional) [0.22.5]
  • AssemblyName- Filename we are creating. Default like 'Id' (e.g. TestMod.dll). (Optional)
  • EntryMethod - A function that will be called by the mod manager at load game. (Required)
  • HomePage - Web address. (Optional)
  • Repository - Web address to check for updates. (Optional)

Assembly file

The mod manager supports several variants of the Entry functions. Use only one. Let's call it Load. The name must be the same as in the EntryMethod.

using UnityEngine;
using UnityModManagerNet;

namespace TestMod
{
    static class Main
    {
        // Simply call. Compiled without dependencies on UnityModManagerNet
        static void Load() 
        {
            // Something
        }

        // Transfer a variable with data about the mod.
        static void Load(UnityModManager.ModEntry modEntry) 
        {
            // Something
        }

        // Send a response to the mod manager about the launch status, success or not.
        static bool Load(UnityModManager.ModEntry modEntry)
        {
            // Something
            return true; // If false the mod will show an error.
        }
    }
}

You can add a function that will control on/off mode similar to hot-plug. This function is optional. If the function is missing, the mod may turn on, but it will turn off only after restarting the game.

using UnityEngine;
using UnityModManagerNet;

namespace TestMod
{
    static class Main
    {
        public static bool enabled;

        static bool Load(UnityModManager.ModEntry modEntry)
        {
            modEntry.OnToggle = OnToggle;
            return true;
        }

        // Called when the mod is turned to on/off.
        // With this function you control an operation of the mod and inform users whether it is enabled or not.
        static bool OnToggle(UnityModManager.ModEntry modEntry, bool value /* active or inactive */)
        {
            if (value)
            {
                Run(); // Perform all necessary steps to start mod.
            }
            else
            {
                Stop(); // Perform all necessary steps to stop mod.
            }
            
            enabled = value;
            return true; // If true, the mod will switch the state. If not, the state will not change.
        }
    }
}

Details of ModEntry (Mod Entry)

  • Info - Contains all fields from the 'Info.json' file.
  • Path- The path to the mod folder e.g. '\Steam\steamapps\common\YourGame\Mods\TestMod\'.
  • Active - Active or inactive.
  • Logger - Writes logs to the 'Log.txt' file.
  • OnToggle The presence of this function will let the mod manager know that the mod can be safely disabled during the game.
  • OnGUI - Called to draw UI.
  • OnSaveGUI - Called while saving.
  • OnUpdate - Called by MonoBehaviour.Update.
  • OnLateUpdate - Called by MonoBehaviour.LateUpdate.
  • OnFixedUpdate - Called by MonoBehaviour.FixedUpdate.
  • OnShowGUI - Called when opening mod GUI.
  • OnHideGUI - Called when closing mod GUI.

Examples

A simple example of how to bind any action to keys. Note: With the new Unity Engine update, some games may require you to reference UnityEngine.InputLegacyModule before you can use the UnityEngine.Input function as usual.

using UnityEngine;
using UnityModManagerNet;

namespace TestMod
{
    static class Main
    {
        public static bool enabled;

        static bool Load(UnityModManager.ModEntry modEntry)
        {
            modEntry.OnUpdate = OnUpdate;
            return true;
        }

        static void OnUpdate(UnityModManager.ModEntry modEntry, float dt)
        {
            if (Input.GetKeyDown(KeyCode.F1))
            {
                 Player.health = 9999f;
                 Player.weapon.ammo = 9999f;
            }
        }
    }
}

An example of how to draw a mod menu for a UMM UI. Some IMGUI documentation.

using UnityEngine;
using UnityModManagerNet;
using UnityEngine.UI;

namespace TestMod
{
    static class Main
    {
        public static bool enabled;
        public static string health;
        public static string ammo;

        static bool Load(UnityModManager.ModEntry modEntry)
        {
            modEntry.OnGUI = OnGUI;
            return true;
        }

        static void OnGUI(UnityModManager.ModEntry modEntry)
        {
            GUILayout.Label("God mode");
            health = GUILayout.TextField(health, GUILayout.Width(100f));
            ammo = GUILayout.TextField(ammo, GUILayout.Width(100f));
            if (GUILayout.Button("Apply") && int.TryParse(health, out var h) && int.TryParse(ammo, out var a))
            {
                 Player.health = h;
                 Player.weapon.ammo = a;
            }
        }
    }
}

Harmony

With harmony patches, you can completely take control of game functions. How to do this better to read the official Wiki. Here is an example.

using UnityEngine;
using Harmony;
using UnityModManagerNet;
using System.Reflection

namespace TestMod
{
    static class Main
    {
        public static bool enabled;
        public static UnityModManager.ModEntry mod;

        static bool Load(UnityModManager.ModEntry modEntry)
        {
            var harmony = new Harmony(modEntry.Info.Id);
            harmony.PatchAll(Assembly.GetExecutingAssembly());

            mod = modEntry;
            modEntry.OnToggle = OnToggle;

            return true;
        }

        static bool OnToggle(UnityModManager.ModEntry modEntry, bool value) 
        {
            enabled = value;
            modEntry.Logger.Log(Application.loadedLevelName);

            return true;
        }
    }

    [HarmonyPatch(typeof(Application), "loadedLevelName", MethodType.Getter)]
    static class Application_loadedLevelName_Patch
    {
        static void Postfix(ref string __result)
        {
            if (!Main.enabled)
                return;

            try
            {
                __result = "New Level Name";
            }
            catch(Exception e)
            {
                mod.Logger.Error(e.ToString());
            }
        }
    }
}

Now the function 'Application.loadedLevelName' will always return "New Level Name" string. Similarly, we can change any value in the game.
Be sure to wrap the patch with a Try-Catch block so as not to interrupt an original function in case of errors. Errors can occur if a game code changes after updates.
Harmony 2 requires a minimum ManagerVersion 0.22.0.

Loading custom textures or predefined assets.

In the Unity editor, use AssetBundles to create an asset file. The editor version used to create that asset bundle should not be higher than the Unity version in a game. Copy it to the mod folder, now you can load it using the code. These functions will require the additional libraries UnityEngine.AssetBundleModule.dll and UnityEngine.ImageConversionModule.dll.

var assets = AssetBundle.LoadFromFile(Path.Combine(modEntry.Path, "modname.assets"));
var go = assets.LoadAsset<GameObject>("go name");
var tex = assets.LoadAsset<Texture2D>("texture name");

Can also be loaded jpg and png directly, but in this case they will be uncompressed and take much more video memory.

var tex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
tex.LoadImage(File.ReadAllBytes(filePath));
tex.wrapMode = TextureWrapMode.Clamp;

Finally

Last, compile this code by going to Build > Build Solution (Ctrl + Shift + B) and copy the TestMod.dll file to the Info.json file. After starting the game, you will see messages in the Log.txt file. Also detailed log can be found in YourGame\YourGame_Data\output_log.txt or C:\Users%USERNAME%\AppData\LocalLow\YourGame\output_log.txt.

Additionally

You can explore the game code using the dnspy tool. Also available source code of my mods.

Runtime Unity Editor

In-game inspector, editor and interactive console for applications made with Unity3D game engine. It's designed for debugging and modding Unity games, but can also be used as a universal trainer. RuntimeUnityEditor