项目组用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连接、重定向、发送登录游戏请求,收到各种模块信息及登陆游戏请求回复: