用
Unity
游戏
的时候,客户端
程序
经常会需要
动态
从远程服务器取 asset(如服务器更新了宠物,而宠物是包含
脚本
,跟随主人移动). Unity 提供了BuildPipeline.BuildAssetBundle 可以
解决
大多数问题, 但是, script 和 shader 不能被编成 asset.
好在Unity 的脚本语言是C#,跑在mono 上 ,为解决此类问题提供了可能性.
首先,用vs2005/vs2008新建一个类库类型的 项目 名字为test,引用 UnityEngine.dll(在\Program Files\Unity\Editor\Data\lib 下)
在 class1里贴上:
using System;
using UnityEngine;
using System.Text;
public class Class1
{
public void Do GUI ()
{
GUILayout.Button("12345");
}
public void DoStart(){}
public void DoUpdate(){}
public void DoLateUpdate(){}
}
Class1 就是你需要编译后发送到客户端的类(你可以在这个项目里添加多个类
)
ok, 编译,得到test.dll,把test 放到web服务器上(在我本机地址为 http://192.168.0.120/test.dll ).
接下来在 客户端程序里加一个类 ScriptAssetFactory
using System;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
public class ScriptAssetFactory: MonoBehaviour
{
public static void AddDll(string GameObjectName, ScriptPara para)
{
GameObject obj = GameObject.Find(GameObjectName);
obj.AddComponent(typeof(ScriptAssetFactory));
obj.SendMessage("AddScript", para);
}
void AddScript(ScriptPara para)
{
LoadHelp.LoadBytes(this, para.DllUrl, null, delegate(byte[] buffer)
{
Assembly ass = Assembly.Load(buffer);
foreach (string item in para.Types)
{
Type tempType = ass.GetType(item);
if (tempType != null)
{
ScriptEntry entry = new ScriptEntry();
entry.ScriptType = tempType;
entry.ScriptObject = ass.CreateInstance(item);
if (tempType.GetMethod("DoGUI") != null)
{
entry.DoGUI = true;
}
if (tempType.GetMethod("DoLateUpdate") != null)
{
entry.DoLateUpdate = true;
}
if (tempType.GetMethod("DoUpdate") != null)
{
entry.DoUpdate = true;
}
if (tempType.GetMethod("DoStart") != null)
{
entry.DoStart = true;
entry.ScriptType.InvokeMember("DoStart", BindingFlags.InvokeMethod, null, entry.ScriptObject, null);
}
ScriptsList.Add(entry);
}
}
});
}
private List<ScriptEntry> ScriptsList;
void Start()
{
ScriptsList = new List<ScriptEntry>();
}
void Update()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoUpdate)
{
item.ScriptType.InvokeMember("DoUpdate", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
void LateUpdate()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoLateUpdate)
{
item.ScriptType.InvokeMember("DoLateUpdate", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
void OnGUI()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoGUI)
{
item.ScriptType.InvokeMember("DoGUI", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
}
public struct ScriptEntry
{
public object ScriptObject;
public Type ScriptType;
public bool DoGUI;
public bool DoStart;
public bool DoUpdate;
public bool DoLateUpdate;
}
public class ScriptPara
{
public ScriptPara(string _DllUrl, string[] _Types)
{
DllUrl = _DllUrl;
Types = _Types;
}
public string DllUrl;
public string[] Types;
}
提供一个工具类 LoadHelp
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;
using System.Xml.Serialization;
using System.Collections;
public delegate void LoadDelegate<T>(T asset);
public class LoadHelp
{
private static List<WWW> proList = new List<WWW>();
private static bool enable = false;
private static int process = 0;
public static void LoadObject<T>(MonoBehaviour thread, string url, LoadDelegate<T> asset) where T : UnityEngine.Object
{
thread.StartCoroutine(_LoadObject<T>(url, asset));
}
public static void DoGUI()
{
enable = true;
if (proList.Count > 0)
{
float processf = 0;
WWW[] WWWList = proList.ToArray();
foreach (WWW item in WWWList)
{
processf += item.progress;
}
process = (int)(processf * 100.0f) / WWWList.Length;
GUI.Box(new Rect((Screen.width - 150) / 2, Screen.height - 70, 150, 37), "Loading...\n" + process.ToString() + "%");
}
}
public static void LoadBytes(MonoBehaviour thread, string url, Action<string> GetString, Action<byte[]> GetBytes)
{
thread.StartCoroutine(_LoadBytes(url, GetString, GetBytes));
}
private static IEnumerator _LoadBytes(string url, Action<string> GetString, Action<byte[]> GetBytes)
{
WWW rerquest = new WWW(url);
if (enable)
{
proList.Add(rerquest);
}
yield return rerquest;
if (rerquest.isDone)
{
if (GetBytes != null)
{
GetBytes(rerquest.bytes);
}
if (GetString != null)
{
GetString(rerquest.data);
}
}
if (enable)
{
proList.Remove(rerquest);
}
rerquest.Dispose();
}
public static IEnumerator _LoadObject<T>(string url, LoadDelegate<T> asset) where T : UnityEngine.Object
{
WWW rerquest = new WWW(url);
if (enable)
{
proList.Add(rerquest);
}
yield return rerquest;
if (rerquest.isDone)
{
if (typeof(T) == typeof(Texture2D))
{
Texture2D tex = rerquest.texture;
if (tex != null && asset != null) asset(tex as T);
}
if (typeof(T) == typeof(AssetBundle))
{
if (asset != null) asset(rerquest.assetBundle as T);
}
if (typeof(T) == typeof(GameObject))
{
GameObject mod = rerquest.assetBundle.mainAsset as GameObject;
if (mod != null && asset != null) asset(mod as T);
}
if (typeof(T) == typeof(MovieTexture))
{
MovieTexture mov = rerquest.movie;
if (mov != null && asset != null) asset(mov as T);
}
if (typeof(T) == typeof(AudioClip))
{
AudioClip aud = rerquest.oggVorbis;
if (aud != null && asset != null) asset(aud as T);
}
}
if (enable)
{
proList.Remove(rerquest);
}
rerquest.Dispose();
//GC.Collect();
//GC.WaitForPendingFinalizers();
//GC.Collect();
}
public static byte[] ObjectToArray<T>(T message)
{
XmlSerializer format = new XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream();
format.Serialize(memoryStream, message);
memoryStream.Close();
return memoryStream.ToArray();
}
public static T ArrayToObject<T>(byte[] array)
{
XmlSerializer format = null;
MemoryStream memoryStream = null;
object _message = null;
try
{
format = new XmlSerializer(typeof(T));
memoryStream = new MemoryStream(array);
_message = format.Deserialize(memoryStream);
}
catch (Exception e)
{
}
finally
{
memoryStream.Close();
memoryStream.Dispose();
}
return (T)_message;
}
}
接下来我们测试一下
新建一个cube(用来测试,实际使用的时候可能用宠物的主gameobject)名字为 cube001,新建一个脚本,在 Start()里帖入
ScriptAssetFactory.AddDll("cube001", new ScriptPara(" http://192.168.0.120/test.dll ", new string[] { "Class1"}));
ok!
好在Unity 的脚本语言是C#,跑在mono 上 ,为解决此类问题提供了可能性.
首先,用vs2005/vs2008新建一个类库类型的 项目 名字为test,引用 UnityEngine.dll(在\Program Files\Unity\Editor\Data\lib 下)
在 class1里贴上:
using System;
using UnityEngine;
using System.Text;
public class Class1
{
public void Do GUI ()
{
GUILayout.Button("12345");
}
public void DoStart(){}
public void DoUpdate(){}
public void DoLateUpdate(){}
}
Class1 就是你需要编译后发送到客户端的类(你可以在这个项目里添加多个类
)
ok, 编译,得到test.dll,把test 放到web服务器上(在我本机地址为 http://192.168.0.120/test.dll ).
接下来在 客户端程序里加一个类 ScriptAssetFactory
using System;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
public class ScriptAssetFactory: MonoBehaviour
{
public static void AddDll(string GameObjectName, ScriptPara para)
{
GameObject obj = GameObject.Find(GameObjectName);
obj.AddComponent(typeof(ScriptAssetFactory));
obj.SendMessage("AddScript", para);
}
void AddScript(ScriptPara para)
{
LoadHelp.LoadBytes(this, para.DllUrl, null, delegate(byte[] buffer)
{
Assembly ass = Assembly.Load(buffer);
foreach (string item in para.Types)
{
Type tempType = ass.GetType(item);
if (tempType != null)
{
ScriptEntry entry = new ScriptEntry();
entry.ScriptType = tempType;
entry.ScriptObject = ass.CreateInstance(item);
if (tempType.GetMethod("DoGUI") != null)
{
entry.DoGUI = true;
}
if (tempType.GetMethod("DoLateUpdate") != null)
{
entry.DoLateUpdate = true;
}
if (tempType.GetMethod("DoUpdate") != null)
{
entry.DoUpdate = true;
}
if (tempType.GetMethod("DoStart") != null)
{
entry.DoStart = true;
entry.ScriptType.InvokeMember("DoStart", BindingFlags.InvokeMethod, null, entry.ScriptObject, null);
}
ScriptsList.Add(entry);
}
}
});
}
private List<ScriptEntry> ScriptsList;
void Start()
{
ScriptsList = new List<ScriptEntry>();
}
void Update()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoUpdate)
{
item.ScriptType.InvokeMember("DoUpdate", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
void LateUpdate()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoLateUpdate)
{
item.ScriptType.InvokeMember("DoLateUpdate", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
void OnGUI()
{
foreach (ScriptEntry item in ScriptsList)
{
if (item.DoGUI)
{
item.ScriptType.InvokeMember("DoGUI", BindingFlags.InvokeMethod, null, item.ScriptObject, null);
}
}
}
}
public struct ScriptEntry
{
public object ScriptObject;
public Type ScriptType;
public bool DoGUI;
public bool DoStart;
public bool DoUpdate;
public bool DoLateUpdate;
}
public class ScriptPara
{
public ScriptPara(string _DllUrl, string[] _Types)
{
DllUrl = _DllUrl;
Types = _Types;
}
public string DllUrl;
public string[] Types;
}
提供一个工具类 LoadHelp
using UnityEngine;
using System.Collections.Generic;
using System.IO;
using System;
using System.Xml.Serialization;
using System.Collections;
public delegate void LoadDelegate<T>(T asset);
public class LoadHelp
{
private static List<WWW> proList = new List<WWW>();
private static bool enable = false;
private static int process = 0;
public static void LoadObject<T>(MonoBehaviour thread, string url, LoadDelegate<T> asset) where T : UnityEngine.Object
{
thread.StartCoroutine(_LoadObject<T>(url, asset));
}
public static void DoGUI()
{
enable = true;
if (proList.Count > 0)
{
float processf = 0;
WWW[] WWWList = proList.ToArray();
foreach (WWW item in WWWList)
{
processf += item.progress;
}
process = (int)(processf * 100.0f) / WWWList.Length;
GUI.Box(new Rect((Screen.width - 150) / 2, Screen.height - 70, 150, 37), "Loading...\n" + process.ToString() + "%");
}
}
public static void LoadBytes(MonoBehaviour thread, string url, Action<string> GetString, Action<byte[]> GetBytes)
{
thread.StartCoroutine(_LoadBytes(url, GetString, GetBytes));
}
private static IEnumerator _LoadBytes(string url, Action<string> GetString, Action<byte[]> GetBytes)
{
WWW rerquest = new WWW(url);
if (enable)
{
proList.Add(rerquest);
}
yield return rerquest;
if (rerquest.isDone)
{
if (GetBytes != null)
{
GetBytes(rerquest.bytes);
}
if (GetString != null)
{
GetString(rerquest.data);
}
}
if (enable)
{
proList.Remove(rerquest);
}
rerquest.Dispose();
}
public static IEnumerator _LoadObject<T>(string url, LoadDelegate<T> asset) where T : UnityEngine.Object
{
WWW rerquest = new WWW(url);
if (enable)
{
proList.Add(rerquest);
}
yield return rerquest;
if (rerquest.isDone)
{
if (typeof(T) == typeof(Texture2D))
{
Texture2D tex = rerquest.texture;
if (tex != null && asset != null) asset(tex as T);
}
if (typeof(T) == typeof(AssetBundle))
{
if (asset != null) asset(rerquest.assetBundle as T);
}
if (typeof(T) == typeof(GameObject))
{
GameObject mod = rerquest.assetBundle.mainAsset as GameObject;
if (mod != null && asset != null) asset(mod as T);
}
if (typeof(T) == typeof(MovieTexture))
{
MovieTexture mov = rerquest.movie;
if (mov != null && asset != null) asset(mov as T);
}
if (typeof(T) == typeof(AudioClip))
{
AudioClip aud = rerquest.oggVorbis;
if (aud != null && asset != null) asset(aud as T);
}
}
if (enable)
{
proList.Remove(rerquest);
}
rerquest.Dispose();
//GC.Collect();
//GC.WaitForPendingFinalizers();
//GC.Collect();
}
public static byte[] ObjectToArray<T>(T message)
{
XmlSerializer format = new XmlSerializer(typeof(T));
MemoryStream memoryStream = new MemoryStream();
format.Serialize(memoryStream, message);
memoryStream.Close();
return memoryStream.ToArray();
}
public static T ArrayToObject<T>(byte[] array)
{
XmlSerializer format = null;
MemoryStream memoryStream = null;
object _message = null;
try
{
format = new XmlSerializer(typeof(T));
memoryStream = new MemoryStream(array);
_message = format.Deserialize(memoryStream);
}
catch (Exception e)
{
}
finally
{
memoryStream.Close();
memoryStream.Dispose();
}
return (T)_message;
}
}
接下来我们测试一下
新建一个cube(用来测试,实际使用的时候可能用宠物的主gameobject)名字为 cube001,新建一个脚本,在 Start()里帖入
ScriptAssetFactory.AddDll("cube001", new ScriptPara(" http://192.168.0.120/test.dll ", new string[] { "Class1"}));
ok!