各位朋友大家好,我是秦元培,歡迎大家關注我的博客,我的博客地址是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.
}
那麼,這樣我們就得到一個可以在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.
}
01.
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
02.
xmlns:tools=
"http://schemas.android.com/tools"
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>
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.
}
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>
1.
<meta-data
android:name=
"unityplayer.UnityActivity"
android:value=
"true"
/>
2.
<meta-data
android:name=
"unityplayer.ForwardNativeEventsToDalvik"
android:value=
"true"
/>
這樣我們就可以認識到從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的交互就基本沒什麼問題了。好了,謝謝大家關注我的博客,今天的內容就是這樣了,希望大家喜歡。