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進行包裝,然后進行相關的操作,這個在后續的文章中在研究,先寫到這兒,祝大家五一快樂,我也回家過節去了,哈哈~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM