[Unity3D]Unity3D游戏开发之从Unity3D到Eclipse



---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39717795

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


       各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。首先祝大家能够度过一个愉快的十一长假。今天呢,博主将为大家送上Unity-Android系列的最后一篇文章《Unity3D游戏开发之从Unity3D到Eclipse》。通过前面的学习,大家已经知道通过在Eclipse中为Unity编写插件的方法,我们可以实现在Unity与Android API的通信。可是不幸的是,这种方法并不能对所有的Android API奏效,在某些时候,我们需要反客为主,将Unity项目导出为Android项目,然后在Eclipse中继续修改游戏的内容。这种方法在《Unity3D游戏开发之Unity与Android交互调用研究》这篇文章中已经提及,不过并没有真正地进行过研究。之前有个叫@SHANlover的朋友问我,能不能写一篇Unity项目导出Eclipse的文章,因此博主便抽空研究了下这种方法,从而有了今天的这篇文章。首先,我们来创建一个简单地场景:


       好了,创建完场景以后我们就可以直接编写一个脚本CubeScripts.cs来控制场景中的Cube:

[csharp]  view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class CubeScripts : MonoBehaviour {  
  5.   
  6.     /// <summary>  
  7.     /// 定义旋转速度  
  8.     /// </summary>  
  9.     public float RotateSpeed=45;  
  10.   
  11.     /// <summary>  
  12.     /// 定义摄像机的最近距离    
  13.     /// </summary>  
  14.     private float mNear=2.5F;  
  15.       
  16.     /// <summary>  
  17.     /// 摄像机当前距离  
  18.     /// </summary>  
  19.     private float mDistance=5F;  
  20.   
  21.     /// <summary>  
  22.     /// 定义摄像机的最远距离  
  23.     /// </summary>  
  24.     private float mFar=7.5F;  
  25.   
  26.     /// <summary>  
  27.     /// 摄像机的缩放速率  
  28.     /// </summary>  
  29.     private float mZoomRate=0.5F;  
  30.   
  31.     /// <summary>  
  32.     /// 主摄像机  
  33.     /// </summary>  
  34.     private Transform mCamera;  
  35.       
  36.     /// <summary>  
  37.     /// 在Start()方法中我们设定了游戏体的名称,因为我们在  
  38.     /// Android项目中需要用到这个名称,同时获取主相机对象  
  39.     /// </summary>  
  40.     void Start ()   
  41.     {  
  42.         this.name="Main Cube";  
  43.         mCamera=Camera.main.transform;  
  44.     }  
  45.       
  46.     /// <summary>  
  47.     /// 在Update()方法中我们让Cube按照一定的速度进行旋转  
  48.     /// </summary>  
  49.     void Update ()   
  50.     {  
  51.         transform.Rotate(Vector3.up * Time.deltaTime * RotateSpeed);  
  52.     }  
  53.   
  54.   
  55.     /// <summary>  
  56.     /// 定义一个放大的方法供外部调用  
  57.     /// </summary>  
  58.     public void ZoomIn()  
  59.     {  
  60.         mDistance-=mZoomRate;  
  61.         mDistance=Mathf.Clamp(mDistance,mNear,mFar);  
  62.         mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;  
  63.     }  
  64.   
  65.     /// <summary>  
  66.     /// 定义一个缩小的方法供外部调用  
  67.     /// </summary>  
  68.     public void ZoomOut()  
  69.     {  
  70.         mDistance+=mZoomRate;  
  71.         mDistance=Mathf.Clamp(mDistance,mNear,mFar);  
  72.         mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;  
  73.     }  
  74. }  
