时间:2016-11-21来源:本站原创作者:佚名

前段时间开发UnityAndroid项目的时候需要在UnityC#Script、NativeCode和Java三个模块之间相互调用,因此总结了一下这几种代码之间的调用方法,以防忘记。这篇先看看UnityC#与NativeCode之间的相互调用。

一、UnityC#调用NativeCode

最常见的应该是在游戏的C#脚本中调用NativeCode中的方法,从层级来看可以视为游戏上层调用下层封装的函数,具体实现如下:

1、编写C、C++代码,导出相应函数或变量,下面代码是.h和.cpp中对应的代码:

//InNativeCode.h

#ifndef__NATIVE_CODE__

#define__NATIVE_CODE__

externC

{

floatAddFun(floatx,floaty);

}

#endif

//InNativeCode.cpp

#includeNativeCode.h

externC

{

floatAddFun(floatx,floaty)

{

returnx+y;

}

}

2、编写Android.mk,将mk文件和c、cpp文件放到jni文件夹下,使用NDK(ndk-build命令)将NativeCode编译成.so文件。开发过Android项目的同学应该都非常熟悉Android.mk和Application.mk,这里就不详细介绍了,以下是Android.mk中的内容:

LOCAL_PATH:=$(callmy-dir)

include$(CLEAR_VARS)

LOCAL_MODULE:=NativeEngine

LOCAL_C_INCLUDES:=$(LOCAL_PATH)

LOCAL_SRC_FILES:=NativeCode.cpp

LOCAL_LDLIBS:=-llog-landroid

include$(BUILD_SHARED_LIBRARY)

其中LOCAL_MODULE为模块的名字,生成文件的名字会自动带上lib前缀和.so后缀,比如上面使用ndk编译出的动态库的名字就是libNativeEngine.so;LOCAL_C_INCLUDES和LOCAL_SRC_FILES为头文件和源文件列表,注意路径;LOCAL_LDLIBS为编译模块要使用的附加连接器选项;最后BUILD_SHARED_LIBRARY表示编译成动态库(.so),相反使用LOCAL_STATIC_LIBRARY表示编译成静态库(.a)。这应该是最简单的Android.mk了,实际项目中的Android.mk可能远比这个复杂,例如Nativecode中引用了其他库文件或者启用一些编译优化选项等等。

3、将编译好的libNativeEngine.so文件放入Unity工程Asset\Plugins\Android目录下,如果在Eclipse中调试则放入Android项目下的libs\armeabi-v7a目录下。

4、在Unity中编写C#脚本,引用so文件,声明导出的接口,代码如下:

//InUnityC#Script

usingUnityEngine;

usingSystem.Collections;

usingSystem.Runtime.InteropServices;

publicclassCallNativeCode:MonoBehaviour

{

[DllImport(NativeEngine)]

publicstaticexternfloatAddFun(floatx,floaty);

voidStart()

{

floataddResult=AddFun(2.5f,8.8f);

Debug.Log(Addresultis:+addResult);

}

voidUpdate()

{

}

}

通过DllImport引用Native模块的名字,注意需要去掉lib前缀和.so后缀,当然DllImport还包含一些缺省参数,这里就不一一介绍了,从字面上也很好理解:

5、通过Unity编译打包生成apk,安装并运行,使用adblogcat命令或者Eclipse的LogCat查看结果,下图可以看到UnityC#调进了NativeCode的函数:

方法很简单,但是也有一些需要注意的地方:

(1)如果程序提示找不到函数入口点,请查看导出的函数名字是否和声明的名字一致,如果不使用extern“C”,g++会对导出的函数进行签名,导出的接口除了函数名字还包含一些其他的字符。此时可以将DllImport的EntryPoint设置为包含其他字符的函数名字,或者在Nativecode导出时加上extern“C”,使导出的函数名和声明的函数名一致。Tips:extern“C”告诉链接器在链接的时候用C函数规范来链接,主要原因是C++和C程序编译完成后在目标代码中命名规则不同。

(2)最常见的问题应该是函数参数的传递上了,传值的还好,native层不会去修改传入的参数,但如果需要native层修改C#传入的参数就应该设置为传引用:

//InNativeCode.cpp

#includeNativeCode.h

externC

{

floatAddRefFun(float*x,float*y)

{

*x=3.2;

*y=2.4;

return*x+*y;

}

}

//InUnityC#Script

usingUnityEngine;

usingSystem.Collections;

usingSystem.Runtime.InteropServices

publicclassCallNativeCode:MonoBehaviour

