您的位置:首页 > 移动开发 > Unity3D

Unity3D与Android,iOS交互

2015-12-31 17:34 453 查看

一. 写在前面

最近由于业务需求,对Unity3D与Android,iOS平台交互有所了解,特此记录和分享。

二. 准备工作

1)我使用的Unity版本是4.6.3,eclipse+ADT开发环境,以及Xcode7.2;

2)unity脚本采用C#编写。

三. 开始

2.1 Unity脚本部分

若要在C#脚本中调用OC方法,须引入系统库:

using System.Runtime.InteropServices


要在C#脚本中调用Android部分方法,先定义如下方法:

#if UNITY_ANDROID
public AndroidJavaObject androidContext() {
return new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
}
#endif


获取android当前Activity的引用,然后使用该引用调用其他方法。

获取引用还有一种写法,但个人认为以上方法更具普遍性,故不在这里讨论。

1)调用无参方法

iOS:

对要调用的方法进行定义:

[DllImport ("__Internal")]

private static extern void _showUI();


须注意的是,Internal前面是2个下划线,DllImport后面有个空格。

Android:无须其他操作。

此时,就可以在C#代码中使用该方法:

if (GUILayout.Button ("显示一个对话框", GUILayout.Height (100), GUILayout.Width (400))) {
#if UNITY_IOS
_showUI();
#elif UNITY_ANDROID
androidContext().Call("showUI");
#endif
}


android部分调用,第一个参数为要调用的参数名字,类型为string。

2)调用有参方法

iOS:

对方法进行定义:

[DllImport ("__Internal")]
private static extern void _passParameter(string name,int age);


Android:无须其他操作

在C#中调用该方法:

if (GUILayout.Button ("传递参数", GUILayout.Height (100), GUILayout.Width (400))) {
#if UNITY_IOS
_passParameter("Jerry",24);
#elif UNITY_ANDROID
androidContext().Call("passParameter","Jerry",24);
#endif
}


android部分,方法参数紧跟在方法名称参数之后。

目前应该只支持string 和int类型参数。

3)调用有返回值的方法

iOS:

对方法进行定义:

[DllImport ("__Internal")]
private static extern string _getReturnValue();


Android:无须其他操作

在C#中调用:

if (GUILayout.Button ("有返回值的方法", GUILayout.Height (100), GUILayout.Width (200))) {
string value = "";
#if UNITY_IOS
value = _getReturnValue();
#elif UNITY_ANDROID
value = androidContext().Call<string>("getReturnValue");
#endif
}


留意android部分,有返回值的方法调用,和无返回值的方法略有不同。

4)接收回调信息

iOS:

定义该方法:

[DllImport ("__Internal")]
private static extern void _receiveCallback(string gameObject,string callbackMethod);


Android:无须其他操作

在C#中调用:

if (GUILayout.Button ("有回调的方法", GUILayout.Height (100), GUILayout.Width (200))) {
#if UNITY_IOS
_receiveCallback(this.gameObject.name,"callback");
#elif UNITY_ANDROID
androidContext().Call("receiveCallback",this.gameObject.name,"callback");
#endif
}


往unity部分发送回调信息,需要知道回调方法名称,以及该方法所属的GameObject的名字,因此,从方法参数中传入这2个值。

注:也可以采用其他方式,如在配置文件中配置,或约定方法名称,预制体名称。

2.2 Android部分

在eclipse中新建一个android工程,从unity的安装路径下取出classes.jar放到libs目录下。

classes.jar目录

Mac:Unity.app右键show package contents,然后Contents->PlaybackEngines->AndroidPlayer->developement->bin目录下

Windows:unity安装目录->Editor->Data->PlaybackEngines->AndroidPlayer->development->bin目录下

1)新建一个Activity:

public class MainActivity extends UnityPlayerActivity {

@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
}

public void showUI() {
AlertDialog.Builder builder = new Builder(this);
builder.setMessage("我是个对话框").show();
}

public void passParameter(String name,int age) {
Toast.makeText(this, name + "is" + age, Toast.LENGTH_LONG).show();
}

public String getReturnValue() {
return "hello";
}

public void receiveCallback(String gameObject,String callback) {
UnityPlayer.UnitySendMessage(gameObject, callback, "I am a callback");
}

}


使其继承UnityPlayerActivity,然后实现刚才在C#需要调用的方法;

以上代码简洁清楚,不再赘述。

须注意的是,向Unity部分发送回调信息,使用方法:

UnityPlayer.UnitySendMessage(String,String,String)


第一个参数为接收该信息的预制体GameObject名称,第二个参数为接收信息的方法,该方法所在脚本须与上述GameObject绑定,第三个参数为回调消息,即回调方法的参数。

2)配置AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.leaf.blogdemo"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="18"
android:targetSdkVersion="19" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.leaf.blogdemo.MainActivity"
android:screenOrientation="landscape">
<intent-filter >
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>