这段脚本十分地简单,没有什么可说的,在这里我们定义了两个方法ZoomIn和ZoomOut,这两个方法我们将提供给Android来调用。我们将这段脚本绑定到Main Cube这个对象上。接下来,我们将项目Build一下,这里我们将项目的PackageName设为com.android.unity2eclipse,然后将其导出为一个Android项目:


          那么,这样我们就得到一个可以在Eclipse中打开的Android项目。可是这个Android项目我们怎么样使用呢?在金曾玺老师《Unity3D手机游戏开发》一书中是将Unity导出的Android项目作为一个库,然后再用一个新的Android项目去调用这个库。这本书中所使用的Unity版本是4.X,而博主所使用的Unity版本是4.5.1。最初博主就是按照这种思路去编写Android程序,可是在经历了无数次的失败后,博主开始怀疑这种方法的正确性。带着尝试的念头,博主直接运行了由Unity导出的Android项目,结果程序成功地在手机上运行了。如图:


       那么,接下来我们不妨冷静地分析下从Unity导出的这个Android项目,我们可以看到整个项目的目录结构是这样的:


在这个目录结构中,我们可以看到它是一个标准的Android项目,在libs中的unity-class.jar就是我们在前面的文章中所使用过的jar库文件。而在assets文件夹中我们可以看到Unity所依赖的dll文件。在com.android.unity2eclipse这个Package中,我们会发现一个名为UnityPlayerNativeActivity.class的类,该类定义如下:

