各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。在前一篇文章中,我们研究了Android平台上Unity3D的手势操作并在之前的基础上实现了手势旋转、放缩等功能。今天呢,我们继续来研究Unity在Android平台上扩展的内容。众所周知,Unity3D是一个强大的跨平台游戏引擎,和大多数喜欢Unity的朋友一样,博主在体验了Unity强大的跨平台能力后,被深深地震撼了,试想曾经我们假设要开发一款游戏的话,我们须要对DX、OpenGL等图形库有足够的了解,因为游戏对性能的严格要求,诸如Unreal、CryEngine等顶级的商业游戏引擎通常都是使用C/C++这种底层的语言来开发,使得很多开发人员望而却步。而此时此刻,得益于Unity3D强大的跨平台能力,博主能够用自己喜欢的C#语言来编写游戏,这无疑说明博主和成千上百使用Unity的开发人员一样,我们正处于一个商业化引擎逐渐成熟的时代,作为开发人员的我们是幸福的。
记得"仙剑之父“姚壮宪作为评委參加Unity亚洲区的比赛时曾经感慨道:"我学生时也是痴迷于自己不断钻研游戏开发,从各种小游戏和小工具做起,并不断的回头优化改良曾经的作品,积累经验技巧。那时候没有商业引擎能够用,须要自己给自己做底层引擎。如今的年轻人是幸福的,有Unity这样向大众公开的引擎能够学习和使用,能够把精力很多其他地专注在游戏作品本身的创作,更应该把握这种环境"。所以相比游戏界的前辈们用QBasic来写《仙剑奇侠传》这种游戏作品,我们这代人无疑要幸福得多,但是我们这代人身上的担子却并不轻啊。如今的时代是一个多元化的时代,不管是在应用开发领域还是游戏开发领域,目标用户平台多样化成为我们不得不去面对的问题。以眼下国内的移动手机平台为例,主流的移动手机平台就有IOS、Android、Window Phone三种。虽然从理论上来讲,我们能够不用写一行Object-C或者Java代码就能够开发出IOS、Android上的应用,但是假设我们须要和该平台的某些接口进行交互的时候,我们就不得不考虑Unity和这些平台的对接。举一个最为常见的样例,大家都知道在移动平台上流行一种应用内付费的模式,但是Unity并没有为我们提供对应的API,在IOS平台上因为苹果应用商店的唯一合法性,我们想在该平台上实现应用内付费就必须使用苹果官方提供的SDK,而苹果官方主推Object-C,所以我们不可避免地要和Object-C打交道。相同地,在Android平台上因为Android的开源导致Android设备的多样性、应用商店的多样性,我们选择将自己的应用上架的时候,相同面临着和Java或者C++(JNI)打交道的问题。所以,我们今天就来研究下Unity和其他平台的交互调用的问题,因为博主手头上仅仅有一部用了非常长时间的Android手机,所以我们今天就以Android平台为例,探讨下Unity和Android的交互吧!
Unity和Android交互通常有两种方式:
1、Unity调用为Android平台编写的插件
2、将Unity项目导出为Android项目,然后编写Android程序
这两种方式在实际的应用中各有优劣,我们今天先来解说第一种方法,另外一种方法博主稍后再和大家分享。首先来说说第一种方法的原理,我们首先用Eclipse编写一个Java的库文件(.Jar),在这个库文件里我们会封装一系列的方法来为Unity提供接口,我们将这个库文件导出后能够将其放置到一个特定的文件夹下(Plugins/Android),然后我们就能够利用Unity提供的API来调用这些方法。好了,以下我们来看详细的过程吧,首先我们创建一个Android项目,并将其设为一个库,这里将其包名设为com.android.android2unity,这个包名非常重要,我们在Unity中将用到这个包名。我们接下来在MainActivity.java这个类中编写代码,它将作为我们封装Android API的一个类。在编写代码之前,让我们来做这样一件事情,将位于D:Program FilesUnityEditorDataPlaybackEnginesandroidplayer eleaseinclasses.jar(不同的计算机上,这个位置可能会有所不同,大家依照自己的路径加入就可以)这个库加入到我们的项目中来,如图:
这个库是Unity为Android提供的一个库,主要提供了支持该平台的Player,详细的大家能够自己去查看它的类空间。好了,我们以下编写这样一个脚本:
package com.android.android2unity; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Vibrator; import android.widget.Toast; /* 引入Unity的包 */ import com.unity3d.player.UnityPlayerActivity; import com.unity3d.player.UnityPlayer; /* 假设须要Activity与Unity对接,能够通过继承UnityPlayerActivity来实现 */ /* 我们须要重写Activity的相关方法,在此节代码中,我们仅仅须要调用Android API */ public class MainActivity extends UnityPlayerActivity { //当前上下文 private Context mContext=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //初始化上下文 mContext=this; } /* 定义一个调用Unity方法的方法 ,基于UnitySendMessage实现 。因为当前Activity没有採用 */ /* Android的布局文件,所以我们无法使用Android的事件来完毕这种方法的调用,我们 */ /* 採用Unity调用的方法,虽然这样显得舍近求远,但是我们知道了怎样在Android中调用 */ /* Unity中定义的方法 */ public void InvokeUnity(String mStr) { UnityPlayer.UnitySendMessage("Vabille","SetCameraColor", ""); } /* 定义一个打开Activity的方法,我们将在Unity中调用此方法 */ public void StartWebView(String mUrl) { //创建一个Intent以打开一个新的Activity Intent intent=new Intent(mContext,WebActivity.class); //传入一个URL intent.putExtra("URL", mUrl); //打开Activity this.startActivity(intent); } /* 定义一个显示对话框的方法,我们将在Unity中调用此方法 */ public void ShowDialog(final String mTitle,final String mContent) { /* 在UI线程下执行相关方法 */ runOnUiThread(new Runnable() { @Override public void run() { //创建Builder AlertDialog.Builder mBuilder=new AlertDialog.Builder(MainActivity.this); //创建对话框 mBuilder.setTitle(mTitle) .setMessage(mContent) .setPositiveButton("确定", null); //显示对话框 mBuilder.show(); } }); } /* 定义一个使设备震动的方法,我们将在Unity中调用此方法 */ public void SetVibrator(long mTime) { Vibrator mVibrator=(Vibrator)getSystemService(VIBRATOR_SERVICE); mVibrator.vibrate(mTime); } /* 定义一个使设备震动的方法,我们将在Unity中调用此方法 */ public void ShowToast(String mContent) { Toast.makeText(mContext,mContent,Toast.LENGTH_LONG); } }在这段代码中我们让MainActivity类继承自Unity提供的UnityPlayerActivity,这样我们就能够在Android中使用Unity提供的某些方法。在这段代码中我们定义了5个方法,即用于显示对话框的ShowDialog()方法、用于显示Toast的ShowToast()方法、用于打开一个Activity的StartWebView()方法、用于使设备震动的方法SetVibrator()方法以及用于调用Unity中定义的方法的InvokeUnity()方法,当中ShowDialog()方法须要在UI线程下执行。在这里,我们不须要为当前的Activity设置一个布局文件,所以我们没有使用setContentView()方法。
接下来我们创建一个继承自Activity的类WebActivity,我们希望在这个页面中载入一个网页,网页的地址能够通过Unity来指定,我们首先来看布局文件avtivity_web.xml,它是一个简单的线性布局,在它的内部仅仅有一个用于显示网页的WebView组件webView:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>对应地,它对应于WebActivity类,我们在MainActivity类中定义的StartWebView()方法即指向这个Acitivity:
package com.android.android2unity; import android.app.Activity; import android.os.Bundle; import android.webkit.WebView; public class WebActivity extends Activity { //网页组组件 private WebView mWebView; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置当前布局 setContentView(R.layout.activity_web); //获取WebView mWebView=(WebView)findViewById(R.id.webView); //获取URL String mUrl=this.getIntent().getStringExtra("URL"); //载入网页 mWebView.loadUrl(mUrl); } }它的意义非常明白,从MainActivity中读取传来的字符型參数URL,这是一个网页地址,我们通过这个地址打开一个网页。最后,我们来看项目的配置文件AndroidManifest.xml文件,这是一个比較关键的文件,我们在这里要做的事情是注冊WebActivity、给应用分配对应的权限:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.android2unity" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14" /> <uses-permission android:name="android.permission.VIBRATE"/> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.android.android2unity.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".WebActivity"/> </application> </manifest>好了,到如今为止,我们基本上完毕了Android插件的编写,最后我们要做的就是将它输出为一个Jar库,以便我们在Unity中使用,在这里要注意的是,全部的Java类、Android生成的配置文件类(如属性、布局、值等)都要编译,博主之前就是因为没有搞懂Jar库的编译,结果在Java、Unity两边费了不少的周折、来回奔波。假设对Java熟悉的朋友,一定会採用命令来输出库文件吧,只是博主是个菜鸟,对Java命令不太熟悉,所以博主採用的方法是通过文件->导出来导出Jar文件的,如图,我们须要选中src文件夹和R.java文件,假设有第三方的库的话还须要选中libs文件夹:
这样我们就能够直接输出我们须要的Jar库文件了。这样我们就完毕了在Android中编写Unity插件的任务,接下来,我们进入Unity的势力范围吧,哈哈!
在Unity这块呢,我们继续用我们前一篇文章中的项目,我们继续使用FF中这个美丽的妹子(原谅我不知道她叫什么名字)如图:
以下请大家依照这种结构来组织Android插件的文件夹:
----------Assets
----Plugins
-----Android
-----bin(存放导出的Jar)
-----libs(存放第三方的库)
-----res(资源文件夹,可直接复制Android项目)
-----AndroidManifest.xml(配置文件,可直接复制Android项目)
终于的效果应该是这样:
好了,以下我们来编写C#脚本AndroidAPI.cs
using UnityEngine; using System.Collections; public class AndroidAPI : MonoBehaviour { void Start() { //设置当前游戏体的名字,在Android中我们将使用这个名字 this.name="Vabille"; } //定义一个方法以改变摄像机背景颜色,我们将在Android中调用这种方法 void SetCameraColor() { //设置摄像机背景颜色 Camera.main.backgroundColor=new Color(1.0F,0.5F,0.5F); } void OnGUI () { //通过API调用对话框 if(GUILayout.Button("调用Android API显示对话框",GUILayout.Height(45))) { //获取Android的Java接口 AndroidJavaClass jc=new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo=jc.GetStatic<AndroidJavaObject>("currentActivity"); //构造參数 string[] mObject=new string[2]; mObject[0]="Unity3D"; mObject[1]="Unity3D成功调用Android API"; //调用方法 jo.Call("ShowDialog",mObject); } //通过传值打开Activity if(GUILayout.Button("调用Android API中打开Activity",GUILayout.Height(45))) { //获取Android的Java接口 AndroidJavaClass jc=new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo=jc.GetStatic<AndroidJavaObject>("currentActivity"); //打开博主的博客 jo.Call("StartWebView","http://blog.csdn.net/qinyuanpei"); } //通过API调用Toast if(GUILayout.Button("调用Android API中的Toast",GUILayout.Height(45))) { //获取Android的Java接口 AndroidJavaClass jc=new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo=jc.GetStatic<AndroidJavaObject>("currentActivity"); //打开博主的博客 jo.Call("ShowToast","为Unity3D编写Android插件是件苦差事!"); } //通过API调用Toast if(GUILayout.Button("调用Android API中的震动方法",GUILayout.Height(45))) { //获取Android的Java接口 AndroidJavaClass jc=new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo=jc.GetStatic<AndroidJavaObject>("currentActivity"); //打开博主的博客 jo.Call("SetVibrator",40); } //通过API调用Toast if(GUILayout.Button("通过SendMessage调用Unity中的方法",GUILayout.Height(45))) { //获取Android的Java接口 AndroidJavaClass jc=new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo=jc.GetStatic<AndroidJavaObject>("currentActivity"); //打开博主的博客 jo.Call("InvokeUnity",""); } } }这里的方法都是调用在Android中定义好的方法,主要是利用AndroidJavaObject的Call()方法,该方法有两个參数,第一个參数是一个字符型的变量,是我们要调用的方法的名字,第二个參数是一个object[]类型,是我们要调用的方法的參数。好了,我们一起来看看手机上执行的效果吧!
这就是今天的成果了,只是大家都知道我的习惯,每次我在文章中解决不了的问题都会在博客里说出来让大家帮我解决,而今天的问题就是ShowToast()方法和SetVibrator()方法一直没有被调用,博主怀疑是不是Unity提供的Java接口能力有限,仅仅能訪问Android的某些接口,不知道大家是怎么看的,假设大家知道的话,希望大家能够告诉我啊,呵呵。
最后,想说的一点就是我们在C#里定义了一个SetCameraColor()的方法,这是一个改变摄像机背景的方法,我们在Android中使用InvokeUnity()方法来訪问这种方法,因为在第一个页面中,我们没有使用Android的布局元素,因此Android的事件我们无法使用,我们依旧採用在Unity中调用的方法,最然这样显得舍近求远,但是这说明了一个问题,Android能够调用Unity的的方法,而详细实现就是通过SendMessage()来实现的,博主个人认为这是一种托付吧。大家注意到最后屏幕的颜色变成了红色,说明这种方法被调用了。好了,今天的内容就是这样啦,写完这篇文章博主感觉好累啊!
每日箴言:孤独是人生的伴侣,寂寞是人生的常客。人生就是一场修行,修行要有耐性,要能忍受孤独、无悔寂寞。
喜欢我的博客请记住我的名字:秦元培,我博客地址是blog.csdn.net/qinyuanpei。
转载请注明出处,本文作者:秦元培,本文出处:http://blog.csdn.net/qinyuanpei/article/details/39348677