首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八)
2012
06-02

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八)

          开始本篇文章之前我先为大家简单的介绍一下Android NDK编程的原理, 我们知道Android开发使用JAVA语言来编程它的运行效率要比C/C++低很多,为了让JAVA语言可以调用 C/C++ 这时候NDK就出现了,使用DNK可以很方便的实现 JAVA 与 C/C++之间的互相调用。NDK的工作原理是使用谷歌提供的NDK工具将C/C++的代码编译成 .so文件,最后使用JAVA代码与.so文件之间相互调用。下面我先说一下在Unity中结合Android NDK实现本地数据的共享的原理,如下图所示 ,Unity工程加入NDK后工程大致可分为三个部分 Android(JAVA) 、 C/C++(.so)、Unity(C#)它们之间是可以相互调用的,在之前的文章中我向大家介绍了 Android与Unity之间相互调用的原理,Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八)。这种方式只能相互传递String字符串,少量数据传递时可以使用这种方式。如果是大量数据这样就有点限制了,其实我们可以把C/C++的代码做为一个中转站,实现两边的数据引用与共享。

 

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 1

 

首先在官网中下载NDK最新的安装包,安装包分为三个版本这里我下载使用的是MAC OS版本,最新版本已经到了R8。

下载地址:http://developer.android.com/sdk/ndk/index.html

OK,我们在Eclipse中创建一个Android工程,在res与src平级文件夹目录下创建一个jni的文件夹。接着在文件夹中创建两个代码文件,分别是C与C++还有一个配置文件Android.mk。 

        c.c代码一共分为两个方法,先说说方法Java_com_xys_UnityTestActivity_TestAddInt 这个方法是漏给Java代码调用的,jint表示此方法的返回值为整形,数据类型还可以是jlong 、jfloat、jdouble、 jobject、jboolean、jbyte、jchar、jshort,搞程序的一看就应该明白了吧?我就不一一解释了。 方法名中java开头是标准用法,com_xys表示当前程序的包名,UnityTestActivity表示当前类,TestAddInt表示方法名,在Android中就是调用这个方法的,这个方法实现了一个简单的整形相加的操作。在说说第二个方法int addInt(),这个方法是留给Unity中C#语言调用的,它的结构与上面不一样不能在Java代码中调用,同样它也就是实现整形相加的操作。

先是C的文件 c.c

#include <string.h>
#include <jni.h>

//Android中java代码调用此方法
jint
Java_com_xys_UnityTestActivity_TestAddInt( JNIEnv* env, jobject thiz ,jint a,jint b)
{
    return addInt(a,b);
}

//Unity中C#代码调用此方法
int addInt(int a, int b)
{
    return a + b;
}

 

在看看C++文件,它和C文件的调用原理差不多,不过值得注意的是C++中一定要把需要调用的方法写在extern “C”{ } 中,否则无法调用。

cplus.cpp

#include <string.h>
#include <jni.h>

//声明一个类
class  MyClass
{
	public:
    static float add(float a, float b)
    {
        return a + b;
    }

};

//外部调用的方法需要写在这里
extern "C"
{

	jfloat
	Java_com_xys_UnityTestActivity_TestAddFloat( JNIEnv* env, jobject thiz ,jfloat a,jfloat b)
	{
		return MyClass::add(a,b);
	}

	float addFloat(float a,float b)
	{
		return MyClass::add(a,b);
	}

}

         再看看第三个配置文件,文件中比较重要的两个变量 LOCAL_MODULE表示生成出的.so的名称 LOCAL_SRC_FILES 表示需要编译的文件,如果是多个C/C++文件中间需要使用   隔开。

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := xuanyusong
LOCAL_SRC_FILES := 
c.c 
cplus.cpp

include $(BUILD_SHARED_LIBRARY)

 OK ,此时C/C++代码的准备工作就做完了,下面我们学习如何把c.c 与 cplus.cpp 一个C文件与一个C++文件一同打包进.so文件中。首先打开终端,cd到刚刚创建的jni目录下,然后执行一开始下载的DNK开发包中的ndk-build命令,你可以直接在android-ndk-r8中找到ndk-build然后拖拽到终端中即可,如果代码没有错误如图所示表示.so文件编译成功。

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 2

 

         再看看当前Android工程的目录结构,libs -> armeabi -> libxuanyusong.so 就是刚刚编译生成的.so文件,xuanyusong.so前面的lib是系统默认添加的,大家不必惊慌。

 

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 3

 

       下面我们编写Java代码,学习如何在java代码中调用C/C++,代码比较简单在OnCreat()方法中分别调用C与C++的方法,并且弹出一个Toast显示在界面中。

UnityTestActivity.java

package com.xys;

import android.os.Bundle;
import android.widget.Toast;

import com.unity3d.player.UnityPlayerActivity;

public class UnityTestActivity extends UnityPlayerActivity
{

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        //在这里调用.so中的两个方法,并且显示在屏幕中
        Toast.makeText(getApplicationContext(), "整形  = " + TestAddInt(1,2)+" 浮点型 = " + TestAddFloat(1.5f, 1.1f),
        	     Toast.LENGTH_LONG).show();

    }   

    //声明c.c中的TestAddInt方法
    public native  int TestAddInt(int a, int b);  

    //声明cplus.cpp中的TestAddFloat方法
    public native  float TestAddFloat(float a, float b);  

    static
	{
    	//加载.so文件
        System.loadLibrary("xuanyusong");
    }  

}

 

     好的,我们终于把Android的工程建立完毕。接着我们需要把工程拷贝至Unity的Android插件中, 插件的制作还有谁不会?? 不会的朋友请看之前的文章,这里就不赘述。如下图所示,Android插件已经制作完毕放在Unity中。目录结构如下所示。

 

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 4

 

         编写test.cs脚本,实现通过C#脚本直接访问libxuanyusong.so文件,直接把test.cs挂在摄像机上。使用[DllImport (“xuanyusong”)]来引入.so 的方法, 这里注意的是 一定要把.so文件名的lib 与后缀.so去掉, 最后将数据通过GUI显示在屏幕中。