[java]  view plain copy
  1. package com.android.unity2ecplise;  
  2.   
  3. import com.unity3d.player.*;  
  4. import android.app.NativeActivity;  
  5. import android.content.res.Configuration;  
  6. import android.graphics.PixelFormat;  
  7. import android.os.Bundle;  
  8. import android.view.KeyEvent;  
  9. import android.view.MotionEvent;  
  10. import android.view.Window;  
  11. import android.view.WindowManager;  
  12.   
  13. public class UnityPlayerNativeActivity extends NativeActivity  
  14. {  
  15.     protected UnityPlayer mUnityPlayer;     // don't change the name of this variable; referenced from native code  
  16.   
  17.     // Setup activity layout  
  18.     @Override protected void onCreate (Bundle savedInstanceState)  
  19.     {  
  20.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  21.         super.onCreate(savedInstanceState);  
  22.   
  23.         getWindow().takeSurface(null);  
  24.         setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);  
  25.         getWindow().setFormat(PixelFormat.RGB_565);  
  26.   
  27.         mUnityPlayer = new UnityPlayer(this);  
  28.         if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar"true))  
  29.             getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,  
  30.                                    WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  31.   
  32.         setContentView(mUnityPlayer);  
  33.         mUnityPlayer.requestFocus();  
  34.     }  
  35.   
  36.     // Quit Unity  
  37.     @Override protected void onDestroy ()  
  38.     {  
  39.         mUnityPlayer.quit();  
  40.         super.onDestroy();  
  41.     }  
  42.   
  43.     // Pause Unity  
  44.     @Override protected void onPause()  
  45.     {  
  46.         super.onPause();  
  47.         mUnityPlayer.pause();  
  48.     }  
  49.   
  50.     // Resume Unity  
  51.     @Override protected void onResume()  
  52.     {  
  53.         super.onResume();  
  54.         mUnityPlayer.resume();  
  55.     }  
  56.   
  57.     // This ensures the layout will be correct.  
  58.     @Override public void onConfigurationChanged(Configuration newConfig)  
  59.     {  
  60.         super.onConfigurationChanged(newConfig);  
  61.         mUnityPlayer.configurationChanged(newConfig);  
  62.     }  
  63.   
  64.     // Notify Unity of the focus change.  
  65.     @Override public void onWindowFocusChanged(boolean hasFocus)  
  66.     {  
  67.         super.onWindowFocusChanged(hasFocus);  
  68.         mUnityPlayer.windowFocusChanged(hasFocus);  
  69.     }  
  70.   
  71.     // For some reason the multiple keyevent type is not supported by the ndk.  
  72.     // Force event injection by overriding dispatchKeyEvent().  
  73.     @Override public boolean dispatchKeyEvent(KeyEvent event)  
  74.     {  
  75.         if (event.getAction() == KeyEvent.ACTION_MULTIPLE)  
  76.             return mUnityPlayer.injectEvent(event);  
  77.         return super.dispatchKeyEvent(event);  
  78.     }  
  79.   
  80.     // Pass any events not handled by (unfocused) views straight to UnityPlayer  
  81.     @Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }  
  82.     @Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }  
  83.     @Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }  
  84.     /*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }  
  85. }  
相信大多数看过我博客的人都会觉得这个类有点眼熟吧,不错,在之前的 《Unity3D游戏开发之在Android视图中嵌入Unity视图》 这篇文章中,我们就是利用这个类实现了在Android视图中嵌入Unity视图,只不过当时这个类是定义在Unity的unity-class.jar这个库中的,而更为确切地位置是在com.unity3d.player这个包下面的UnityPlayerNativeActivity类,我们注意到此时这个类是继承自NativeActivity的,在前面的文章中博主曾经提及这个类,它是Android提供给C/C++开发者的接口。换句话说,Unity项目在Android平台上内部依靠的实际上就是NativeActivity接口,只不过Unity自己使用的接口是封装过的,而我们这里使用的接口是直接继承自父类。那么,我们在这一刻就会产生疑问,从这个类从具体实现上来讲主要的功能就是对Activity进行初始化,那么我们在Activity中能够看到什么由谁来决定呢?大家注意到这里有一个SetContentView()的方法,它传入了UnityPlayer类型的参数作为Activity显示的内容。相信从现在开始,大家对Unity提供的Android接口的认识会越来越清晰吧。为了验证我们的想法,接下来,我们来创建一个布局文件activity_main,它的代码定义如下:

[html]  view plain copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.     <Button  
  8.         android:id="@+id/BtnZoomIn"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_alignParentTop="true"  
  12.         android:text="@string/ZoomIn" />  
  13.     <LinearLayout    
  14.         android:id="@+id/UnityView"    
  15.         android:layout_width="match_parent"    
  16.         android:layout_height="match_parent"    
  17.         android:layout_above="@+id/BtnZoomOut"    
  18.         android:layout_below="@+id/BtnZoomIn"    
  19.         android:orientation="vertical" >    
  20.     </LinearLayout>  
  21.     <Button  
  22.         android:id="@+id/BtnZoomOut"  
  23.         android:layout_width="match_parent"  
  24.         android:layout_height="wrap_content"  
  25.         android:layout_alignParentBottom="true"  
  26.         android:text="@string/ZoomOut" />  
  27. </RelativeLayout>  
接下来,我们创建与之对应的Java类文件MainActivity.class:

[java]  view plain copy
  1. package com.android.unity2ecplise;  
  2.   
  3. import com.android.unity2ecplise.R;  
  4. import com.unity3d.player.UnityPlayer;  
  5.   
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10. import android.widget.LinearLayout;  
  11.   
  12.   
  13. /*我们这里让我们定义的MainActivity继承自Unity导出项目中的UnityPlayerNativeActivity*/  
  14. public class MainActivity extends UnityPlayerNativeActivity   
  15. {  
  16.     private Button BtnZoomIn,BtnZoomOut;  
  17.       
  18.     @Override   
  19.     protected void onCreate (Bundle savedInstanceState)  
  20.     {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.         //获取显示Unity视图的父控件  
  24.         LinearLayout mParent=(LinearLayout)findViewById(R.id.UnityView);  
  25.         //获取Unity视图  
  26.         View mView=mUnityPlayer.getView();  
  27.         //将Unity视图添加到Android视图中  
  28.         mParent.addView(mView);  
  29.           
  30.         //放大  
  31.         BtnZoomIn=(Button)findViewById(R.id.BtnZoomIn);  
  32.         BtnZoomIn.setOnClickListener(new OnClickListener()  
  33.         {  
  34.             @Override  
  35.             public void onClick(View arg0)   
  36.             {  
  37.                 UnityPlayer.UnitySendMessage("Main Cube","ZoomIn","");  
  38.             }  
  39.         });  
  40.         //缩小  
  41.         BtnZoomOut=(Button)findViewById(R.id.BtnZoomOut);  
  42.         BtnZoomOut.setOnClickListener(new OnClickListener()  
  43.         {  
  44.             @Override  
  45.             public void onClick(View arg0)   
  46.             {  
  47.                 UnityPlayer.UnitySendMessage("Main Cube","ZoomOut","");  
  48.             }  
  49.         });  
  50.     }  
  51. }  