3)导出jar包

将所有源码导出成jar包。

4)Unity工程修改

将3)导出的jar包放在Assets->Plugins->Android->bin目录下(目录没有时请创建),若android工程中libs下有引用其他jar包,请放在Android->libs目录下;

将配置好的AndroidManifest.xml文件放在Android目录下;

将assets目录复制到Android目录下;

将res目录复制到Android目录下。

5)导出apk

以上步骤操作无误后,先检查导出环境:

Mac:Unity->Preferences->External Tools->Android SDK Location

Windows:Edit->Preferences->External Tools->Android SDK Location

该配置项设置Android SDK目录。(须注意的是,不同的unity版支持的android sdk不同,若版本不匹配时,可能出现导出错误。)

SDK配置无误后,开始导出:File->Build Settins…



在Player Settings里可对导出的apk属性进行设置,不详述,点击Build,导出apk后,可安装进行测试。

2.3 iOS部分

1)导出Xcode工程

File->Build Settings…选择iOS平台,导出Xcode工程;

2)编写oc方法

在导出的Xcode工程中,新建一个.mm文件,开始coding,实现在C#中调用的方法,方法体需要采用C语言格式:



主要代码如下:

extern "C" {
void _showUI()
{
UIAlertView *view = [UIAlertView new];
view.message = @"我是一个对话框";

[view addButtonWithTitle:@"确定"];
[view dismissWithClickedButtonIndex:0 animated:YES];
[view show];
}

void _passParameter(const char* name,int age)
{
NSLog(@"%@ is %d",cStr2NStr(name),age);
}

const char* _getReturnValue()
{
return MakeStringCopy("hello");
}

void _receiveCallback(const char* gameObject,const char* callback)
{
UnitySendMessage(gameObject, callback, "I am a callback");
}
}


以上代码片段还有一个写法:

@interface iBlogDemo : NSObject

@end

@implementation iBlogDemo

void _showUI()
{
UIAlertView *view = [UIAlertView new];
view.message = @"我是一个对话框";

[view addButtonWithTitle:@"确定"];
[view dismissWithClickedButtonIndex:0 animated:YES];
[view show];
}

void _passParameter(const char* name,int age)
{
NSLog(@"%@ is %d",cStr2NStr(name),age);
}

const char* _getReturnValue()
{
return MakeStringCopy("hello");
}

void _receiveCallback(const char* gameObject,const char* callback)
{
UnitySendMessage(gameObject, callback, "I am a callback");
}

@end


但此时,文件后缀只能为.m,否则会报错。

_showUI方法是利用iOS API显示了一个简单的对话框;

_passParameter须注意的是,c#当中的string对应过来是const char*类型,二者可互相转换:

//convert const char* to NSString
NSString* cStr2NStr(const char* cstr) {
return [[NSString alloc] initWithCString:cstr encoding:NSUTF8StringEncoding];
}


_getReturnValue有返回值的方法,须注意的是,目前只能返回string值,并且,在返回时,需要进行特殊处理:

// By default mono string marshaler creates .Net string for returned UTF-8 C string
// and calls free for returned value, thus returned strings should be allocated on heap
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;

char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}


否则会出现空指针异常。

_receiveCallback在oc中向Unity发送回调信息时,是用于android类似的方法:

UnitySendMessage(const char* obj, const char* method, const char* msg);


各参数意义,与android一样。

3)步骤2)可以提前在导出Xcode工程之前进行

新建一个静态库(或动态库)工程,新建.mm(或.m)文件,实现方法;

#ifdef __cplusplus
extern "C" {
#endif

void UnitySendMessage(const char* obj, const char* method, const char* msg);

#ifdef __cplusplus
}
#endif


源码中的以上声明,其实就是为了在此种方式下,调用该方法而不报错。

在Unity导出的Xcode工程中,该项声明可以在iPhone_target_prefix.h中找到,该文件是Xcode的一个预编译文件。

因此,若按照方法2),其实以上声明可以去掉;

这种方式开发时,可以将编写完成的代码,编译成.a或者.framework文件,或者也可以保留成源码文件。

coding完成后,在Unity工程的Plugins目录下,新建iOS目录,将源文件放在该目录下,然后导出Xcode工程,源文件会出现在Xcode工程的Libraries目录下。

须注意,只能放在iOS下不可嵌套目录,否则无法识别。

若已编译成库文件,则只能在导出Xcode工程后,再引入。

在Unity官方文档中,都有这些说明,扔个链接:Getting Started with iOS Development

4)运行工程,就可以出现与android类似的效果。

unity与iOS存在编码问题,因此中文会乱码,此问题没有在Demo中解决。

三. 其他

有时运行会报找不到libiPhone-lib.a库的错误,这时,修改一下Build Settings里面的Library Search paths即可。

Demo链接地址:Demo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: