Unity游戏状态脚本控制思考

发表于2018-10-19
评论0 4.5k浏览
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

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引