在这段代码中,我们通过UnitySendMessage调用了Unity中定义的两个方法ZoomIn、ZoomOut。好了,接下来,我们修改配置文件,使主Activity对应于MainActivity类,打开
AndroidManifest.xml文件:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"   
  3.     package="com.android.unity2ecplise"   
  4.     android:theme="@android:style/Theme.NoTitleBar"   
  5.     android:versionName="1.0" android:versionCode="1"   
  6.     android:installLocation="preferExternal">  
  7.   <supports-screens android:smallScreens="true"   
  8.       android:normalScreens="true"   
  9.       android:largeScreens="true"   
  10.       android:xlargeScreens="true"   
  11.       android:anyDensity="true" />  
  12.   <application android:icon="@drawable/app_icon"   
  13.       android:label="@string/app_name"   
  14.       android:debuggable="false">  
  15.     <activity android:label="@string/app_name"   
  16.         android:screenOrientation="fullSensor"   
  17.         android:launchMode="singleTask"   
  18.         android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"   
  19.         android:name="com.android.unity2ecplise.MainActivity">  
  20.       <intent-filter>  
  21.         <action android:name="android.intent.action.MAIN" />  
  22.         <category android:name="android.intent.category.LAUNCHER" />  
  23.       </intent-filter>  
  24.       <meta-data android:name="unityplayer.UnityActivity" android:value="true" />  
  25.       <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />  
  26.     </activity>  
  27.   </application>  
  28.   <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" />  
  29.   <uses-feature android:glEsVersion="0x00020000" />  
  30. </manifest>  
如我们所猜测的那样,acivity节点的name属性对应于com.android.unity2ecplise.UnityPlayerNativeActivity。这说明在此之前,Android是以这个类作为主Acivity。现在我们将其改为我们自己定义的类,这样我们就能使用自己定义的布局。同时大家会注意到下面这两行:

[html]  view plain copy
  1. <meta-data android:name="unityplayer.UnityActivity" android:value="true" />  
  2. <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />  
之前我们在讲Unity视图嵌入Android视图时曾经碰到过Android视图无法取得焦点的问题,当时就是在配置文件中加入了这样两行代码,此时此刻,大家是不是和博主一样有种大彻大悟的感觉呢。我们运行程序,会发现界面像我们所希望看到的那样被改变了,而且我们可以通过两个按钮来改变视图中立方体的大小:





这样我们就可以认识到从Unity中导出的就是Android项目,我们可以直接将其修改来达到满足我们要求的目的。至于金曾玺老师的这种方法,在官方API文档中的确有提到过这种方法,不过博主尝试了好久,这种方法都失败了,不知道是不是因为Unity在新版本中已经解决了这个问题,从而可以直接导出可运行的Android项目。

        最后来说说网络上流传的一种方法,据说在Unity中将项目Build下就会在工程目录下的Temp文件夹下生成一个名为StagingArea文件夹,我们将这个文件夹在导入到Eclipse中并将其设为一个库,然后在新建的Android项目中引用这个库,并将这个项目中的assets文件夹覆盖新建项目中的assets文件夹,然后我们只要让主Acitivity继承Unity提供的Android接口中的UnityPlayerActivity即可。这种方法博主并没有去尝试,不过在Build的时候确实会产生这样一个文件夹,不过博主觉得这样是不是有点麻烦了啊,既然Unity导出的Android项目直接就能用,我们何必要再去用这种复杂的方法呢,不过我觉得大致的思路就是这样的,UnityPlayerActivity负责Acivity生命周期的维护,UnityPlayer负责渲染Unity场景中的内容,而我们在Unity中使用的资源都被Unity的引擎在内部进行了处理,总之把握了这些,Unity和Android的交互就基本没什么问题了。好了,谢谢大家关注我的博客,今天的内容就是这样了,希望大家喜欢。


每日箴言:有些路看起来很近,可是走下去却很远的,缺少耐心的人永远走不到头。人生,一半是现实,一半是梦想。 ——顾城




-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39717795

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