{

[DllImport(NativeEngine)]

publicstaticexternfloatAddRefFun(reffloatx,reffloaty);

voidStart()

{

floatx=2.5f;

floaty=8.8f;

floataddResult=AddRefFun(refx,refy);

Debug.Log(Xis:+x);

Debug.Log(Yis:+y);

Debug.Log(Addresultis:+addResult);

}

voidUpdate()

{

}

}

结果可以看出,Nativecode修改了C#传入的参数:

最后总结了一下C#和Nativecode之间常见不同参数类型的调用方法:

a、参数为基本类型,如int,float,char等:

//InNativeCode

voidFun(intvalue);

voidFun(floatvalue);

voidFun(charch);

//InUnityC#Script

[DllImport(xxx)]

publicstaticexternvoidFun(Int32value);

[DllImport(xxx)]

publicstaticexternvoidFun(floatvalue);

[DllImport(xxx)]

publicstaticexternvoidFun(charch);

b、参数为基本指针类型,如int*,float*,char*等:

//InNativeCode

voidFun(int*value);

voidFun(float*value);

voidFun(char*ch);

//InUnityC#Script

[DllImport(xxx)]

publicstaticexternvoidFun(refInt32value);

[DllImport(xxx)]

publicstaticexternvoidFun(reffloatvalue);

Nativecode参数为char*,C#有多种声明方式:

[DllImport(xxx)]

publicstaticexternvoidFun(stringch);//ch的内容不会被更改

[DllImport(xxx)]

publicstaticexternvoidFun(StringBuilderch);//ch的内容可以被更改

c、参数为结构体,C#中一般使用MarshalAs属性来指示如何在托管代码和非托管代码之间传递数据(注意:定义结构体时应该考虑字节对其的问题):

//InNativeCode

structstEvent

{

intvalue;//基本类型

charch;//基本类型

intnumber[];//数组

charbuffer[];//字符串数组

};

voidFun(stEventtEvent);

//InUnityC#Script

[StructLayout(LayoutKind.Sequential)]

publicstructstEvent

{

publicInt32value;

publiccharch;

[MarshalAs(UnmanagedType.ByValArray,SizeConst=)]

publicInt32[]number;

[MarshalAs(UnmanagedType.ByValTStr,SizeConst=)]

publicchar[]buffer;

}

[DllImport(xxx)]

publicstaticexternvoidFun(stEventtEvent);

d、参数为结构体指针

//InNativeCode

voidFun(stEvent*ptEvent);

//InUnityC#Script

[DllImport(xxx)]

publicstaticexternvoidFun(refstEventptEvent);

二、NativeCode调用UnityC#

通常在NativeCode完成了某个任务后需要及时通知给游戏,从层级来看可以视为下层封装的函数同步回调至游戏上层,实现思路如下:

我们知道C++中有函数指针回调的概念,通过函数指针可以实现函数回调,C#是托管语言,没有指针的概念,但是有个和函数指针很类似的委托(delegate),我们可以在C#中定义一个委托,并将这个委托设置进NativeCode,这样调用Native的函数指针就相当于直接调用上层C#的函数了,直接看代码:

//InNativeCode.h

#ifndef__NATIVE_CODE__

#define__NATIVE_CODE__

externC

{

typedefvoid(*CALLBACK)(constchar*);

staticCALLBACKcallback;//Native层的全局回调指针

voidSetCallBackAndRun(CALLBACKcb);

}

#endif

//InNativeCode.cpp

#includeNativeCode.h

externC

{

voidSetCallBackAndRun(CALLBACKcb)

{

callback=cb;//cb相当于C#中的函数地址

callback(HelloWorld!);

}

}

//InUnityC#Script

usingUnityEngine;

usingSystem.Collections;

usingSystem.Runtime.InteropServices;

publicclassCallNativeCode:MonoBehaviour

{

publicdelegatevoidcallbackDelegate(stringstr);

[DllImport(NativeEngine)]

publicstaticexternvoidSetCallBackAndRun(callbackDelegatecb);

voidStart()

{

SetCallBackAndRun(newcallbackDelegate(PrintString));

}

voidPrintString(stringstr)

{

Debug.Log(CallPrintString);

Debug.Log(str);

}

voidUpdate()

{

}

}

从LogCat看到,NativeCode通过callback成功回调到了C#中的PrintString方法中:

先写到这,下次再来总结UnityC#和Java之间的调用吧。

Gad给大家拜年啦!

Gad-GameDev∣腾讯游戏开发者平台

长按,识别







































北京都有哪些白癜风医院
北京那个医院治白癜风最好

转载请注明原文网址:http://www.helimiaopu.com/cxys/1567.html

------分隔线----------------------------