http://blog.csdn.net/libeifs/article/details/7110371
Unity Android平台AdMob应用
开发环境
Window7
Unity3D 3.4.1
MB525defy Android 2.2.1
羽化的第二十一篇博客,这次一次发布三篇确实有点漫长。。。因为最近羽化一直很忙,没多少时间重新整理Android AdMob方面的东西,正好今天有空就把准备写的补上。其实羽化这方面也不是很熟悉,但知道的解决方法有两三种,这次就说一个最简单的解决方案,并送上代码,以后任何人开发都可以改改项目名称和ID就可以直接拿来用。很多方法来自官方的大牛们热议,大家可以可以借鉴下这些脚本写法。
最近通关了刺客信条启示录,作为Ezio和Altair时代的终结,本作应该算是前作兄弟会的延伸,创新点不多,但依然很有趣,刺客养成方面做得过于繁琐,也没有隐藏的装备了,这点实在是可惜。想当初羽化开始玩刺客信条系列的时候,大概读大一,XBOX360才刚买不久,家里还是一个纯平电视,第一次玩的时候感觉很兴奋,开阔的场景,密集的建筑,自由的攀爬,这就是游戏的乐趣,真正的次世代体验,但初代任务方面薄弱很多,幸运的是,刺客信条系列总是在不断改进和创新,可以看到开发团队在这方面的努力,这种开发游戏的态度值得游戏制作人学习,希望这个系列能做得更好,真正做到“万物皆虚,万事皆允”,应该下一作就是终结。
本次学习:
1. Eclipse报错解决方案
2. AdMob应用
1.Eclipse报错解决方案
本来不用写的,但羽化在打开Eclipse的时候遇到了几个蛋疼问题,下面羽化把这几个问题做个小小总结:
(1)Error: Unableto open class file XXX/R.java错误
有两种解决方案:首先是Project -> clean 再勾上Build Automatically,可以解决一类常出现的脑抽问题。。。其次是Window -> Preferences -> Java -> Build Path 中选择Project -> Apply,可以解决些意外问题。。。
(2)error generating final archive 错误
这类错误一般新手是遇不到的,因为这是证书过期引起的,一般证书有效期只有1年,超过1年后,所有新建的Android工程都会无故报错,解决方法是来到Window -> Preferences -> Android -> Build中 Default debug keystore目录下,复制里面地址打开删除debug.keystore及 ddms.cfg两个文件,重启eclipse就可以解决。
2.AdMob应用
首先是Eclipse上Android项目的写法,导入羽化的项目AdMob(Import –> Existing Projects into Workspace),肯定会报错。。。
这时修改引用,选中项目文件单击鼠标右键-> Properties -> Java Build Path里面的Libraries,把admob-sdk-android.jar和classes.jar文件重新Edit到当前的libs里面,再到Order and Export里面勾选上。
注意: admob-sdk-android.jar是网上一个大牛封装的AdMob SDK,有一些自己的方法,但不是最新版本,下载最新版本的同学注意Android系统版本最好超过3.2,否则广告栏会显示一段错误。classes.jar是Unity里面自带的一个jar包,通过搜寻就可以知道位置,羽化就不再介绍。
如果不出意外,现在应该完全不会报错,报错了请看上面的注意和Eclipse报错原因。。。下面我们看代码
AdMob.java
- package com.LB.AdMob;
-
- import android.os.Bundle;
- import android.os.Handler;
- import android.util.Log;
- import android.view.View;
- import android.view.ViewGroup.LayoutParams;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.AlphaAnimation;
- import android.widget.LinearLayout;
- import android.os.Message;
-
- import com.unity3d.player.*;
-
- import com.admob.android.ads.AdView;
- import com.admob.android.ads.SimpleAdListener;
-
- public class AdMob extends UnityPlayerActivity
- {
- public AdView adView;
- private boolean adVisible = true;
- protected void onCreate(Bundle savedInstanceState)
- {
- Log.i("AdMob", "onCreate");
- super.onCreate(savedInstanceState);
-
- setupAds();
- }
- private Handler handler = new Handler()
- {
- public void handleMessage(Message msg)
- {
- switch (msg.what)
- {
- case 0:
- if(adVisible)
- {
- adView.setVisibility(View.GONE);
- adVisible = false;
- }
- break;
- case 1:
- if (!adVisible)
- {
- adView.setVisibility(View.VISIBLE);
- AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
- animation.setDuration(400);
- animation.setFillAfter(true);
- animation.setInterpolator(new AccelerateInterpolator());
- adView.startAnimation(animation);
- adVisible = true;
- }
- break;
- default:
- break;
- }
- }
- };
-
- public void EnableAds()
- {
- handler.sendEmptyMessage(1);
- }
-
- public void DisableAds()
- {
- handler.sendEmptyMessage(0);
- }
- private void setupAds()
- {
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
-
- adView = new AdView(this);
- layout.addView(adView, new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
-
- adView.setBackgroundColor(0xff000000);
- adView.setPrimaryTextColor(0xffffffff);
- adView.setSecondaryTextColor(0xffcccccc);
- adView.setKeywords("kickoff Goal Pinball game spiel bundesliga male Flipper Bumper Parlotones kick off 3d soccer fussball football tor vuvuzela");
-
-
-
- adView.setAdListener( new SimpleAdListener()
- {
- public void onFailedToReceiveAd(com.admob.android.ads.AdView adView)
- {
- Log.d("AdListener", "onFailedToReceiveAd: " + adView.toString());
- super.onFailedToReceiveAd(adView);
- }
-
- public void onFailedToReceiveRefreshedAd(com.admob.android.ads.AdView adView)
- {
- Log.d("AdListener", "onFailedToReceiveRefreshedAd: " + adView.toString());
- super.onFailedToReceiveRefreshedAd(adView);
- }
-
- public void onReceiveAd(com.admob.android.ads.AdView adView)
- {
- Log.d("AdListener", "onReceiveAd: " + adView.toString());
- super.onReceiveAd(adView);
- }
-
- public void onReceiveRefreshedAd(com.admob.android.ads.AdView adView)
- {
- Log.d("AdListener", "onReceiveRefreshedAd: " + adView.toString());
- super.onReceiveRefreshedAd(adView);
- }
- } );
- adView.requestFreshAd();
- }
- }
和AndroidMainifest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.LB.AdMob"
- android:versionCode="1"
- android:versionName="1.0">
-
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".AdMob"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
-
- <meta-data android:value="a14ddb78fc3babe" android:name="ADMOB_PUBLISHER_ID" />
-
-
- <activity android:name="com.admob.android.ads.AdMobActivity"
- android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- android:configChanges="orientation|keyboard|keyboardHidden" />
-
-
- <receiver android:name="com.admob.android.ads.analytics.InstallReceiver" android:exported="true">
- <intent-filter> <action android:name="com.android.vending.INSTALL_REFERRER" />
- </intent-filter>
- </receiver>
- </application>
-
-
- <uses-permission android:name="android.permission.INTERNET" />
- </manifest>
这是个典型的Android项目代码,有几点注意下,1.如果想用在自己的应用上,就照着羽化的引用结构自己新建一个工程,把这两个文件中所有的羽化自定义名称换掉即可。2. android:value中填入自己申请的AdMob ID。3.羽化把代码简化到最少,所以并没用使用res中的所有内容,若想自定义图标等就需要自己去修改AndroidManifests实现。4.这个可以加上阻止休眠命令,在羽化上一篇Android博客里面有提到。5.Unity中的Bundle Identifier必须与这里定义的package一致。6.这时把Eclipse项目运行肯定报错。7.这里只写了广告淡出效果,没写淡入效果- - 大家凑合看吧。。。
当上面的都没问题后就把AdMob.java打jar包,方法是选中AdMob鼠标右键 -> Export -> java -> JAR file -> Next
选择你的到处路径和名称JAR file,直接Finish,这样的jar包里面只包括编译后的AdMob.class,很小很干净。。。然后打开羽化的Unity工程AdMobTest(File- > Open Project),双击UnityAdMob这个Sence,可以看到只有一个相机。。。上面只有一个脚本Ad.cs。
Ad.cs
- using UnityEngine;
-
- using System.Collections;
-
- using System;
-
-
-
- public class Ad : MonoBehaviour
-
- {
-
- void OnGUI()
-
- {
-
- if(Input.GetKey(KeyCode.Escape))
-
- {
-
- Application.Quit();
-
- }
-
-
-
- if(GUI.Button(new Rect(0,Screen.height-220,100,100),"EnableAds!"))
-
- {
-
- EnableAds();
-
- }
-
-
-
- if(GUI.Button(new Rect(0,Screen.height-110,100,100),"DisableAds!"))
-
- {
-
- DisableAds();
-
- }
-
-
-
- if(GUI.Button(new Rect(Screen.width-120,Screen.height-40,120,30),"Click to YUHUA!"))
-
- {
-
- Application.OpenURL("http://blog.csdn.net/libeifs");
-
- }
-
- }
-
-
-
-
-
- public void EnableAds()
-
- {
-
- #if UNITY_ANDROID
-
- AndroidJNI.AttachCurrentThread();
-
-
-
-
-
- IntPtr cls_Activity = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");
-
- IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
-
- IntPtr obj_Activity = AndroidJNI.GetStaticObjectField(cls_Activity, fid_Activity);
-
-
-
-
-
- IntPtr cls_OurAppNameActivityClass = AndroidJNI.FindClass("com/LB/AdMob/AdMob");
-
-
-
- IntPtr startAdsMethod = AndroidJNI.GetMethodID(cls_OurAppNameActivityClass, "EnableAds", "()V");
-
-
-
-
-
- if (AndroidJNI.IsInstanceOf(obj_Activity, cls_OurAppNameActivityClass) != false)
-
- {
-
-
-
- jvalue[] myArray = new jvalue[1];
-
- AndroidJNI.CallVoidMethod(obj_Activity, startAdsMethod,myArray);
-
- }
-
- #else
-
- m_adShowing = true;
-
- #endif //UNITY_ANDROID
-
- }
-
-
-
-
-
- public void DisableAds()
-
- {
-
- #if UNITY_ANDROID
-
- AndroidJNI.AttachCurrentThread();
-
-
-
-
-
- IntPtr cls_Activity = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");
-
- IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
-
- IntPtr obj_Activity = AndroidJNI.GetStaticObjectField(cls_Activity, fid_Activity);
-
-
-
- IntPtr cls_OurAppNameActivityClass = AndroidJNI.FindClass("com/LB/AdMob/AdMob");
-
- IntPtr stopAdsMethod = AndroidJNI.GetMethodID(cls_OurAppNameActivityClass, "DisableAds", "()V");
-
-
-
- if (AndroidJNI.IsInstanceOf(obj_Activity, cls_OurAppNameActivityClass) != false)
-
- {
-
- jvalue[] myArray = new jvalue[1];
-
- AndroidJNI.CallVoidMethod(obj_Activity, stopAdsMethod,myArray);
-
- }
-
- #else //UNITY_ANDROID
-
- m_adShowing = false;
-
- #endif //UNITY_ANDROID
-
- }
-
- }
看起很简单,确实也不难,首先建立Plugins -> Android -> bin | libs这个文件集,把Eclipse项目里面的AndroidManifest.xml放在Android目录下,把打包的UnityAdMob.jar放在bin下,把这个重写的AdMob SDK放在libs下,注意:把Bundle Identifier设置成和Eclipse项目包一样的名字,就算没有Ad.cs程序运行也会执行一次广告,这个C#类只是个广告开关,还有里面的两个AndroidJNI.FindClass("XXX")自行修改。。。
运行效果: (有空就帮羽化点下广告吧- -)
这个应该能满足大部分广告显示方式,置于显示位置就麻烦大家自行修改了,谢谢大家支持~ ~
老样子,项目送上:
http://dl.dbank.com/c0ya3iynro
下集预告:
Thinking inShader(5)
分享到:
上一篇:Thinking in Shader(6)
下一篇:Unity骨骼动力学应用
http://blog.csdn.net/libeifs/article/details/6791346
Unity调用Android
开发环境
Window7
Unity3D 3.4
MB525defy Android 2.2.1
jdk1.6.0 (7版本貌似不行)
羽化的第十一篇博客。最近在玩《北欧女神:负罪者》,3A做的游戏都很有诚意,游戏秉承了系列优良传统,而且做成了SRPG也不觉得不适应,游戏难度中上,剧情也不错,在羽化看来《北欧女神》是个十分经典的系列,在RPG中算是个另类作品,准确来说应该是独树一帜。能力在逐渐提高,而工资却越拿越少,羽化面包和泡面的日子不知道还能坚持多久,面对这个忧郁的现实,如何改变,羽化现在就跟威尔弗雷德一个心情,一直坚持自己最初的理由还是放下包袱开始新的生活?因为。。。于是羽化做了个决定~ ~ Brand New World
这次说一个比较有趣的内容,Android类在Unity上如何调用的方法,感谢★幻想の猫⌒的分享,话说方法来自网上,羽化只是小做修改,在此感慨国外牛人多,下面开始吧- -
本次学习:
1. Android类打jar包
2. Unity如何调用Android
3. 功能分析
1. Android类打jar包
羽化在这还是用网上的那个例子,用Android类得到Sensor的三个值,这里把过程简化,毕竟这些都是Android的基础。
首先打开Unity,在Project下创建Plugins文件夹,然后在该文件夹下创建一个名为Android的子文件夹,再在Android文件夹下创建src文件夹,创建这三个层级的文件夹后我们开始写Android类,下面有些部分是需要自己修改的,羽化用自己的习惯修改了。
CompassActivity.java
- package com.LB.UnityAndroid1;
-
- import com.unity3d.player.UnityPlayerActivity;
-
- import android.content.Context;
- import android.hardware.Sensor;
- import android.hardware.SensorEvent;
- import android.hardware.SensorEventListener;
- import android.hardware.SensorManager;
- import android.os.Bundle;
- import android.util.Config;
- import android.util.Log;
- import android.app.Activity;
- import android.view.WindowManager;
-
- public class CompassActivity extends UnityPlayerActivity {
- private static final String TAG = "Compass";
-
- private SensorManager mSensorManager;
- private Sensor mSensor;
-
- static public float xmag;
- static public float x;
- static public float ymag;
- static public float zmag;
-
- private final SensorEventListener mListener = new SensorEventListener() {
- public void onSensorChanged(SensorEvent event) {
- if (Config.DEBUG) Log.d(TAG,
- "sensorChanged (" + event.values[0] + ", " + event.values[1] + ", " + event.values[2] + ")");
-
- xmag = event.values[0];
- ymag = event.values[1];
- zmag = event.values[2];
- }
-
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- };
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
- mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
- }
-
- @Override
- protected void onResume()
- {
- if (Config.DEBUG) Log.d(TAG, "onResume");
- super.onResume();
-
- mSensorManager.registerListener(mListener, mSensor,
- SensorManager.SENSOR_DELAY_GAME);
- }
-
- @Override
- protected void onStop()
- {
- if (Config.DEBUG) Log.d(TAG, "onStop");
- mSensorManager.unregisterListener(mListener);
- super.onStop();
- }
-
- public static float getX() {
-
- x+=10.0;
- return x;
- }
-
- public static float getY() {
- return ymag;
- }
-
- public static float getZ() {
- return zmag;
- }
- }
好了,这个类放在src文件夹里面,现在是四个层级关系,右击UnityAndroid选中Show in Explorer,这样就会来到这个文件夹下,下面打开cmd命令控制台,cd到我们这个文件夹下,分别输入这三条语句:
1.> javac CompassActivity.java -classpath D:\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar -bootclasspath D:\android-sdk-windows\platforms\android-8\android.jar -d .
2.> javap -s com.LB.UnityAndroid1.CompassActivity
3.> jar cvfM ../Compass.jar com/
注意这里需要配置好java的环境变量和要有android 2.2的API。这样,在Unity里面会发现多出来一个com的文件夹和Android的jar包,我们打jar包就这样打好了。
2. Unity如何调用Android
这里我们还需要新建AndroidMainfest.xml在Android文件夹下,这里要新建个res文件夹,因为羽化这有用到里面的资源。于是放好后结构就很像Android的项目了。
AndroidMainfest.xml
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.LB.UnityAndroid1"
- android:versionCode="1"
- android:versionName="1.0">
-
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".CompassActivity"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- </manifest>
万事具备,现在写个C#来调用这个类了,这个就放在根目录下。
CompassJNI.cs
- using UnityEngine;
- using System.Collections;
- using System;
-
- public class CompassJNI : MonoBehaviour
- {
- static float xValue;
- static float yValue;
- static float zValue;
-
-
- void Start ()
- {
- AndroidJNI.AttachCurrentThread();
- }
-
- void Update() {
- if(Input.GetKey(KeyCode.Escape))
- {
- Application.Quit();
- }
- using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
- {
-
- using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) {
-
- AndroidJavaClass cls_CompassActivity = new AndroidJavaClass("com.LB.UnityAndroid1.CompassActivity");
-
- cls_CompassActivity.CallStatic("Init", obj_Activity);
-
- xValue = cls_CompassActivity.CallStatic<float>("getX");
- yValue = cls_CompassActivity.CallStatic<float>("getY");
- zValue = cls_CompassActivity.CallStatic<float>("getZ");
-
- }
- }
- }
-
- void OnGUI()
- {
- GUI.Label(new Rect(Screen.width / 2 -200, Screen.height / 2, 400,100), "xmag = " + xValue.ToString() + " ymag = " + yValue.ToString() + " zmag = " + zValue.ToString());
- if(GUI.Button(new Rect(Screen.width-120,Screen.height-40,120,30),"Click to YUHUA!"))
- {
- Application.OpenURL("http://blog.csdn.net/libeifs");
- }
- }
- }
在运行的时候一定要记住把Player Settings里面的Identification的Bundle Identifier改成自己的项目名,羽化这里是Com.LB.UnityAndroid1 。
3. 功能分析
这个例子是得到Android手机的Sensor,羽化这里让X自增长以示区别,细心的人会发现羽化这里加了些看似没用的语句,比如这里有个阻止屏幕休眠的语句,这是羽化目前发现唯一的一个Unity做不到的功能,多亏了Michael的提醒,不然会成为一个盲点。其实这个项目里面很多东西可以删掉,留下jar包和CompassJNI就够了,大家可以试试,Unity载入jar包貌似是全部载入与名字没有关系,话说这个例子可以用来添加ADMob,公司里面明哥已经实现出来了,羽化是不建议加这个,因为很烦人,原来玩《割绳子》的时候深有体会=。= 这里加了羽化博客的广告,大家不要骂。。。
老样子项目地址送上:
http://download.csdn.net/detail/libeifs/3616911
下集预告
Unity简单Ai编写
分享到:
上一篇:Unity无缝地图研究
下一篇:Unity简单AI编写
为android构建一个插件
要创建一个android插件,首先要有 Android NDK 并熟悉使用ndk构建共享库的方法。
如果用C++来实现库,必须声明成用C语言的链接方式,以避免Name Mangling问题。
- extern "C"
- {
- float FooPluginFunction ();
- }
通过C#脚本使用插件
构建了共享库后,必须把共享库复制到unity3d工程中的Assets->Plugins->Android目录下。(没有该目录的话,自己依次创建。)
当你在unity3d中在C#脚本中定义如下的函数时,unity3d就能通过名称找到共享库
- [DllImport ("PluginName")]
- private static extern float FooPluginFunction ();
注意
PluginName
不要包含共享库文件名中的“lib”前缀和“.so”后缀。建议所有的native代码方法都用C#代码封装一层,并在C#代码中检查 Application.platform
变量,以保证只有app运行在正确的设备上时,才去调用native方法。当在Editor环境下运行时,可以在C#代码中返回空值。
当然也可以用平台宏定义的方法,来控制与平台相关的代码的编译。
部署
对于要部署到多个平台的项目,项目工程中必须包含各个平台所需要的插件(例如:libPlugin.so用于android平台,Plugin.bundle用于mac平台,Plugin.dll用于windows平台)。unity3d会自动为目标平台选择正确的插件。
使用java插件
android插件机制同样允许使用java来与android系统进行交互。
为android构建一个java插件
有好几种方法来构建java插件,最终结果都是生成包含.class文件的.jar包。一种方法是下载 JDK,在命令行下用javac命令编译,用jar命令打包成jar文件;另一种方法是 Eclipse+ADT。
在native代码中使用java插件
构建好了java插件后,将java插件(.jar)复制到 unity3d工程中的
- jint JNI_OnLoad(JavaVM* vm, void* reserved)
- {
- JNIEnv* jni_env = 0;
- vm->AttachCurrentThread(&jni_env, 0);
- }
这个是从c/c++调用java所必需的。JNI超越了本文档的范畴,不做详细解释。通常情况下,先找到类的定义,然后解析类的构造方法(<init>)并创建类的实例,如下面例子所示:
- jobject createJavaObject(JNIEnv* jni_env)
- {
- jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class");
- jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V");
- jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass);
- return jni_env->NewGlobalRef(obj_JavaClass);
- }
通过帮助类来使用java插件
Call方法
Get域的值
Set域的值
-
- AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string");
-
-
-
-
- int hash = jo.Call<int>("hashCode");
-
-
这个例子中,我们创建了一个 java.lang.String的实例,并用我们自定义的一个字符串初始化它,最后我们得到该字符串的哈希值。 some_string
android.view.ViewGroup$LayoutParams或者android/view/ViewGroup$LayoutParams,这两种方式都是可行的。
上面有个插件的例子是说获取当前程序的缓存目录的,下面这个例子直接用c#代码做同样的事情,而不需要任何插件:
- AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
-
- AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
-
-
-
-
- Debug.Log(jo.Call<AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath"));
-
-
-
-
-
-
这个例子中,我们没有首先使用AndroidJavaObject,而是AndroidJavaClass ,因为我们想获取类com.unity3d.player.UnityPlayer的一个静态成员,而不是去创建一个新的对象(Android UnityPlayer会自动创建一个实例)。我们访问其静态域"currentActivity" ,这个时候我们用的是AndroidJavaObject作为泛型参数,这是因为实际类型(android.app.Activity)是类java.lang.Object的子类,任意非基本类型都必须作为AndroidJavaObject来访问。有一个例外就是字符串,字符串可以直接访问,尽管它在java中并不是基本类型。
之后就是调用的Activity的getCacheDir()得到缓存目录的文件对象,再调用getCanonicalPath()方法获取缓存目录路径的字符串表示。
当然,现在已经不需要通过这种方式来获取缓存目录了,因为unity3d提供了接口用以访问程序的缓存目录和数据目录,也就是Application.temporaryCachePath and Application.persistentDataPath。
例子3:
最后,是一个通过UnitySendMessage方法从java代码向脚本代码传递数据的小窍门。
- using UnityEngine;
- public class NewBehaviourScript : MonoBehaviour
- {
-
- <span style="white-space:pre"> </span>void Start ()
- <span style="white-space:pre"> </span>{
- <span style="white-space:pre"> </span>JNIHelper.debug = true;
- <span style="white-space:pre"> </span>using (JavaClass jc = new JavaClass("com.unity3d.player.UnityPlayer"))
- <span style="white-space:pre"> </span>{
- <span style="white-space:pre"> </span> jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "whoowhoo");
- <span style="white-space:pre"> </span>}
- }
-
- void JavaMessage(string message)
- <span style="white-space:pre"> </span>{
- Debug.Log("message from java: " + message);
- }
- }
类com.unity3d.player.UnityPlayer现在有一个静态方法UnitySendMessage,与iOS中native端的UnitySendMessage一样,可用来在java中向脚本传递数据。
这里我们直接从脚本中调用的,但它确实是在java端发送的消息,它会调回到unity3d的native代码,传递消息到名为"Main Camera"的游戏对象上去,该对象上绑定的某个脚本中包含有名为"JavaMessage"的方法。
在unity3d中使用java插件的最佳实践
这一节主要针对那些没有足够jni,java和android经验的人。假设我们在unity3d中使用AndroidJavaObject/AndroidJavaClass来与java交互。
首先就是要注意对AndroidJavaObject/AndroidJavaClass的任何操作都是很费时的(是通过JNI来进行的)。因此为了代码性能和代码清晰性,我们强烈建议托管代码与native/java代码间的转换次数保持在最小数量。
你可以定义一个java方法完成所有的事情,然后我们通过AndroidJavaObject/AndroidJavaClass来与这个方法通信和获取结果,我们的JNI帮助类会尽可能多的缓存数据已提高性能。
-
- AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string");
- int hash = jo.Call<int>("hashCode");
- int hash = jo.Call<int>("hashCode");
在使用过后,Mono垃圾回收器会释放所有创建的AndroidJavaObject和AndroidJavaClass实例,但我们还是建议把它们放到using(){}块中,以保证它们能被尽快的清除掉。除此之外,你无法保证它们会被销毁掉。如果你设置了AndroidJNIHelper.debug为true,你会在log输出中看到垃圾回收器的活动记录。
-
- void Start ()
- {
- using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale"))
- <span style="white-space:pre"> </span>{
- using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault"))
- <span style="white-space:pre"> </span>{
- Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage"));
-
- }
- }
- }
也可以直接调用.Dispose()方法确保没有java对象残留,c#对象会存活长一点,最终还是会被mono的垃圾回收器回收。
继承UnityPlayerActivity java代码
在Unity Android上,我们可以继承标准的UnityPlayerActivity类(android上Unity Player的主要java类,类似于Unity iOS上的AppController.mm)。
应用程序可以覆写android系统与Unity Android之间的任意交互方法,只要新建一个Activity继承UnityPlayerActivity就可以实现。(在mac系统上,UnityPlayerActivity.java在/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/src/com/unity3d/player目录下;在windows系统中,它通常在C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player目录下)
首先定位Unity Android的classes.jar文件,可以在Unity3d的安装目录(windows下通常是C:\Program Files\Unity\Editor\Data,mac下是/)下的子文件夹PlaybackEngines/AndroidPlayer/bin中找到,将它添加到你编译activity的classpath中。最终编译出来的.class文件,需要打包成.jar文件,放到工程中的Assets->Plugins->Android目录下。因为android中manifest文件指明了启动哪个Activity,因此我们也需要重新写一个AndroidManifest.xml文件,也需要将它放到Assets->Plugins->Android目录下。
继承UnityPlayerActivity的一个例子,OverrideExample.java:
- package com.company.product;
-
- import com.unity3d.player.UnityPlayerActivity;
-
- import android.os.Bundle;
- import android.util.Log;
-
- public class OverrideExample extends UnityPlayerActivity {
-
- protected void onCreate(Bundle savedInstanceState) {
-
-
- super.onCreate(savedInstanceState);
-
-
- Log.d("OverrideActivity", "onCreate called!");
- }
-
- public void onBackPressed()
- {
-
-
- }
- }
相关的AndroidManifest.xml文件如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
- <application android:icon="@drawable/app_icon" android:label="@string/app_name">
- <activity android:name=".OverrideExample"
- android:label="@string/app_name"
- android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
UnityPlayerNativeActivity
同样我们可以创建UnityPlayerNativeActivity的子类,这与创建UnityPlayerActivity的子类具有相同的效果,但是会有较小的输入延迟。但是,需要明白的是,NativeActivity是在Gingerbread中引入的(即android 2.3),老的android版本没有这个特性,因为在NativeActivity中,触摸事件都是在native代码中处理的,java视图正常情况下是无法获取这些事件的,不过在unity3d中,有允许将事件传到DalvikVM的转发机制,要应用这个转发机制,必须修改manifest文件如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
- <application android:icon="@drawable/app_icon" android:label="@string/app_name">
- <activity android:name=".OverrideExampleNative"
- android:label="@string/app_name"
- android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
- <meta-data android:name="android.app.lib_name" android:value="unity" />
- <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
- </manifest>
要注意activity元素中的.OverrideExampleNative属性,还有两条meta-data元素,第一条meta-data元素指明使用unity3d库libunity.so,第二条meta-data元素使事件能传递到你创建的UnityPlayerNativeActivity子类中。
例子
native插件例子
这里有一个简单的使用native插件的例子。
这个例子演示了如果从unity3d android程序中来调用c代码,包中包含了一个通过native插件计算出来的两个数之和的场景,要注意,你必须用Android NDK来编译这个插件。
java插件例子
这里有一个简单的使用java代码的例子。
这个例子演示了怎么用java代码与android系统进行交互,以及如何用c++来将c#和java沟通起来,包中的场景显示了一个按钮,点击该按钮,会显示出程序在android系统中的缓存目录路径。需要JDK和 Android NDK来编译这个插件。
这里有一个相似的例子,但是是基于预先编译好的JNI库,来封装native代码,供c#调用。
最后更新于:2012-09-25