赞
踩
转自:http://www.yxkfw.com/?p=7976
今 天我们继续来研究Unity在Android平台上的扩展,通过昨天的学习,大家已经知道Unity和Android是可以互相调用的,可是相信大家从昨 天的文章中可以看出,如果单纯地从调用Android接口的角度来看,我们已经可以实现这一目的。可是从实际开发的角度来看,我们只是迈出了很小的一步。 为什么这么说呢,因为在实际的开发中可能我们不仅需要从接口上实现与Unity的对接,而且需要从界面上实现与Unity的对接。比如,现在主流的网游都 会在游戏开始的时候给玩家一个选择创建角色的过程,通常界面上会显示各种类型的角色设定,玩家可以通过界面了解每种类型的角色的特点,从而选择合适自己的 角色。从技术上来讲,这一部分我们可以完全使用Unity3D的GUI系统来实现,不过本文的目的在于探讨Unity和Android的对接问题,因此我 们可以假定Android在整个对接过程中扮演着界面渲染的角色,而Unity则负责游戏逻辑的维护。那么,这样就诞生了我们今天的问题,能不能将 Unity作为Android界面的一部分嵌入到Android应用中呢?答案当然是肯定的。
一、为Unity编写Android插件
首先,我们来看Unity中为Android提供的类文件UnityPlayerNativeActivity.java,该文件位于如下位置:
D:\ProgramFiles\Unity\Editor\Data\PlaybackEngines\androidplayer\com\unity3d\player(在不同的计算机上可能会有所不同,大家灵活运用即可)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
package 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.View;
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);
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); }
}
|
熟悉Android开发的朋友一定对这个类觉得眼熟吧,NativeActivity是Google在Android3.0后推出的,其目的是为了让开发 者使用C/C++在NDK环境下管理Activity的生存周期,于是我们可以大胆的猜测Unity之所以可以和Android进行交互是因为NDK提供 了方便之门,果然,博主在Unity的目录下找到了
libmain.so、libunity.so、libmono.so这三个文件,这证实我们 的猜想是正确的。在这个类中,我们可以看到Unity做了大量的初始化工作,并且重写了NativeActvity的相关方法。不知道大家还记不记得我们 在前一篇文章中定义主Activity时,我们是让它继承自UnityPlayerActivity而不是Android的Activity。那么,这个 UnityPlayerActivity是什么呢?我们打开它的文件:
1
2
3
4
5
6
|
package com.unity3d.player;
/**
* @deprecated Use UnityPlayerNativeActivity instead.
*/
public class UnityPlayerActivity extends UnityPlayerNativeActivity { }
|
这时候我相信大家和博主一样都有一种恍然大悟的感觉,原来它是继承自UnityPlayerNativeActivity的,换句话说Unity在Android这部分其实做了两件事,即初始化Activity和重写相关的方法。我们注意到代码中的注释提示这个类已经deprecated建议我们使用UnityPlayerNativeActivity,这点我们暂时不用管,我们依然使用UnityPlayerActivity,因为它和UnityPlayerNativeActivity本质上是一样的。好了,到目前为止,如果大家已经理解了这里所有的内容,那么我们可以浩浩荡荡地冲向Eclipse开始编写Android项目了。
和昨天一样,我们创建一个Android项目并将其设为库,这里我们将其包名设为com.android.unityview4android。这个名 称很重要,我们在Unity中将继续使用这个名字。首先我们来创建一个Android的布局文件activity_main.xml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<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/BtnPre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:text="@string/BtnPre" />
<LinearLayout
android:id="@+id/UnityView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/BtnNext"
android:layout_below="@+id/BtnPre"
android:orientation="vertical" >
</LinearLayout>
<Button
android:id="@+id/BtnNext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/BtnNext" />
</RelativeLayout>
|
这里我们放置了两个Button按钮,分别用来控制Unity向前、向后切换模型,在屏幕中间有一个叫做UnityView的线性布局,它将作为Unity视图的父控件,即我们会将Unity的视图放到这个控件里。我们注意到在UnityPlayerNativeActivity.java 类中有一个UnityPlayer类型的变量mUnityPlayer,该类型博主目前没有找到官方的相关说明,博主推测它应该是负责Unity界面渲染 的一个类吧,在这个类中有一个getView()方法,它将返回一个View类型的值,我们便可以通过这种方法将其添加到Android视图里,因为我们 已经准备好了一个UnityView的父控件,所以这一切实现起来变得相当地容易:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package com.android.unityview4android;
/*导入Unity提供的类*/
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends UnityPlayerActivity {
private Button BtnPre,BtnNext;
@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);
//上一个
BtnPre=(Button)findViewById(R.id.BtnPre);
BtnPre.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
UnityPlayer.UnitySendMessage("GameObject", "ShowPrevious", "");
}
});
//下一个
BtnNext=(Button)findViewById(R.id.BtnNext);
BtnNext.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
UnityPlayer.UnitySendMessage("GameObject", "ShowNext", "");
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
|
好了,这样我们就完成了Jar库的编写,我们只要将Jar库导入到Unity中,并且按照上一篇文章中所述的方法构建Android插件目录组织相关资源就可以了。最后我们给出AndroidManifest.xml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.unityview4android"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.android.unityview4android.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
|
二、创建Unity项目
好了,现在我们来创建Unity项目,我们这里创建一个简单地场景,场景中只有一个空游戏体和摄像机,我们将通过脚本的形式来控制Unity渲染,主要的脚本代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
using UnityEngine;
using System.Collections;
public class ModelManager : MonoBehaviour {
//模型
public GameObject[] Models;
//当前索引
private int index=0;
//当前对象
private GameObject mObject;
void Start ()
{
this.name="GameObject";
//生成第一个物体
mObject=(GameObject)Instantiate(Models[index]);
}
public void ShowNext()
{
//使索引增加
index+=1;
//范围控制
if(index>Models.Length-1){
index=0;
}
//销毁原来物体
Destroy(mObject);
//生成新物体
mObject=(GameObject)Instantiate(Models[index]);
}
public void ShowPrevious()
{
//使索引减少
index-=1;
//范围控制
if(index<0){
index=Models.Length-1;
}
//销毁原来物体
Destroy(mObject);
//生成新物体
mObject=(GameObject)Instantiate(Models[index]);
}
}
|
这段脚本我们绑定到这个空的游戏体上,脚本的两个方法ShowNext()和ShowPrevious()相对应,通过UnitySendMessage()实现通信。好了,我们来运行项目,如图所示:
我 们可以注意到当程序打开的那一瞬间,是Android视图先加载、然后Unity视图才加载的,给人整体的感觉就是在Activity里面嵌套了一个 Activity。可是博主马上就发现了一个问题,这Android视图中的按钮无法取得焦点啊,点击事件无效。这是为什么呢?后来博主找到的解决方法是 在AndroidManifest.xml文件中的activity子节点下增加如下两行代码:
1
2
|
<meta-data android:name="android.app.lib_name" android:value="unity" />
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
|
这样问题是解决了,可是我们得搞懂为什么吧,在Unity官方文档中,我们找到了答案:
请 注意,NativeActivity在Android2.3以后被引入而且不支持该版本以下的设备。因为触摸/运动事件处理在本机代码,Java视图通常 不会看到这些事件。然而,在统一转发机制允许将事件传播到DalvikVM。为了使用这个机制,您需要修改manifest文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
<application android:icon="@drawable/app_icon" android:label="@string/app_name">
<activity android:name=".OverrideExampleNative"
android:label="@string/app_name">
<meta-data android:name="android.app.lib_name" android:value="unity" />
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
|
这段话的意思就是说Android的事件是运行在本机代码中的,Java视图是看不到这些事件的,然而我们可以通过Unity提供的统一转发机制将这些事件发送到Android虚拟机,为了使用这个机制,我们必须修改Android的AndroidManifest.xml文件。博主感觉Android插件这块官方目前做得不是很好,总是给人一种云里雾里的感觉,而且通过各种语言相互调用增加了调试的难度,关于Android更多的内容,大家可以参考官方的文档:点击这里.好了,今天的内容就是这样啦,希望大家喜欢。最后再来看看程序运行的效果吧
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。