test.cs

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class test : MonoBehaviour {

	int i = 0 ;
	float f = 0.0f;

	//引用C 、C++中的方法
    [DllImport ("xuanyusong")]
    private static extern int addInt(int a,int b);
    [DllImport ("xuanyusong")]
	private static extern float addFloat(float a,float b);	

	void Start ()
	{
		//调用方法中相加函数
		i = addInt (1,2);
		f = addFloat (1.0f,2.2f);

	}

	void OnGUI()
	{

		//将相加后的信息显示在屏幕中
		if(i !=0)
		GUILayout.Label(" use  c  =" +  i );

		if(f !=0.0f)
		GUILayout.Label(" use  cplus  =" +  f );
	}
}

 

所有的工作已经做完,我们打包编译上真机,首先是在Android中调用.so时 弹出结果的Toast 。

 

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 5

 

然后是在Unity中调用.so后通过GUI绘制在屏幕中的结果,蛤蛤。Unity给力吧。。

 

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八) - 雨松MOMO程序研究院 - 6

 

最后雨松MOMO希望和大家一起进步,哇咔咔,啦啦啦,~~~本文下载内容包括 Android工程 与 Unity工程,欢迎大家下载学习。,不早了MOMO祝大家学习愉快并且Good night !

下载地址: http://vdisk.weibo.com/s/ab_Ll

 

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之Android NDK编译C/C++结合Unity实现本地数据共享(二十八)》有 76 条评论

  1. www.cocos2dres.com说:

    IOS要使用C++的插件,可以编译成dll吗?我看官方手册上说:For cross platform plugins you must include the .bundle (for Mac), .dll (for Windows), and .so (for Linux) files in the Plugins folder. No further work is then required on your side – Unity automatically picks the right plugin for the target platform and includes it with the player.有谁做过这方面的实验,相烦说下相关经验!

  2. 狄某人说:

    雨松,您好,DllNotFoundException: xuanyusong 是什么原因,我自己写的例子,也报这个错误。

  3. 狄某人说:

    请问,这个问题你解决了吗?我也遇到了,不知道怎么解决,系统是mac

  4. angle说:

    请问您最后是怎么解决的吗?我现在也遇到这个问题了

  5. make money from photos…

    […]that could be the finish of this report. Here you?ll come across some web pages that we think you?ll value, just click the hyperlinks over[…]…

  6. 您好,下载例子后运行提示 DllNotFoundException: xuanyusong 请问这是为什么?

  7. 韩恩鹏说:

    这个例子做出来 是 Unity3d与C 交互 。 java与C 交互。 可是要怎么才能实现Unity和Android交互。。。 除了用作者写的文章(十八)(十九)的方法 还有什么方法捏

  8. 韩恩鹏说:

    这个例子做出来 是 Unity3d与C++交互 。 java与C++交互。 可是要怎么才能实现Unity和Android交互。。。 除了用作者写的文章(十八)(十九)的方法 还有什么方法捏

  9. 韩恩鹏说:

    我打包好在手机上怎么是强退啊 求帮助,还有你是怎么在unity3d上测试的啊?unity3d掉.so捏

  10. 苏琪说:

    在ndk中也可以用UnitySendMessage来和u3d传数据吗

  11. topameng说:

    c# 是虚拟机运行的,可以跨平台,而c/c 编译的是native代码,无法跨平台

  12. topameng说:

    c# 是虚拟机运行的,可以跨平台,而c/c++编译的是native代码,无法跨平台

  13. 这个我还真不知道。 我们使用的是.so文件

  14. Zorro说:

    请问雨松,unity3d调用c/c 的库,只能打包成so吗?打包成dll行吗?我在模拟器上OK,跑到android真机上不行,解包apk后发现,没有把我的dll放到assets目录下,我手动放进去也提示找不到dll,那么,android真机上读取dll的路径应该是哪里呢?多谢啊

  15. Zorro说:

    请问雨松,unity3d调用c/c++的库,只能打包成so吗?打包成dll行吗?我在模拟器上OK,跑到android真机上不行,解包apk后发现,没有把我的dll放到assets目录下,我手动放进去也提示找不到dll,那么,android真机上读取dll的路径应该是哪里呢?多谢啊

  16. 程晓正说:

    雨松,你好 ,我想问一下在安卓平台上用sqlite.db 这种数据库可行?我在U3D的Plugins里面放了 sqlite.dll,libsqlite.so,mono.data.sqlite.dll, mono.data.sqliteclient.dll.然后 如果是在 U3D 的编辑器里选择安卓平台,然后可以正常访问数据。但是如果是放到真机上就会提示
    DllNotFoundException: sqlite3
    07-15 13:27:33.550: INFO/Unity(31157): at (wrapper managed-to-native) Mono.Data.Sqlite.UnsafeNativeMethods:sqlite3_open_ v2 (byte[],intptr&,int,intptr)
    07-15 13:27:33.550: INFO/Unity(31157): at Mono.Data.Sqlite.SQLite3.Open (System.String strFilename, SQLiteOpenFlagsEnum flags, Int32 maxPoolSize, Boolean usePool) [0x00000] in :0
    07-15 13:27:33.550: INFO/Unity(31157): at Mono.Data.Sqlite.SqliteConnection.Open () [0x00000] in :0 ..你知道这种情况怎么处理么。。求教

  17. pengyingh说:

    支持,大好人啊,不过只是初学者,暂时用不到

  18. solq说:

    呵呵,,,产量真快

  19. 自己沙发, 加油哇咔咔!

留下一个回复

你的email不会被公开。 必填项已用 * 标注。