Fake UnityEngine:如何让unity编译的代码库直接在.net环境上运行起来(无绘制层)
2016-11-15 10:56
507 查看
项目组用unity开发的slg游戏项目到了收尾阶段,最近要求实现一个带服系统,就是一个程序能挂机很多游戏账号。 UnityEngine的C#层是在mono虚拟机上运行的,一个mono虚拟机至少要占用40M左右的内存。 如果unity编译好的游戏代码库能直接在.net的环境上运行起来,那么挂机程序就能挂更多的账号, 带服专员就能“忽悠”更多的玩家,老板就能挣更多的钱,万恶的资本家QAQ。
思路很简单:
建立一个工程包含两个代码库:UnityEngine(以下称为FakeUnityEngine)和UnityEngineImp,在UnityEngineImp里引用游戏里用到的第三方库和unity编译好的游戏代码库以及FakeUnityEngine代码库。这样游戏里用到的UnityEngine的接口都会call到FakeUnityEngine里,只要在FakeUnityEngine里实现所有用到的UnityEngine的接口(无绘制层),游戏代码库就能在.net环境上运行起来。
首先要实现MonoBehaviourManager类,模拟mono脚本的调用过程,利用反射依次调用mono脚本的Awake、Start及Update函数。
下面贴出该类的代码,代码里有完整的注释:
using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Reflection; using System.Threading; using System.Collections; namespace UnityEngine { public static class MonoBehaviourManager { private static List<object> MonoBehaviourPool = new List<object>(); private static List<object> MonoBehaviourUpdatePool = new List<object>(); private static Thread callMonoBehaviourThread; private static Thread callMonoBehaviourUpdateThread; public static void AddInstance(object instance)//加载一个mono脚本对象,把它加到MonoBehaviourPool,并调用它的Awake方法 { MonoBehaviourPool.Add(instance); CallMethod(instance, "Awake", null); } public static void CallMethod(this object instance, string name, params object[] param) { BindingFlags flag = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; Type type = instance.GetType(); MethodInfo method = type.GetMethod(name, flag); if (method != null) { if (method.ReturnType == typeof(IEnumerator)) { IEnumerator etr = (IEnumerator)method.Invoke(instance, param); while (etr.MoveNext()) { //Debug.Log(); } } else { method.Invoke(instance, param); } } } public static void CallMonoBehaviourStartFunc(this object instance) { CallMethod(instance, "Start", null); } public static void CallMonoBehaviourUpdateFunc(this object instance) { CallMethod(instance, "Update", null); } public static void Initialize()//初始化,起两个线程分别用来调mono脚本对象的Start方法和Update方法 { callMonoBehaviourThread = new Thread(new ThreadStart(CallMonoBehaviourThreadFunc)); callMonoBehaviourThread.Start(); callMonoBehaviourUpdateThread = new Thread(new ThreadStart(CallMonoBehaviourUpdateThreadFunc)); callMonoBehaviourUpdateThread.Start(); } public static void CallMonoBehaviourThreadFunc() { while (true) { if (MonoBehaviourPool.Count > 0) { object currentMonoBehaviour = MonoBehaviourPool[0]; CallMonoBehaviourStartFunc(currentMonoBehaviour);//调用MonoBehaviourPool里的mono脚本对象的Start方法 MonoBehaviourUpdatePool.Add(currentMonoBehaviour);//把MonoBehaviourPool里的mono脚本对象加到MonoBehaviourUpdatePool MonoBehaviourPool.Remove(currentMonoBehaviour);//移除MonoBehaviourPool里的mono脚本对象 } Thread.Sleep(20); } } public static void CallMonoBehaviourUpdateThreadFunc() { while (true) { for (int i = 0; i < MonoBehaviourUpdatePool.Count; i++) { object currentMonoBehaviour = MonoBehaviourUpdatePool[i]; CallMonoBehaviourUpdateFunc(currentMonoBehaviour);//调用MonoBehaviourUpdatePool里的mono脚本对象的Update方法,20ms调一次 Thread.Sleep(20); } } } } }
另外在FakeUnityEngine里实现的UnityEngine的接口,由于不需要实现绘制层,所有大部分的接口留空就行了。以GameObject为例:
public sealed class GameObject : Object { // Methods public GameObject() { } public GameObject(string name) { } public GameObject(string name, params Type[] components) { } public T AddComponent<T>() { T instance = Activator.CreateInstance<T>(); MonoBehaviourManager.AddInstance(instance); return instance; } public T GetComponent<T>() { T instance = Activator.CreateInstance<T>(); return instance; } public T[] GetComponentsInChildren<T>(bool includeInactive) { //T instance = Activator.CreateInstance<T>(); return new T[] { }; } public T[] GetComponentsInChildren<T>() { //T instance = Activator.CreateInstance<T>(); return new T[] { }; } public static void Destroy(GameObject obj) { } public Transform transform { get { return new Transform(); } } }
然后是整个UnityEngineImp工程的主函数:
using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; using Framework; using UnityEngine; //Fake UnityEngine by:yjx namespace UnityEngineImp { class Program { static void Main(string[] args) { MonoBehaviourManager.Initialize();//mono脚本管理类初始化 //接下来都是Unity的游戏代码库的调用 SDKMsg paySdk = new SDKMsg(); MonoBehaviourManager.AddInstance(paySdk);//SDKMsg加入mono脚本管理类 ANetManager.Init();//ANetManager初始化 OnlineSDKManager onlineSDKManager = new OnlineSDKManager(); MonoBehaviourManager.AddInstance(onlineSDKManager); AAssetBundleManager aAssetBundleManager = new AAssetBundleManager(); MonoBehaviourManager.AddInstance(aAssetBundleManager); ViewServerLogic viewServerLogic = new ViewServerLogic(); MonoBehaviourManager.AddInstance(viewServerLogic); ViewGeneralTipsLogic viewGeneralTipsLogic = new ViewGeneralTipsLogic(); MonoBehaviourManager.AddInstance(viewGeneralTipsLogic); LoginManager loginManager = new LoginManager(); MonoBehaviourManager.AddInstance(loginManager); LoginManager.StartGame();//进入游戏:建立socket连接,发送进入游戏请求 } } }
最后是实际运行效果,建立socket连接、重定向、发送登录游戏请求,收到各种模块信息及登陆游戏请求回复:
相关文章推荐
- winpe 下如何运行.NETFramewor 环境的程序
- 如何解决linux下编译环境,运行环境不同的问题
- MAC 系统如何使用 Sublime Text 2 直接编译运行 java 代码
- 如何用命令行(CMD)编译运行C 程序—环境变量的设置
- ruby中如何直接编译运行C代码
- 第1章 Java基本概念及环境配置——FAQ1.15 如何编译. 运行Java应用程序?
- 如何实现.net下开发的程序脱离.net环境运行并且可以跨平台的想法
- editplus如何直接编译运行命令提示符
- ruby中如何直接编译运行C代码
- 如何将MTK的代码分模块在eclipse中单独编译运行起来调试
- MyEclipse中如何设置 jdk 和 jre 编译运行环境
- 在Delphi中宿主.NET运行环境,直接调用.NET中的代码
- .NET是在2002年发布,2000、2001年的C#是如何编译运行的?
- MyEclipse中如何设置 jdk 和 jre 编译运行环境
- .NET 环境下运行时代码生成和编译
- 如何解决linux下编译环境,运行环境不同的问题
- 如何在一个clean的环境下,编译、运行EFL。
- MyEclipse中如何设置 jdk 和 jre 编译运行环境
- cmd 如何进入d盘,然后执行编译 这样就可以省去每次自己动手运行,直接脚本一次性搞定
- ruby中如何直接编译运行C代码