本文源代码下载:Unity项目 Android项目


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39717795

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


       各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。首先祝大家能够度过一个愉快的十一长假。今天呢,博主将为大家送上Unity-Android系列的最后一篇文章《Unity3D游戏开发之从Unity3D到Eclipse》。通过前面的学习,大家已经知道通过在Eclipse中为Unity编写插件的方法,我们可以实现在Unity与Android API的通信。可是不幸的是,这种方法并不能对所有的Android API奏效,在某些时候,我们需要反客为主,将Unity项目导出为Android项目,然后在Eclipse中继续修改游戏的内容。这种方法在《Unity3D游戏开发之Unity与Android交互调用研究》这篇文章中已经提及,不过并没有真正地进行过研究。之前有个叫@SHANlover的朋友问我,能不能写一篇Unity项目导出Eclipse的文章,因此博主便抽空研究了下这种方法,从而有了今天的这篇文章。首先,我们来创建一个简单地场景:


       好了,创建完场景以后我们就可以直接编写一个脚本CubeScripts.cs来控制场景中的Cube:

[csharp]  view plain copy
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class CubeScripts : MonoBehaviour {  
  5.   
  6.     /// <summary>  
  7.     /// 定义旋转速度  
  8.     /// </summary>  
  9.     public float RotateSpeed=45;  
  10.   
  11.     /// <summary>  
  12.     /// 定义摄像机的最近距离    
  13.     /// </summary>  
  14.     private float mNear=2.5F;  
  15.       
  16.     /// <summary>  
  17.     /// 摄像机当前距离  
  18.     /// </summary>  
  19.     private float mDistance=5F;  
  20.   
  21.     /// <summary>  
  22.     /// 定义摄像机的最远距离  
  23.     /// </summary>  
  24.     private float mFar=7.5F;  
  25.   
  26.     /// <summary>  
  27.     /// 摄像机的缩放速率  
  28.     /// </summary>  
  29.     private float mZoomRate=0.5F;  
  30.   
  31.     /// <summary>  
  32.     /// 主摄像机  
  33.     /// </summary>  
  34.     private Transform mCamera;  
  35.       
  36.     /// <summary>  
  37.     /// 在Start()方法中我们设定了游戏体的名称,因为我们在  
  38.     /// Android项目中需要用到这个名称,同时获取主相机对象  
  39.     /// </summary>  
  40.     void Start ()   
  41.     {  
  42.         this.name="Main Cube";  
  43.         mCamera=Camera.main.transform;  
  44.     }  
  45.       
  46.     /// <summary>  
  47.     /// 在Update()方法中我们让Cube按照一定的速度进行旋转  
  48.     /// </summary>  
  49.     void Update ()   
  50.     {  
  51.         transform.Rotate(Vector3.up * Time.deltaTime * RotateSpeed);  
  52.     }  
  53.   
  54.   
  55.     /// <summary>  
  56.     /// 定义一个放大的方法供外部调用  
  57.     /// </summary>  
  58.     public void ZoomIn()  
  59.     {  
  60.         mDistance-=mZoomRate;  
  61.         mDistance=Mathf.Clamp(mDistance,mNear,mFar);  
  62.         mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;  
  63.     }  
  64.   
  65.     /// <summary>  
  66.     /// 定义一个缩小的方法供外部调用  
  67.     /// </summary>  
  68.     public void ZoomOut()  
  69.     {  
  70.         mDistance+=mZoomRate;  
  71.         mDistance=Mathf.Clamp(mDistance,mNear,mFar);  
  72.         mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;  
  73.     }  
  74. }  
这段脚本十分地简单,没有什么可说的,在这里我们定义了两个方法ZoomIn和ZoomOut,这两个方法我们将提供给Android来调用。我们将这段脚本绑定到Main Cube这个对象上。接下来,我们将项目Build一下,这里我们将项目的PackageName设为com.android.unity2eclipse,然后将其导出为一个Android项目:


          那么,这样我们就得到一个可以在Eclipse中打开的Android项目。可是这个Android项目我们怎么样使用呢?在金曾玺老师《Unity3D手机游戏开发》一书中是将Unity导出的Android项目作为一个库,然后再用一个新的Android项目去调用这个库。这本书中所使用的Unity版本是4.X,而博主所使用的Unity版本是4.5.1。最初博主就是按照这种思路去编写Android程序,可是在经历了无数次的失败后,博主开始怀疑这种方法的正确性。带着尝试的念头,博主直接运行了由Unity导出的Android项目,结果程序成功地在手机上运行了。如图:


       那么,接下来我们不妨冷静地分析下从Unity导出的这个Android项目,我们可以看到整个项目的目录结构是这样的:


