To load external script into Unity at runtime, the external script needs to be compiled as assembly(.dll), here are steps to do so.
1. Create a new C# project with Visual Studio, choose the project type as "Class Library"
2. Add UnityEngine.dll as project reference, the UnityEngine.dll is located at "<Unity_Install_Path>\Editor\Data\Managed". Add the dll to project reference by the following steps:
right click project name in Solution Explorer -> Properties -> Reference Paths -> Folder -> browse to find the folder that contains .dll -> Add Folder
right click "References" in Solution Explorer -> Add References -> Assembly -> search the dll you added
3. in the created Class Library project, create a class that extends MonoBehaviour like what we usually do in Unity, you can implement its Start(), Update()...
4. Release build this library project and remember the path to the .dll created by this library project
5. use the WWWAssemblyLoader in the reference link above or my modified assembly loader below:
using UnityEngine; using System.Collections; using System; using System.Reflection; public class MyWWWAssemblyLoader { public IEnumerator loadAssembly(string url, ActionfinishCallback) { if(url == null || finishCallback == null) { yield break; } WWW m_WWW = new WWW(url); while(!m_WWW.isDone) { yield return null; } Assembly assembly = LoadAssembly(m_WWW); if (assembly != null) { Debug.Log("load assembly: " + url + " Done"); finishCallback(true, url, assembly); } else { Debug.Log("load assembly: " + url + " Failed"); finishCallback(false, url, assembly); } } private Assembly LoadAssembly(WWW m_WWW) { try { return Assembly.Load(m_WWW.bytes); } catch (System.Exception e) { Debug.LogError(e.ToString()); return null; } } }
6. load the dll and use reflection to control it as following:
IEnumerator Start() { //load script yield return StartCoroutine(loadAssemblyScriptAsync("file:///YourAssembly.dll", onAssemblyScriptLoaded)); } private IEnumerator loadAssemblyScriptAsync(string url, Action callback) { MyWWWAssemblyLoader myLoader = new MyWWWAssemblyLoader(); yield return StartCoroutine(myLoader.loadAssembly(url, callback)); } private void onAssemblyScriptLoaded(bool success, string url, Assembly assembly) { if (!success) { Debug.LogError("onAssemblyScriptLoaded failed"); return; } Debug.Log("Assembly url: " + url); System.Type type = assembly.GetType("YourClassNameSpace.YourClassName"); if (type == null) { Debug.LogError("type is null"); return; } FieldInfo field = type.GetField("YourFloatField"); GameObject go = GameObject.Find("YourEmptyGameObject"); //add loaded script to go Component comp = go.AddComponent(type); FieldInfo myFieldInfo = type.GetField("YourFloatField"); Debug.Log("The field value of YourFloatField is " + myFieldInfo.GetValue(comp)); myFieldInfo.SetValue(comp, 30.0f); Debug.Log("The new field value of YourFloatField is " + myFieldInfo.GetValue(comp)); }
7. the external script for above code can be:
using System; using System.Collections.Generic; using System.Text; using UnityEngine; namespace YourClassNameSpace { public class YourClassName : MonoBehaviour { public float YourFloatField = 1.0f; // Use this for initialization void Start() { } // Update is called once per frame void Update() { transform.Rotate(Vector3.up, YourFloatField * Time.deltaTime); } } }
Thank you, this was very useful
回覆刪除You are welcome. Actually, I don't know a practical use case with this trick, do you have one?
刪除