一 為什么需要動態庫
1)提供原生代碼(native code)的支持,也叫原生插件,但是我實踐的是c/cpp跨平台動態庫,這里不具體涉及安卓平台java庫和ios平台的objectc庫構建。
2)某些開源庫是c/cpp編寫,沒有對應c#版本
3)或者有c#版本實現,但是效率或者gc達不到期望效果,特別是GC,一般的開源庫c#版本的作者,對gc優化得大多不好
4)追求效率,比如A*尋路等開銷比較大的算法,想做下優化
5)某些模塊,如網絡模塊,需要Unity客戶端和后端跑一份相同的邏輯代碼,而不想維護兩份語言的實現
二 示例demo選擇
前陣子由於項目需要,編譯了一下kcp庫,這里拿kcp編譯到Unity的各平台動態庫來做一次總結,kcp的github地址:https://github.com/skywind3000/kcp。
有關這個庫的其它信息這里不做介紹,它本質就是一個可靠UDP的網絡庫,我們項目是用在了一款多人實時射擊類游戲中。
三 編譯工具選擇
這里使用的是CMake,官網地址為:https://cmake.org/。有關CMake的使用資料自行網絡獲取,我這里只說一點,這是一個跨平台的構建工具,針對不同的平台,只需要一份描述文件,很方便,不需要每個平台去寫makefile。
四 示例demo選擇
其中:
1)cmke目錄:包含CMake在安卓、iOS平台進行構建時需要要到的兩個文件:android.toolchain.cmake、iOS.cmake
2)Plugins目錄:各平台構建的輸出目錄,構建完成后可以直接放置到Unity項目Assdets目錄下使用
3)CMakeLists.txt文件:主要要由我們自己編寫的一個文件,cmake根據CMakeLists生成各個平台編譯的中間文件以及makefile文件
4)目標庫源代碼:kcp.h、kcp.c
5)make_xxx:各平台執行構建腳本文件,這些文件基本是固定的,不需要做什么修改
五 項目簡要說明
1、kcp項目需要做的調整
kcp.c不需要動,kcp.h修改部分如下:
1 #ifdef DLL_EXPORTS 2 #define KCPDLL _declspec(dllexport) 3 #else 4 #define KCPDLL 5 #endif 6 7 #ifdef __cplusplus 8 extern "C" { 9 #endif 10 11 //--------------------------------------------------------------------- 12 // interface 13 //--------------------------------------------------------------------- 14 15 // create a new kcp control object, 'conv' must equal in two endpoint 16 // from the same connection. 'user' will be passed to the output callback 17 // output callback can be setup like this: 'kcp->output = my_udp_output' 18 KCPDLL ikcpcb* ikcp_create(IUINT32 conv, void *user); 19 //其它導出接口省略 20 21 #ifdef __cplusplus 22 } 23 #endif
2、CMakeLists文件編寫
1 cmake_minimum_required(VERSION 2.8) 2 3 if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) ) 4 set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "") 5 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "") 6 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT" CACHE STRING "") 7 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd" CACHE STRING "") 8 endif () 9 10 project(kcp) 11 12 if ( IOS ) 13 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode") 14 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode") 15 endif () 16 17 find_path(KCP_PROJECT_DIR NAMES SConstruct 18 PATHS 19 ${CMAKE_SOURCE_DIR} 20 NO_DEFAULT_PATH 21 ) 22 23 MARK_AS_ADVANCED(KCP_PROJECT_DIR) 24 25 set ( KCP_CORE 26 kcp.c 27 ) 28 29 macro(source_group_by_dir proj_dir source_files) 30 if(MSVC OR APPLE) 31 get_filename_component(sgbd_cur_dir ${proj_dir} ABSOLUTE) 32 foreach(sgbd_file ${${source_files}}) 33 get_filename_component(sgbd_abs_file ${sgbd_file} ABSOLUTE) 34 file(RELATIVE_PATH sgbd_fpath ${sgbd_cur_dir} ${sgbd_abs_file}) 35 string(REGEX REPLACE "\(.*\)/.*" \\1 sgbd_group_name ${sgbd_fpath}) 36 string(COMPARE EQUAL ${sgbd_fpath} ${sgbd_group_name} sgbd_nogroup) 37 string(REPLACE "/" "\\" sgbd_group_name ${sgbd_group_name}) 38 if(sgbd_nogroup) 39 set(sgbd_group_name "\\") 40 endif(sgbd_nogroup) 41 source_group(${sgbd_group_name} FILES ${sgbd_file}) 42 endforeach(sgbd_file) 43 endif(MSVC OR APPLE) 44 endmacro(source_group_by_dir) 45 46 source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} KCP_CORE) 47 48 if (APPLE) 49 if (IOS) 50 set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") 51 add_library(kcp STATIC 52 ${KCP_CORE} 53 ) 54 else () 55 set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_64_BIT)") 56 add_library(kcp MODULE 57 ${KCP_CORE} 58 ) 59 set_target_properties ( kcp PROPERTIES BUNDLE TRUE ) 60 #set_target_properties ( kcp PROPERTIES FRAMEWORK TRUE ) 61 #set_target_properties ( kcp PROPERTIES MACOSX_RPATH TRUE ) 62 endif () 63 else ( ) 64 add_library(kcp SHARED 65 ${KCP_CORE} 66 ) 67 endif ( ) 68 69 if ( WIN32 AND NOT CYGWIN ) 70 target_compile_definitions (kcp PRIVATE DLL_EXPORTS) 71 endif ( )
1)第10行:指定項目名稱為kcp
2)第26行:指定要編譯的c/cpp文件
3)第70行:指定預定義宏DLL_EXPORTS,這個宏只有在window平台編譯dll動態庫才需要,其它平台不需要
4)其它沒什么好說的,在構建你自己的項目時,只需要注意的帶有“kcp”字樣的地方替換為你自己對應的項目名稱即可。
3、各平台構建腳本
1)window32位系統:make_win32.bat
1 mkdir build32 & pushd build32 2 cmake -G "Visual Studio 14 2015" .. 3 popd 4 cmake --build build32 --config Release 5 md Plugins\x86 6 copy /Y build32\Release\kcp.dll Plugins\x86\kcp.dll 7 rmdir /S /Q build32 8 pause
2)windows64位系統:make_win64.bat
1 mkdir build64 & pushd build64 2 cmake -G "Visual Studio 14 2015 Win64" .. 3 popd 4 cmake --build build64 --config Release 5 md Plugins\x86_64 6 copy /Y build64\Release\kcp.dll Plugins\x86_64\kcp.dll 7 rmdir /S /Q build64 8 pause
3)linux32位系統:make_linux32.sh
1 mkdir -p build_linux32 && cd build_linux32 2 cmake -DCMAKE_C_FLAGS=-m32 -DCMAKE_CXX_FLAGS=-m32 -DCMAKE_SHARED_LINKER_FLAGS=-m32 ../ 3 cd .. 4 cmake --build build_linux32 --config Release 5 cp build_linux32/kcp.so Plugins/x86/kcp.so 6 rm -rf build_linux32
4)linux64位系統:make_linux64.sh
1 mkdir -p build_linux64 && cd build_linux64 2 cmake ../ 3 cd .. 4 cmake --build build_linux64 --config Release 5 cp build_linux64/kcp.so Plugins/x86_64/kcp.so 6 rm -rf build_linux64
5)Mac系統:make_osx.sh
1 mkdir -p build_osx && cd build_osx 2 cmake -GXcode ../ 3 cd .. 4 cmake --build build_osx --config Release 5 mkdir -p Plugins/kcp.bundle/Contents/MacOS/ 6 cp build_osx/Release/kcp.bundle/Contents/MacOS/kcp Plugins/kcp.bundle/Contents/MacOS/kcp 7 rm -rf build_osx
6)android系統:make_android.sh
1 if [ -z "$ANDROID_NDK" ]; then 2 export ANDROID_NDK=~/android-ndk-r10e 3 fi 4 5 mkdir -p build_v7a && cd build_v7a 6 cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang3.6 -DANDROID_NATIVE_API_LEVEL=android-9 ../ 7 cd .. 8 cmake --build build_v7a --config Release 9 mkdir -p Plugins/Android/libs/armeabi-v7a/ 10 cp build_v7a/libkcp.so Plugins/Android/libs/armeabi-v7a/libkcp.so 11 rm -rf build_v7a 12 13 mkdir -p build_x86 && cd build_x86 14 cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME=x86-clang3.5 -DANDROID_NATIVE_API_LEVEL=android-9 ../ 15 cd .. 16 cmake --build build_x86 --config Release 17 mkdir -p Plugins/Android/libs/x86/ 18 cp build_x86/libkcp.so Plugins/Android/libs/x86/libkcp.so 19 rm -rf build_x86
7)iOS系統:make_ios.sh
1 mkdir -p build_ios && cd build_ios 2 cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake -GXcode ../ 3 cd .. 4 cmake --build build_ios --config Release 5 mkdir -p Plugins/iOS/ 6 exist_armv7=`lipo -info build_ios/Release-iphoneos/libkcp.a | grep armv7 | wc -l` 7 exist_arm64=`lipo -info build_ios/Release-iphoneos/libkcp.a | grep arm64 | wc -l` 8 if [ $[exist_armv7] -eq 0 ]; then 9 echo "** ERROR **: No support for armv7, maybe XCode version is to high, use manual_build_ios instead!" 10 elif [ $[exist_arm64] -eq 0 ]; then 11 echo "** ERROR ** : No support for arm64, maybe XCode version is to high, use manual_build_ios instead!" 12 else 13 cp build_ios/Release-iphoneos/libkcp.a Plugins/iOS/libkcp.a 14 rm -rf build_io 15 fi
8)Windows Phone系統:make_uwp.bat
1 mkdir build_uwp & pushd build_uwp 2 cmake -G "Visual Studio 14 2015" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 .. 3 popd 4 cmake --build build_uwp --config Release 5 md Plugins\WSA\x86 6 copy /Y build_uwp\Release\kcp.dll Plugins\WSA\x86\kcp.dll 7 rmdir /S /Q build_uwp 8 9 mkdir build_uwp64 & pushd build_uwp64 10 cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 .. 11 popd 12 cmake --build build_uwp64 --config Release 13 md Plugins\WSA\x64 14 copy /Y build_uwp64\Release\kcp.dll Plugins\WSA\x64\kcp.dll 15 rmdir /S /Q build_uwp64 16 17 mkdir build_uwp_arm & pushd build_uwp_arm 18 cmake -G "Visual Studio 14 2015 ARM" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0 .. 19 popd 20 cmake --build build_uwp_arm --config Release 21 md Plugins\WSA\ARM 22 copy /Y build_uwp_arm\Release\kcp.dll Plugins\WSA\ARM\kcp.dll 23 rmdir /S /Q build_uwp_arm 24 25 pause
9)這些腳本都很簡單明了,像需要的VS版本、安卓的NDK版本等等,一看便知。要移植到你自己的構建項目也很簡單,基本只需要修改項目名稱即可。
六 各平台構建環境簡要說明
1)以下各平台,均需要安裝CMake,安裝教程,包括各平台環境變量等的配置自行谷歌,很多相關帖子。
2)window系統和windows phone系統:在window系統中執行對應的.bat腳本即可,windows phone的編譯需要相關SDK。本帖使用VS2015,windows phone暫時不需要,所以沒有構建測試過。
3)linux系統:在linux系統中執行對應的.sh腳本即可
4)mac系統:在mac系統中執行對應的.sh腳本即可
5)iOS系統:在mack系統中執行對應的.sh腳本即可。需要xcode版本8.1,版本太高會有問題,可以進行手動構建,參考博文:iOS代碼封裝成.a文件(封裝SDK)。真機上的.a動態庫需要同時支持armv7,ram64架構,查看你編譯出來的.a是否是正確架構可使用lipo -info kcp.a
6)android系統:安卓系統可以在各個平台編譯,我這里是在linux系統中編譯的,執行對應的.sh腳本即可,andorid SDK版本為:android-ndk-r10e。關於SDK安裝以及環境變量配置自行谷歌。
7)可能根據各位自己的情況,編譯過程會遇到各種小問題,但是這些問題幾乎都是出在環境上,如環境變量、SDK版本、xcode版本等,我這里已經對環境信息給了足夠的說明,如果你實踐當中問題解決不了,可以參考我這里列舉的環境。
8).sh文件在window下編輯可能會存在文件結束符問題,使用doc2unix命令轉換就好,遇到權限問題使用chmod命令就好,其它可能遇到的細節問題自己耐心多多摸索,問題都不大。
七 Unity項目動態庫配置簡要說明
1、輸出目錄結構
由於我們公司全部在window下開發,所以沒有編譯linux下的動態庫,如果你們需要,將在x86、x86_64目錄下存在libkcp.so文件。同樣,window phone平台也沒實測。
2、window平台
x86、x86_64下的.dll文件:
3、Mac平台
kcp.bundle,配置時選擇osx系統,任意cpu
4、linux平台
x86、x86_64下的.so文件,相應配置
5、andorid平台
Android子目錄下的.so文件,選擇Android Platform,cpu架構做對應勾選,很簡單,不再截圖
6、iOS平台
選擇iOS Platform
八 動態庫項目使用
編寫對應kcp.cs文件,聲明外部函數,腳本如下:
1 namespace KCP 2 { 3 using System; 4 using System.Runtime.InteropServices; 5 6 public class kcp 7 { 8 #if UNITY_IPHONE && !UNITY_EDITOR 9 const string KcpDLL = "__Internal"; 10 #else 11 const string KcpDLL = "kcp"; 12 #endif 13 14 [DllImport(KcpDLL, CallingConvention=CallingConvention.Cdecl)] 15 public static extern uint ikcp_check(IntPtr kcp, uint current);、 16 //其它接口省略 17 } 18 }
1)函數調用約定指明為cdecl
2)所有接口以靜態外部函數導出
3)DllIport在iOS上和其它平台導出使用的參數不一樣
4)在構建你自己的動態庫時依葫蘆畫瓢就好,沒啥太多要說的
九 kcp_build完整構建工程
github地址:https://github.com/smilehao/kcp_bulild