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

喜欢我的博客请记住我的名字:秦元培,我的博客地址是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:

using UnityEngine;using System.Collections;public class CubeScripts : MonoBehaviour {/// <summary>/// 定义旋转速度/// </summary>public float RotateSpeed=45;/// <summary>/// 定义摄像机的最近距离/// </summary>private float mNear=2.5F;    /// <summary>/// 摄像机当前距离/// </summary>private float mDistance=5F;/// <summary>/// 定义摄像机的最远距离/// </summary>private float mFar=7.5F;/// <summary>/// 摄像机的缩放速率/// </summary>private float mZoomRate=0.5F;/// <summary>/// 主摄像机/// </summary>private Transform mCamera;/// <summary>/// 在Start()方法中我们设定了游戏体的名称,因为我们在/// Android项目中需要用到这个名称,同时获取主相机对象/// </summary>void Start () {this.name="Main Cube";mCamera=Camera.main.transform;}/// <summary>/// 在Update()方法中我们让Cube按照一定的速度进行旋转/// </summary>void Update () {transform.Rotate(Vector3.up * Time.deltaTime * RotateSpeed);}/// <summary>/// 定义一个放大的方法供外部调用/// </summary>public void ZoomIn(){mDistance-=mZoomRate;mDistance=Mathf.Clamp(mDistance,mNear,mFar);mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;}/// <summary>/// 定义一个缩小的方法供外部调用/// </summary>public void ZoomOut(){mDistance+=mZoomRate;mDistance=Mathf.Clamp(mDistance,mNear,mFar);mCamera.position=mCamera.rotation * new Vector3(0,0,-mDistance)+transform.position;}}
这段脚本十分地简单,没有什么可说的,在这里我们定义了两个方法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的类,该类定义如下:

package com.android.unity2ecplise;import com.unity3d.player.*;import android.app.NativeActivity;import android.content.res.Configuration;import android.graphics.PixelFormat;import android.os.Bundle;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.Window;import android.view.WindowManager;public class UnityPlayerNativeActivity extends NativeActivity{protected UnityPlayer mUnityPlayer;// don't change the name of this variable; referenced from native code// Setup activity layout@Override protected void onCreate (Bundle savedInstanceState){requestWindowFeature(Window.FEATURE_NO_TITLE);super.onCreate(savedInstanceState);getWindow().takeSurface(null);setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);getWindow().setFormat(PixelFormat.RGB_565);mUnityPlayer = new UnityPlayer(this);if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,                       WindowManager.LayoutParams.FLAG_FULLSCREEN);setContentView(mUnityPlayer);mUnityPlayer.requestFocus();}// Quit Unity@Override protected void onDestroy (){mUnityPlayer.quit();super.onDestroy();}// Pause Unity@Override protected void onPause(){super.onPause();mUnityPlayer.pause();}// Resume Unity@Override protected void onResume(){super.onResume();mUnityPlayer.resume();}// This ensures the layout will be correct.@Override public void onConfigurationChanged(Configuration newConfig){super.onConfigurationChanged(newConfig);mUnityPlayer.configurationChanged(newConfig);}// Notify Unity of the focus change.@Override public void onWindowFocusChanged(boolean hasFocus){super.onWindowFocusChanged(hasFocus);mUnityPlayer.windowFocusChanged(hasFocus);}// For some reason the multiple keyevent type is not supported by the ndk.// Force event injection by overriding dispatchKeyEvent().@Override public boolean dispatchKeyEvent(KeyEvent event){if (event.getAction() == KeyEvent.ACTION_MULTIPLE)return mUnityPlayer.injectEvent(event);return super.dispatchKeyEvent(event);}// Pass any events not handled by (unfocused) views straight to UnityPlayer@Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }@Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }@Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }/*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }}
相信大多数看过我博客的人都会觉得这个类有点眼熟吧,不错,在之前的 《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,它的代码定义如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context=".MainActivity" >    <Button        android:id="@+id/BtnZoomIn"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        android:text="@string/ZoomIn" />    <LinearLayout          android:id="@+id/UnityView"          android:layout_width="match_parent"          android:layout_height="match_parent"          android:layout_above="@+id/BtnZoomOut"          android:layout_below="@+id/BtnZoomIn"          android:orientation="vertical" >      </LinearLayout>    <Button        android:id="@+id/BtnZoomOut"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:text="@string/ZoomOut" /></RelativeLayout>
接下来,我们创建与之对应的Java类文件MainActivity.class:

package com.android.unity2ecplise;import com.android.unity2ecplise.R;import com.unity3d.player.UnityPlayer;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.LinearLayout;/*我们这里让我们定义的MainActivity继承自Unity导出项目中的UnityPlayerNativeActivity*/public class MainActivity extends UnityPlayerNativeActivity {private Button BtnZoomIn,BtnZoomOut;@Override protected void onCreate (Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//获取显示Unity视图的父控件LinearLayout mParent=(LinearLayout)findViewById(R.id.UnityView);//获取Unity视图View mView=mUnityPlayer.getView();//将Unity视图添加到Android视图中mParent.addView(mView);//放大BtnZoomIn=(Button)findViewById(R.id.BtnZoomIn);BtnZoomIn.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0) {UnityPlayer.UnitySendMessage("Main Cube","ZoomIn","");}});//缩小BtnZoomOut=(Button)findViewById(R.id.BtnZoomOut);BtnZoomOut.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View arg0) {UnityPlayer.UnitySendMessage("Main Cube","ZoomOut","");}});}}
在这段代码中,我们通过UnitySendMessage调用了Unity中定义的两个方法ZoomIn、ZoomOut。好了,接下来,我们修改配置文件,使主Activity对应于MainActivity类,打开
AndroidManifest.xml文件:

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

      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />      <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项目


更多相关文章

  1. android SurfaceView播放视频
  2. Android-Scroller
  3. Android(安卓)Studio项目.gitignore
  4. Android在整个项目中设置自定义字体
  5. AndroidAnnotations——Injecting Views视图注入
  6. android 深入理解LayoutInflater
  7. Libgdx桌面项目1
  8. Android日历周视图 可添加事件标记
  9. Android小项目之音乐播放器简易版

随机推荐

  1. Android MVP Pattern
  2. React Native apk打包下载(android)
  3. android studio 的下拉菜单Spinner使用详
  4. 基于OpenCV和OpenGL 的简易美颜相机
  5. android中的spannable的使用(TextView分段
  6. android UI小结(五)
  7. Android 组件安全
  8. android - ViewPager 监听左右滑动
  9. Error: Error parsing D:\android-sdk-w
  10. OpenMax在Android上的实现