我们的项目导出WebGL后运行不了,因为我们使用了System.Reflection.Assembly.GetExecutingAssembly().CreateInstance
来根据类名创建对象。解决方法是创建一个工厂类,根据类名调用不同的代码new对象。而然这样很麻烦,并且每次都需要修改这个工厂。我想了一个方法就是使用一个编辑器脚本自动生成工厂类,思路是利用反射机制获取一个assembly内部所有要用工厂创建的类的名字,然后生成代码。因为我们所有要用工厂创建的类都有共同的基类,所以可以通过System.Type类的IsSubclassOf方法进行判断。基本的代码如下:
List<string> sceneClassNames = new List<string>();
string assemblyPath = Application.dataPath.Replace ("Assets", "Library/ScriptAssemblies");
Debug.Log (assemblyPath);
//System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly ();
System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom (assemblyPath+"/Assembly-CSharp.dll");
System.Type sceneType = assembly.GetType ("Scene");//获取基类type
System.Type[] types = assembly.GetTypes ();
foreach (System.Type type in types) {
if(type.IsSubclassOf(sceneType))
sceneClassNames.Add(type.Name);
}
注意:这儿唯一有些小麻烦的地方是获取项目的assembly,如果是游戏内部代码在游戏运行时获取,就可以使用GetExecutingAssembly ()获取。但是我想在编辑器中运行就必须手动载入游戏项目的assembly,方法是使用LoadFrom(),但需要获取到assembly的路径,使用Application.dataPath.Replace (“Assets”, “Library/ScriptAssemblies”);即可获取。
获取到所有需要工厂创建的类的名字后,生成工厂类的代码就很容易了:
//output SceneFactory code
string codePath = Application.dataPath +"/_scripts/Game/SceneFactory.cs";
StreamWriter writer;
writer = File.CreateText(codePath);
writer.WriteLine ("// This file is auto created by ExportSceneFactory tool.");
writer.WriteLine ("public class SceneFactory {");
writer.WriteLine (" public static Scene CreateScene(string sceneClassName) {");
for (int i=0; i<sceneClassNames.Count; i++) {
string name = sceneClassNames[i];
string tmp = (i==0)?"":"else ";
string code = string.Format(" {0}if (sceneClassName == \"{1}\") ", tmp, name);
writer.WriteLine(code);
writer.WriteLine(" {");
code = string.Format(" return new {0}();",name);
writer.WriteLine(code);
writer.WriteLine(" }");
}
writer.WriteLine (" return null;");
writer.WriteLine (" }");
writer.WriteLine ("}");
writer.Close();
虽然用if/else比较效率不那么高,但是也够用了,如果不嫌麻烦可以改成面向对象的方法用一个map+若干独立工厂实现。
然后把这个脚本集成在我们的持续集成框架中,在build webgl版本之前调用一下就可以了,完美~
当然别忘了创建对象的地方,根据是否webgl版本选择不同的创建方式:
#if UNITY_WEBGL
Scene scene = SceneFactory.CreateScene(sceneClassName);
#else
Scene scene = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(sceneClassName) as Scene;
#endif