在这个目录结构中,我们可以看到它是一个标准的Android项目,在libs中的unity-class.jar就是我们在前面的文章中所使用过的jar库文件。而在assets文件夹中我们可以看到Unity所依赖的dll文件。在com.android.unity2eclipse这个Package中,我们会发现一个名为UnityPlayerNativeActivity.class的类,该类定义如下:

[java]  view plain copy
  1. package com.android.unity2ecplise;  
  2.   
  3. import com.unity3d.player.*;  
  4. import android.app.NativeActivity;  
  5. import android.content.res.Configuration;  
  6. import android.graphics.PixelFormat;  
  7. import android.os.Bundle;  
  8. import android.view.KeyEvent;  
  9. import android.view.MotionEvent;  
  10. import android.view.Window;  
  11. import android.view.WindowManager;  
  12.   
  13. public class UnityPlayerNativeActivity extends NativeActivity  
  14. {  
  15.     protected UnityPlayer mUnityPlayer;     // don't change the name of this variable; referenced from native code  
  16.   
  17.     // Setup activity layout  
  18.     @Override protected void onCreate (Bundle savedInstanceState)  
  19.     {  
  20.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  21.         super.onCreate(savedInstanceState);  
  22.   
  23.         getWindow().takeSurface(null);  
  24.         setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);  
  25.         getWindow().setFormat(PixelFormat.RGB_565);  
  26.   
  27.         mUnityPlayer = new UnityPlayer(this);  
  28.         if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar"true))  
  29.             getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,  
  30.                                    WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  31.   
  32.         setContentView(mUnityPlayer);  
  33.         mUnityPlayer.requestFocus();  
  34.     }  
  35.   
  36.     // Quit Unity  
  37.     @Override protected void onDestroy ()  
  38.     {  
  39.         mUnityPlayer.quit();  
  40.         super.onDestroy();  
  41.     }  
  42.   
  43.     // Pause Unity  
  44.     @Override protected void onPause()  
  45.     {  
  46.         super.onPause();  
  47.         mUnityPlayer.pause();  
  48.     }  
  49.   
  50.     // Resume Unity  
  51.     @Override protected void onResume()  
  52.     {  
  53.         super.onResume();  
  54.         mUnityPlayer.resume();  
  55.     }  
  56.   
  57.     // This ensures the layout will be correct.  
  58.     @Override public void onConfigurationChanged(Configuration newConfig)  
  59.     {  
  60.         super.onConfigurationChanged(newConfig);  
  61.         mUnityPlayer.configurationChanged(newConfig);  
  62.     }  
  63.   
  64.     // Notify Unity of the focus change.  
  65.     @Override public void onWindowFocusChanged(boolean hasFocus)  
  66.     {  
  67.         super.onWindowFocusChanged(hasFocus);  
  68.         mUnityPlayer.windowFocusChanged(hasFocus);  
  69.     }  
  70.   
  71.     // For some reason the multiple keyevent type is not supported by the ndk.  
  72.     // Force event injection by overriding dispatchKeyEvent().  
  73.     @Override public boolean dispatchKeyEvent(KeyEvent event)  
  74.     {  
  75.         if (event.getAction() == KeyEvent.ACTION_MULTIPLE)  
  76.             return mUnityPlayer.injectEvent(event);  
  77.         return super.dispatchKeyEvent(event);  
  78.     }  
  79.   
  80.     // Pass any events not handled by (unfocused) views straight to UnityPlayer  
  81.     @Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }  
  82.     @Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }  
  83.     @Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }  
  84.     /*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }  
  85. }  
相信大多数看过我博客的人都会觉得这个类有点眼熟吧,不错,在之前的 《Unity3D游戏开发之在Android视图中嵌入Unity视图》 这篇文章中,我们就是利用这个类实现了在Android视图中嵌入Unity视图,只不过当时这个类是定义在Unity的unity-class.jar这个库中的,而更为确切地位置是在com.unity3d.player这个包下面的UnityPlayerNativeActivity类,我们注意到此时这个类是继承自NativeActivity的,在前面的文章中博主曾经提及这个类,它是Android提供给C/C++开发者的接口。换句话说,Unity项目在Android平台上内部依靠的实际上就是NativeActivity接口,只不过Unity自己使用的接口是封装过的,而我们这里使用的接口是直接继承自父类。那么,我们在这一刻就会产生疑问,从这个类从具体实现上来讲主要的功能就是对Activity进行初始化,那么我们在Activity中能够看到什么由谁来决定呢?大家注意到这里有一个SetContentView()的方法,它传入了UnityPlayer类型的参数作为Activity显示的内容。相信从现在开始,大家对Unity提供的Android接口的认识会越来越清晰吧。为了验证我们的想法,接下来,我们来创建一个布局文件activity_main,它的代码定义如下:

[html]  view plain copy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical"  
  6.     tools:context=".MainActivity" >  
  7.     <Button  
  8.         android:id="@+id/BtnZoomIn"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_alignParentTop="true"  
  12.         android:text="@string/ZoomIn" />  
  13.     <LinearLayout    
  14.         android:id="@+id/UnityView"    
  15.         android:layout_width="match_parent"    
  16.         android:layout_height="match_parent"    
  17.         android:layout_above="@+id/BtnZoomOut"    
  18.         android:layout_below="@+id/BtnZoomIn"    
  19.         android:orientation="vertical" >    
  20.     </LinearLayout>  
  21.     <Button  
  22.         android:id="@+id/BtnZoomOut"  
  23.         android:layout_width="match_parent"  
  24.         android:layout_height="wrap_content"  
  25.         android:layout_alignParentBottom="true"  
  26.         android:text="@string/ZoomOut" />  
  27. </RelativeLayout>  
接下来,我们创建与之对应的Java类文件MainActivity.class:

[java]  view plain copy
  1. package com.android.unity2ecplise;  
  2.   
  3. import com.android.unity2ecplise.R;  
  4. import com.unity3d.player.UnityPlayer;  
  5.   
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.Button;  
  10. import android.widget.LinearLayout;  
  11.   
  12.   
  13. /*我们这里让我们定义的MainActivity继承自Unity导出项目中的UnityPlayerNativeActivity*/  
  14. public class MainActivity extends UnityPlayerNativeActivity   
  15. {  
  16.     private Button BtnZoomIn,BtnZoomOut;  
  17.       
  18.     @Override   
  19.     protected void onCreate (Bundle savedInstanceState)  
  20.     {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.         //获取显示Unity视图的父控件  
  24.         LinearLayout mParent=(LinearLayout)findViewById(R.id.UnityView);  
  25.         //获取Unity视图  
  26.         View mView=mUnityPlayer.getView();  
  27.         //将Unity视图添加到Android视图中  
  28.         mParent.addView(mView);  
  29.           
  30.         //放大  
  31.         BtnZoomIn=(Button)findViewById(R.id.BtnZoomIn);  
  32.         BtnZoomIn.setOnClickListener(new OnClickListener()  
  33.         {  
  34.             @Override  
  35.             public void onClick(View arg0)   
  36.             {  
  37.                 UnityPlayer.UnitySendMessage("Main Cube","ZoomIn","");  
  38.             }  
  39.         });  
  40.         //缩小  
  41.         BtnZoomOut=(Button)findViewById(R.id.BtnZoomOut);  
  42.         BtnZoomOut.setOnClickListener(new OnClickListener()  
  43.         {  
  44.             @Override  
  45.             public void onClick(View arg0)   
  46.             {  
  47.                 UnityPlayer.UnitySendMessage("Main Cube","ZoomOut","");  
  48.             }  
  49.         });  
  50.     }  
  51. }  
