Unity游戏状态脚本控制思考
发表于2018-10-19
Unity的各个脚本是怎么有条不紊运作的,带着这个思考,下面就给大家分享下自己是怎么用C#控制台程序模拟下游戏状态脚本控制的,主要分为这两大块来给大家介绍。
1、Unity的脚本控制
首先是游戏脚本接口,如下:
interface IGame { void Awake(); void Start(); void Update(); }
有了脚本接口我们就能够去实现具体逻辑了,这里就简单的实现两个脚本。
public class GameBase:IGame { public GameBase() { } public void Awake() { Console.WriteLine("Awake"); } public void Start() { Console.WriteLine("Start"); } public void Update() { Console.WriteLine("Update"); } } public class MyGameScript:IGame { public MyGameScript() { } public void Awake() { Console.WriteLine("MyGameScript Awake"); } public void Start() { Console.WriteLine("MyGameScript Start"); } public void Update() { Console.WriteLine("MyGameScript Update"); } }
脚本里面的函数你可以做任意想做的事情,下面开始游戏逻辑了,其实也很简单,就分两步,一个是注册脚本,一个是游戏循环。
class Program { public static List<IGame> scripts = new List<IGame>(); static void Main(string[] args) { RegistScripts(); GameLoop(); } private static void GameLoop() { foreach (var script in scripts) { script.Awake(); } foreach (var script in scripts) { script.Start(); } while (true) { Thread.Sleep(1000); //这里为了演示 控制帧率 foreach (var script in scripts) { script.Update(); } } } private static void RegistScripts() { GameBase gb = new GameBase(); MyGameScript mgs = new MyGameScript(); scripts.Add(gb); scripts.Add(mgs); } }
分析:Unity的内部实现其实不是这样的,这里只是通过自己的方式去实现,最后上一张运行的结果。
二、Unity的实现原理
核心点:
C#脚本+Mono+C++
UnityEngine里的核心类似这么写的:
public class Component { private IntPtr native_handle=IntPtr.Zero; [MethodImpl(MethodImplOptions.InternalCall)] public extern static Component[] GetComponents(); [MethodImpl(MethodImplOptions.InternalCall)] public extern static int get_id_Internal(IntPtr native_handle); [MethodImpl(MethodImplOptions.InternalCall)] public extern static int get_tag_Internal(IntPtr native_handle); public int ID { get { return get_id_Internal(native_handle); } } public int Tag { get { return get_tag_Internal(native_handle); } } }
关键的部分我们都是反编译看不到的,看不到的部分都是通过底层C++去实现的,然后通过Mono进行交互(托管代码与非托管代码之间的交互)。
对应C++代码是这样的:
struct Component { int id; int tag; };
Component* Components; uint32_t num_Components; MonoClassField* native_handle_field; MonoDomain* domain; MonoClass* Component_class; //获取属性 int ManagedLibrary_Component_get_id_Internal(const Component* component) { return component->id; } //获取tag int ManagedLibrary_Component_get_tag_Internal(const Component* component) { return component->tag; } //获取组件 MonoArray* ManagedLibrary_Component_GetComponents() { MonoArray* array = mono_array_new(domain, Component_class, num_Components); for(uint32_t i = 0; i < num_Components; ++i) { MonoObject* obj = mono_object_new(domain, Component_class); mono_runtime_object_init(obj); void* native_handle_value = &Components[i]; mono_field_set_value(obj, native_handle_field, &native_handle_value); mono_array_set(array, MonoObject*, i, obj); } return array; } int main(int argc, char* argv[]) { //------------------------------------------------------------------------------------------------ //例子3:模仿Unity的实现方式 //mono运行时的配置 mono_set_dirs("C:\\Program Files\\Mono\\lib", "C:\\Program Files\\Mono\\etc"); mono_config_parse(NULL); const char* managed_binary_path = "E:\\test\\ManagedLibrary.dll"; //获取应用域 domain = mono_jit_init(managed_binary_path); //加载程序集 MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path); MonoImage* image = mono_assembly_get_image(assembly); //通过Mono的mono_add_internal_call方法将C#中的接口和C/C++中的具体实现关联起来 mono_add_internal_call("ManagedLibrary.Component::get_id_Internal",reinterpret_cast<void*>(ManagedLibrary_Component_get_id_Internal)); mono_add_internal_call("ManagedLibrary.Component::get_tag_Internal",reinterpret_cast<void*>(ManagedLibrary_Component_get_tag_Internal)); mono_add_internal_call("ManagedLibrary.Component::GetComponents",reinterpret_cast<void*>(ManagedLibrary_Component_GetComponents)); //Component.cs Component_class =mono_class_from_name(image,"ManagedLibrary","Component"); native_handle_field =mono_class_get_field_from_name(Component_class,"native_handle"); num_Components =5; Components = new Component[5]; for(uint32_t i = 0; i < num_Components; ++i) { Components[i].id = i; Components[i].tag = i * 5; } // Main.cs MonoClass* main_class = mono_class_from_name(image, "ManagedLibrary", "Main"); const bool include_namespace = true; MonoMethodDesc* managed_method_desc = mono_method_desc_new("ManagedLibrary.Main:TestComponent()", include_namespace); MonoMethod* managed_method = mono_method_desc_search_in_class(managed_method_desc, main_class); mono_method_desc_free(managed_method_desc); //执行 mono_runtime_invoke(managed_method, NULL, NULL, NULL); //释放应用域 mono_jit_cleanup(domain); //释放组件 delete[] Components; return 0; // }
着重理解下核心点“C#脚本+Mono+C++”,明白了这个就简单了。
来自:https://blog.csdn.net/a958832776/article/details/78857406