各位朋友大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是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的交互就基本没什么问题了。好了,谢谢大家关注我的博客,今天的内容就是这样了,希望大家喜欢。