在这段代码中,我们通过UnitySendMessage调用了Unity中定义的两个方法ZoomIn、ZoomOut。好了,接下来,我们修改配置文件,使主Activity对应于MainActivity类,打开
AndroidManifest.xml文件:

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"   
  3.     package="com.android.unity2ecplise"   
  4.     android:theme="@android:style/Theme.NoTitleBar"   
  5.     android:versionName="1.0" android:versionCode="1"   
  6.     android:installLocation="preferExternal">  
  7.   <supports-screens android:smallScreens="true"   
  8.       android:normalScreens="true"   
  9.       android:largeScreens="true"   
  10.       android:xlargeScreens="true"   
  11.       android:anyDensity="true" />  
  12.   <application android:icon="@drawable/app_icon"   
  13.       android:label="@string/app_name"   
  14.       android:debuggable="false">  
  15.     <activity android:label="@string/app_name"   
  16.         android:screenOrientation="fullSensor"   
  17.         android:launchMode="singleTask"   
  18.         android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"   
  19.         android:name="com.android.unity2ecplise.MainActivity">  
  20.       <intent-filter>  
  21.         <action android:name="android.intent.action.MAIN" />  
  22.         <category android:name="android.intent.category.LAUNCHER" />  
  23.       </intent-filter>  
  24.       <meta-data android:name="unityplayer.UnityActivity" android:value="true" />  
  25.       <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />  
  26.     </activity>  
  27.   </application>  
  28.   <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" />  
  29.   <uses-feature android:glEsVersion="0x00020000" />  
  30. </manifest>  
