zoukankan      html  css  js  c++  java
  • Unity使用C++作为游戏逻辑脚本的研究

    文章申明:本文来自JacksonDunstan的博客系列文章内容摘取和翻译,版权归其所有,附上原文的链接,大家可以有空阅读原文:C++ Scripting( in Unity)

    一、C#和C++的通信

    前面我的文章写过c#/c/lua是如何交互的,通过将c#的函数和属性,注册到lua虚拟机中,可以实现通过c来互相交互。

    而c#和c++的交互,也是非常类似的,c#可以直接的通过P/Invoke的方式来调用c++的函数,而C++调用C#的函数,C++的函数是被封装成DLL来放在Unity的工程文件中的Plugins中,则需要基于.NET来操作,利用Marshal.GetFunctionPointerForDelegate来获取函数的指针,然后传递到c++中进行操作。

    二、编辑器下实现实时的编译和脚本更新

    在Unity中,我们可以在打开的Unity中,直接编译c#的文件,这样不需要每次都关闭工程再打开来执行编译,而C++由于通过DLL来调用,每次更新的C++都需要关闭工程,然后更新DLL,然后打开工程,这样的操作,对于编辑器下的开发是极其耗费的。

    对于上面提到的反复开关工程执行DLL的更新,可以利用[DllImport]的属性来实现在编辑器下的更新:

    该属性是基于OS的,所以不会存在跨平台的问题。

    三、示例代码展示

    show the code

    c# code part:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using UnityEngine;
    
    class TestScript:MonoBehaviour
    {
    #if UNITY_EDITOR
        // pointer handle to the C++ DLL
        public IntPtr libarayHandle;
        public delegate void InitDelegate(IntPtr gameObjectNew,
        IntPtr gameObjectGetTransform, IntPtr transformSetPosition);
    #endif
    }
    
    #if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
        //OSX 和Linux下的导入
        [DLLImport("__Internal")]
        public static extern IntPtr dlopen(string path, int flag);
        [DllImport("__Internal")]
        public static extern IntPtr dlsym(IntPtr handle, string symbolName);
        [DllImport("__Internal")]
        public static extern int dlclose(IntPtr handle);
    
        public static IntPtr OpenLibrary(string path)
        {
            IntPtr handle = dlopen(path, 0);
            if(handle == IntPtr.Zero)
            {
               throw new Exception("Couldn't open native library: "+ path);
            }
            return handle;
        }
        
        public static void CloseLibrary(IntPtr libraryHandle)
        {
             dlclose(libraryHandle);
        }
        
        public static T GetDelegate<T>(IntPtr libraryHandle, string functionName)
        where T: class
        {
             IntPtr symbol = dlsym(libraryHandle, functionName);
             if(symbol == IntPtr.Zero)
             {
                throw new Exception("Couldn't get function:" + functionName); 
             }
             return Marshal.GetDelegateForFunctionPointer(symbol, typeof(T)) as T;
        }
    #elif UNITY_EDITOR_WIN
        // win 编辑器下
        [DllImport("kernel32")]
        public static extern IntPtr LoadLibrary(string path);
    
        [DllImport("kernel32")]
        public static extern IntPtr GetProcAddress(IntPtr libraryHandle, 
        string symbolName);
         
        [DllImport("kernel32)]
        public static extern bool FreeLibrary(IntPtr libraryHandle);
    
        public static IntPtr OpenLibrary(string path)
        {
            IntPtr handle = LoadLibrary(path);
            if(handle == IntPtr.Zero)
            {
               throw new Exception("Couldn't open native library: "+ path);
            }
            return handle;
        }
        
        public static void CloseLibrary(IntPtr libraryHandle)
        {
             FreeLibrary(libraryHandle);
        }
        
        public static T GetDelegate<T>(IntPtr libraryHandle, string functionName) 
        where T: class
        {
             IntPtr symbol = GetProcAddress(libraryHandle, functionName);
             if(symbol == IntPtr.Zero)
             {
                throw new Exception("Couldn't get function:" + functionName); 
             }
             return Marshal.GetDelegateForFunctionPointer(symbol, typeof(T)) as T;
        }
    #else
        //本地加载
        [DllImport("NativeScript")]
        static extern void Init(IntPtr gameObjectNew,
        IntPtr gameObjectGetTransform, IntPtr transformSetPosition);
    
        [DllImport("NativeScript")]
        static extern void MonoBehaviourUpdate();
    #endif
    
        delegate int GameObjectNewDelegate();
        delegate int GameObjectGetTransformDelegate(int thisHandle);
        delegate void TransformSetPositionDelegate(int thisHandle, Vector3 position);
    
    #if UNITY_EDITOR_OSX
        const string LIB_PATH = "/NativeScript.bundle/Contents/MacOS/NativeScript";
    #elif UNITY_EDITOR_LINUX
        const string LIB_PATH = "/NativeScript.so";
    #elif UNITY_EDITOR_WIN
        const string LIB_PATH = "/NativeScript.dll";
    #endif
    
        void Awake()
       {
    #if UNITY_EDITOR
         //open the native library
         libraryHandle = OpenLibrary(Application.dataPath + LIB_PATH);
         InitDelegate Init = GetDelegate<InitDelegate>(libraryHandle, "Init");
         MonoBehaviourUpdate = GetDelegate<MonoBehaviourUpdateDelegate>(
         libraryHandle,"MonoBehaviourUpdate");
    #endif
    
         //init the C++ Library
         ObjectStore.Init(1024);
         Init(
         Marshal.GetFunctionPointerForDelegate(new GameObjectNewDelegate(GameObjectNew)),
         Marshal.GetFunctionPointerForDelegate(new GameObjectGetTransformDelegate(GameObjectGetTransform)),
         Marshal.GetFunctionPointerForDelegate(new TransformSetPositionDelegate(TransformSetPosition))
        );
    
        }
       
        void Update()
        {
            MonoBehaviourUpdate();
        }
    
        void OnApplicationQuit()
        {
    #if UNITY_EDITOR
           CloseLibrary(libraryHandle);
           libraryHandle = IntPtr.Zero;
    #endif
        }
    
        //c# function for c++ call
        static int GameObjectNew()
        {
            GameObject go = new GameObject();
            return ObjectStore.Store(go);
        }
       
        static int GameObjectGetTransform(int thisHandle)
        {
            GameObject go = (GameObject)ObjectStore.Get(thisHandle);
            Transform transform = go.transform;
             return ObjectStore.Store(transform);
        }
    
        static void TransformSetPosition(int handle, Vector3 position)
        {
             Transform t =(Transform)ObjectStore.Get(handle);
             t.position = position;
        }
    }

    c++ code part:

    #ifdef _WIN32
       #define DLLEXPORT __declspec(dllexport)
    #else
       #define DLLEXPORT
    #endif
    
    extern "C"
    {
        //C# VECTOR STRUCT
       struct Vector3
       {
           float x;
           float y;
           float z;
       }
       //c# function for c++ to call
       int(*GameObjectNew)();
       int(*GameObjectGetTransform)(int thisHandle);
       void(*TransformSetPosition)(int thisHandle, Vector3 position);
    
       //c++ functions for c# to call
       int numCreated;
      
       DLLExport void Init(
       int(*gameObjectNew)(),
       int(*gameObjectGetTrasform)(int),
       void(*transformSetPosition)(int, Vector3)
    )
    {
          GameObjectNew = gameObjectNew;
          GameObjectGetTransform = gameObjectGetTransform;
          TransformSetPosition = trasformSetPosition;
    
          numCreated = 0;
    }
        //
        DLLEXPORT void MonoBehaviourUpdate(int thisHandle)
        {
            if( numCreated < 10)
            {
                 //获取函数handle,然后操作
                 int goHandle = GameObjectNew();
                 int transformHandle = GameObejctGetTransform(goHandle);
                 float comp = 10.0f * (float)numCreated;
                 Vector3 position = {comp, comp, comp};
                 TransformSetPosition(transformHandle, position);
                 numCreated++;
            }
        }
    }

    四、总结

    C#和C++的相互交互,是基于.NET和P/Invoke,那么我们可以同理退出c#和lua的操作,其实质就是对handle进行包装,然后进行相关的操作,这个在后续的文章中在研究,先写到这儿,祝大家五一快乐,我也回家过节去了,哈哈~

  • 相关阅读:
    用react的ReactCSSTransitionGroup插件实现简单的弹幕动画
    composer安装yii2问题总结
    记阿里笔试经历
    JVM, JRE,JDK 的区别
    HTML
    Http协议
    操作系统和网络基础知识
    网络基础之网络协议
    计算机硬件知识
    计算机硬件历史
  • 原文地址:https://www.cnblogs.com/zblade/p/8961446.html
Copyright © 2011-2022 走看看