Unity3D遊戲開發之從Unity3D到Eclipse


各位朋友大家好,我是秦元培,歡迎大家關注我的博客,我的博客地址是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:

 

01.using UnityEngine;
02.using System.Collections;
03. 
04.public class CubeScripts : MonoBehaviour {
05. 
06./// <summary>
07./// 定義旋轉速度
08./// </summary>
09.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的類,該類定義如下:

 

01.package com.android.unity2ecplise;
02. 
03.import com.unity3d.player.*;
04.import android.app.NativeActivity;
05.import android.content.res.Configuration;
06.import android.graphics.PixelFormat;
07.import android.os.Bundle;
08.import android.view.KeyEvent;
09.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,它的代碼定義如下:

 

 

01.<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
03.android:layout_width="match_parent"
04.android:layout_height="match_parent"
05.android:orientation="vertical"
06.tools:context=".MainActivity" >
07.<Button
08.android:id="@+id/BtnZoomIn"
09.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:

 

 

01.package com.android.unity2ecplise;
02. 
03.import com.android.unity2ecplise.R;
04.import com.unity3d.player.UnityPlayer;
05. 
06.import android.os.Bundle;
07.import android.view.View;
08.import android.view.View.OnClickListener;
09.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文件:

 

 

01.<?xml version="1.0" encoding="utf-8"?>
02.<manifest xmlns:android="http://schemas.android.com/apk/res/android"
03.package="com.android.unity2ecplise"
04.android:theme="@android:style/Theme.NoTitleBar"
05.android:versionName="1.0" android:versionCode="1"
06.android:installLocation="preferExternal">
07.<supports-screens android:smallScreens="true"
08.android:normalScreens="true"
09.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。現在我們將其改爲我們自己定義的類,這樣我們就能使用自己定義的佈局。同時大家會注意到下面這兩行:

 

 

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的交互就基本沒什麼問題了。好了,謝謝大家關注我的博客,今天的內容就是這樣了,希望大家喜歡。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章