-->

分類

2017年4月4日 星期二

Unity load external script at runtime(using Visual Studio)

reference link

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, Action finishCallback)
 {
  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);
  }
 }
}

2 則留言:

  1. 回覆
    1. You are welcome. Actually, I don't know a practical use case with this trick, do you have one?

      刪除