如我们所猜测的那样,acivity节点的name属性对应于com.android.unity2ecplise.UnityPlayerNativeActivity。这说明在此之前,Android是以这个类作为主Acivity。现在我们将其改为我们自己定义的类,这样我们就能使用自己定义的布局。同时大家会注意到下面这两行:

[html]  view plain copy
  1. <meta-data android:name="unityplayer.UnityActivity" android:value="true" />  
  2. <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />  
之前我们在讲Unity视图嵌入Android视图时曾经碰到过Android视图无法取得焦点的问题,当时就是在配置文件中加入了这样两行代码,此时此刻,大家是不是和博主一样有种大彻大悟的感觉呢。我们运行程序,会发现界面像我们所希望看到的那样被改变了,而且我们可以通过两个按钮来改变视图中立方体的大小:





这样我们就可以认识到从Unity中导出的就是Android项目,我们可以直接将其修改来达到满足我们要求的目的。至于金曾玺老师的这种方法,在官方API文档中的确有提到过这种方法,不过博主尝试了好久,这种方法都失败了,不知道是不是因为Unity在新版本中已经解决了这个问题,从而可以直接导出可运行的Android项目。

        最后来说说网络上流传的一种方法,据说在Unity中将项目Build下就会在工程目录下的Temp文件夹下生成一个名为StagingArea文件夹,我们将这个文件夹在导入到Eclipse中并将其设为一个库,然后在新建的Android项目中引用这个库,并将这个项目中的assets文件夹覆盖新建项目中的assets文件夹,然后我们只要让主Acitivity继承Unity提供的Android接口中的UnityPlayerActivity即可。这种方法博主并没有去尝试,不过在Build的时候确实会产生这样一个文件夹,不过博主觉得这样是不是有点麻烦了啊,既然Unity导出的Android项目直接就能用,我们何必要再去用这种复杂的方法呢,不过我觉得大致的思路就是这样的,UnityPlayerActivity负责Acivity生命周期的维护,UnityPlayer负责渲染Unity场景中的内容,而我们在Unity中使用的资源都被Unity的引擎在内部进行了处理,总之把握了这些,Unity和Android的交互就基本没什么问题了。好了,谢谢大家关注我的博客,今天的内容就是这样了,希望大家喜欢。


每日箴言:有些路看起来很近,可是走下去却很远的,缺少耐心的人永远走不到头。人生,一半是现实,一半是梦想。 ——顾城




-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei

转载请注明出处,本文作者:秦元培, 本文出处:http://blog.csdn.net/qinyuanpei/article/details/39717795

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


本文源代码下载:Unity项目 Android项目


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值