Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to use DLL ?

Discussion in 'Scripting' started by WoofWoofDude, Apr 6, 2014.

  1. WoofWoofDude

    WoofWoofDude

    Joined:
    Apr 6, 2014
    Posts:
    12
    Hi, I make another post because it's not exactly the same question :
    http://forum.unity3d.com/threads/238686-Load-a-script-in-build-mode

    So I want to use dll for "modding" my game.
    Every class in the dll inherited from a sub class of MonoBehaviour.
    But I don't know what class is in the dll (it's a mod!)...

    I want to make something like that
    Code (csharp):
    1.  
    2. Assembly a = Assembly.LoadFile("Addon/Test.dll");
    3. foreach (Type t in a.GetTypes())
    4.     Debug.Log(gameObject.AddComponent(t.ToString()));
    5.  
    (I know it doesn't work!)

    But can I do something similar ?
     
  2. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    There are several options, use reflection to find classes which inherit a particular base class, or are decorated with a particular attribute:
    Code (csharp):
    1.  
    2. // Crowbar Mod
    3. [Weapon("Crowbar")]
    4. public class CrowbarWeapon : Weapon {
    5.  
    6.     public override void OnUseWeapon() {
    7.     }
    8.  
    9. }
    10.  
    The mod would need to be compiled with reference to the DLLs within your game.

    The question is... Would this pose issues with regards to the Unity license? might be worth looking into.

    Would it not be better to define mods using data rather than DLLs?
     
  3. WoofWoofDude

    WoofWoofDude

    Joined:
    Apr 6, 2014
    Posts:
    12
    Yeah, my modding dll use my game dll to acces to specific class of my game. That is not a real problem.. no ?

    What do you mean ? Unity doesn't let me doing this ? It's for a school project so probably nobody will use this :/

    I need to add external script as component on GameObject.. In my first post LightStriker said I can probably use dll for that.. But I don't know how..

    I try to make what he said but that code doesn't work..

    Code (csharp):
    1.  
    2. Assembly a = Assembly.LoadFile("Addon/Test.dll");
    3. foreach (Type t in a.GetTypes())
    4.     Debug.Log(gameObject.AddComponent(t.ToString()));
    5.  
    6. -----
    7.  
    8. Can't add component because class 'Test.Class1' doesn't exist!
    9. UnityEngine.GameObject:AddComponent(String)
    10. Data:Awake() (at Assets/Script/Utilities/Data.cs:21)
    11.  
    --------------------------
    Dantus resolve the problem (I need to test more about it).. I need to use AddComponent(Type).. not AddComponent(String(Type))

    (This is in my first post..)
     
    Last edited: Apr 6, 2014
  4. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    953
    I don't know... but allowing "modders" to link against the UnityEngine.dll might be against their licensing terms...

    I haven't seen your first post.

    For modding, what about packaging scripts into asset bundles? (Unity Pro)
    http://docs.unity3d.com/Documentation/Manual/scriptsinassetbundles.html

    Though, often behaviour can be controlled by data... that is the approach I would use if at all possible. Torchlight 2 is a fantastic example of such a game. Most behaviour is controlled by data values and graphs.

    Just some suggestions to chew on anyhow :)
     
  5. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    Here's an example... Yes, this example of a bit overkilled because I wrote that as a "hot-load" plugin system for a standalone application, and not for Unity. It took a bit of modification to make it work for Unity, but it works fine.

    1) Create a DLL from Visual Studio/MonoDevelop aimed at .NET 3.5. You'll have to add reference to UnityEngine.dll. This is a "Manager" library that will be used as reference for your real plugin.

    2) Add the two following classes to the DLL:

    Plugin base class;
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6.  
    7. public abstract class Plugin
    8. {
    9.     protected string name = "Unnamed Plugin";
    10.  
    11.     public string Name
    12.     {
    13.         get { return name; }
    14.     }
    15.  
    16.     protected string description = "This is a missing plugin description.";
    17.  
    18.     public string Description
    19.     {
    20.         get { return description; }
    21.     }
    22.  
    23.     private bool registered = false;
    24.  
    25.     public bool Registered
    26.     {
    27.         get { return registered; }
    28.     }
    29.  
    30.     public virtual void Register()
    31.     {
    32.         registered = true;
    33.     }
    34.  
    35.     public virtual void Unregister()
    36.     {
    37.         registered = false;
    38.     }
    39.  
    40.     public virtual void OnUpdate() { }
    41. }
    42.  
    Plugin Manager;
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using System.IO;
    6. using System.Linq;
    7. using System.Reflection;
    8. using System.Text;
    9.  
    10. using UnityEngine;
    11.  
    12. /// <summary>
    13. /// Master of all plugins.
    14. /// Plugins are stored here.
    15. /// Plugins normally store a series of Command, Dockable and so on.
    16. /// Plugins are hot loaded. Which means the .DLL can be removed, added or updated and it is automaticly updated.
    17. /// </summary>
    18. public class PluginsManager : MonoBehaviour
    19. {
    20.     private static DirectoryInfo dir;
    21.     private static FileSystemWatcher watcher;
    22.  
    23.     private static List<PluginWrapper> plugins = new List<PluginWrapper>();
    24.  
    25.     public static Plugin[] Plugins
    26.     {
    27.         get
    28.         {
    29.             Plugin[] p = new Plugin[plugins.Count];
    30.  
    31.             for (int i = 0; i < plugins.Count; i++)
    32.                 p[i] = plugins[i].plugin;
    33.  
    34.             return p;
    35.         }
    36.     }
    37.  
    38.     private string path;
    39.  
    40.     private void Start()
    41.     {
    42.         path = Application.dataPath;
    43.         path += "/Plugins";
    44.  
    45.         dir = new DirectoryInfo(path);
    46.         watcher = new FileSystemWatcher(path);
    47.         InitPlugins();
    48.     }
    49.  
    50.     private void Update()
    51.     {
    52.         for (int i = 0; i < plugins.Count; i++)
    53.             plugins[i].plugin.OnUpdate();
    54.     }
    55.  
    56.     public static void InitPlugins()
    57.     {
    58.         RefreshPlugins();
    59.  
    60.         watcher.Deleted += new FileSystemEventHandler(FileDeleted);
    61.         watcher.Created += new FileSystemEventHandler(FileCreated);
    62.         watcher.Changed += new FileSystemEventHandler(FileChanged);
    63.         watcher.EnableRaisingEvents = true;
    64.     }
    65.  
    66.     private static void RefreshPlugins()
    67.     {
    68.         foreach (FileInfo file in dir.GetFiles("*.dll"))
    69.         {
    70.             LoadPlugins(file);
    71.         }
    72.     }
    73.  
    74.     private static void LoadPlugins(FileInfo file)
    75.     {
    76.         try
    77.         {
    78.             Assembly assembly = Assembly.LoadFrom(file.FullName);
    79.  
    80.             foreach (Type type in assembly.GetTypes())
    81.             {
    82.                 if (type.IsSubclassOf(typeof(Plugin))  type.IsAbstract == false)
    83.                 {
    84.                     Plugin b = type.InvokeMember(null,
    85.                                                 BindingFlags.CreateInstance,
    86.                                                 null, null, null) as Plugin;
    87.                     Debug.LogError("Load " + b.GetType().Name);
    88.                     plugins.Add(new PluginWrapper(b, file));
    89.                     b.Register();
    90.                 }
    91.             }
    92.         }
    93.         catch (ReflectionTypeLoadException ex)
    94.         {
    95.             StringBuilder sb = new StringBuilder();
    96.             foreach (Exception exSub in ex.LoaderExceptions)
    97.             {
    98.                 sb.AppendLine(exSub.Message);
    99.                 if (exSub is FileNotFoundException)
    100.                 {
    101.                     FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
    102.                     if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
    103.                     {
    104.                         sb.AppendLine("Fusion Log:");
    105.                         sb.AppendLine(exFileNotFound.FusionLog);
    106.                     }
    107.                 }
    108.                 sb.AppendLine();
    109.             }
    110.             string errorMessage = sb.ToString();
    111.             Debug.Log("Plugins Manager");
    112.         }
    113.     }
    114.  
    115.     private static List<PluginWrapper> GetPluginsFromFile(FileInfo file)
    116.     {
    117.         List<PluginWrapper> p = new List<PluginWrapper>();
    118.  
    119.         foreach (PluginWrapper wrapper in plugins)
    120.         {
    121.             if (wrapper.file.FullName == file.FullName)
    122.                 p.Add(wrapper);
    123.         }
    124.  
    125.         return p;
    126.     }
    127.  
    128.     private static void UnloadPlugins(FileInfo file)
    129.     {
    130.         List<PluginWrapper> toUnload = GetPluginsFromFile(file);
    131.  
    132.         foreach (PluginWrapper p in toUnload)
    133.         {
    134.             p.plugin.Unregister();
    135.             plugins.Remove(p);
    136.         }
    137.     }
    138.  
    139.     private static void FileDeleted(object sender, FileSystemEventArgs e)
    140.     {
    141.         UnloadPlugins(new FileInfo(e.FullPath));
    142.     }
    143.  
    144.     private static void FileCreated(object sender, FileSystemEventArgs e)
    145.     {
    146.         LoadPlugins(new FileInfo(e.FullPath));
    147.     }
    148.  
    149.     private static void FileChanged(object sender, FileSystemEventArgs e)
    150.     {
    151.         FileInfo file = new FileInfo(e.FullPath);
    152.         UnloadPlugins(file);
    153.         LoadPlugins(file);
    154.     }
    155.  
    156.     private struct PluginWrapper
    157.     {
    158.         public Plugin plugin;
    159.         public FileInfo file;
    160.  
    161.         public PluginWrapper(Plugin plugin, FileInfo file)
    162.         {
    163.             this.plugin = plugin;
    164.             this.file = file;
    165.         }
    166.     }
    167. }
    168.  
    3) Build your Manager DLL and add it to your asset in Unity.

    4) Create a GameObject and add the PluginsManager on it.

    5) Save the scene with the GameObject.

    6) Build a Window game using that scene.

    7) Create a second .DLL using the same method as above. However, you will add a second reference; your first DLL. This is your "plugin".

    8) In the second DLL, add the following classes;

    The plugin definition itself
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6.  
    7. using UnityEngine;
    8.  
    9. public class TestPlugin : Plugin
    10. {
    11.     private GameObject go;
    12.    
    13.     public override void OnUpdate()
    14.     {
    15.         if (go == null)
    16.         {
    17.             go = new GameObject("Plug");
    18.             go.AddComponent<TestBehaviour>();
    19.         }
    20.     }
    21. }
    22.  
    And a MonoBehaviour, just to prove that it can be added to the executing assembly, in game, after compile;
    Code (csharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5. using System.Text;
    6.  
    7. using UnityEngine;
    8.  
    9. public class TestBehaviour : MonoBehaviour
    10. {
    11.     private void OnGUI()
    12.     {
    13.         GUI.Label(new Rect(100, 100, 200, 200), "PLUGIN LOADED");
    14.     }
    15. }
    16.  
    9) In your build directory (where you built your .exe), in the "Build_Data" folder, create a new folder named "Plugins".

    10) Build your plugin DLL and add place it in your "Plugins" folder.

    11) Start the game. If everything was done properly, you should see "PLUGIN LOADED" in your GUI.
     
    Last edited: Apr 6, 2014
  6. WoofWoofDude

    WoofWoofDude

    Joined:
    Apr 6, 2014
    Posts:
    12
    You said the problem.. Unity pro :(


    Thanks for the code LightStriker ! (C'est moi ou tes québecois !? Moi qui se fen le cul en anglais hahaha)
     
  7. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    The issue here, is people you give the game to, will need Unity Pro to modify your game. Quite an horrible solution. For code only, you don't need such drastic solution. When data is involved... That's an other problem.

    C'est pas toi, je suis Québecois.
     
  8. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    I think that your doing too much in this case. Allowing modders to add content is very simple in Unity, instead of creating a DLL to run against something, you should simply add the ability to load folders or zip files into your Unity project.

    You would need to specifically create a runtime importer for whatever file that you want to use:

    http://forum.unity3d.com/threads/43161-UPDATE-runtime-obj-importer

    maybe a text reader to read information about your import.

    FYI) Modders love things that are made simple. If you get too complex with your modding process then no one will mod your game.
     
  9. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,716
    Tell me, how would you allow modder to add a new gameplay type without DLL? DLL are simple and straight forwards. Of course, only talking about behaviour here... Asset is something else.