[Unity3D]Unity3D游戏开发Android内嵌视图Unity查看

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

   喜欢我的博客请记住我的名字:秦元培。我博客地址是blog.csdn.net/qinyuanpei

   转载请注明出处,本文作者:秦元培。本文出处:http://blog.csdn.net/qinyuanpei/article/details/39380717

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

   

   大家好。我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei。近期博主发现博主的文章被非常多的站点(如游戏蛮牛)等非法转载,在转载的过程中博主原来的文章出处和链接均被去掉,更让博主认为生气的事情是某些人将博主的文章复制一部分以原创文章的形式发表在CSDN上,这样CSDN在依据Tag推荐相关文章的时候。就会将一部分文章连接到博主博客以外的地方,博主并非一个小气的人,仅仅是这些博客都是倾注了博主自己心血而写成的。如此作践难免让博主本人心寒。从今天起,博主全部的文章、配图都会加上链接,希望以此告诫那些非法转载博主文章的人懂得自重、懂得尊重博主的劳动成果。

   好了。这些人我暂且不去理会,今天我们继续来研究Unity在Android平台上的扩展,通过昨天的学习,大家已经知道Unity和Android是能够互相调用的。但是相信大家从昨天的文章中能够看出。假设单纯地从调用Android接口的角度来看,我们已经能够实现这一目的。但是从实际开发的角度来看,我们仅仅是迈出了非常小的一步。为什么这么说呢。由于在实际的开发中可能我们不仅须要从接口上实现与Unity的对接,并且须要从界面上实现与Unity的对接。比方,如今主流的网游都会在游戏開始的时候给玩家一个选择创建角色的过程,通常界面上会显示各种类型的角色设定,玩家能够通过界面了解每种类型的角色的特点,从而选择合适自己的角色。从技术上来讲,这一部分我们能够全然使用Unity3D的GUI系统来实现。只是本文的目的在于探讨Unity和Android的对接问题,因此我们能够假定Android在整个对接过程中扮演着界面渲染的角色,而Unity则负责游戏逻辑的维护。

那么,这样就诞生了我们今天的问题,能不能将Unity作为Android界面的一部分嵌入到Android应用中呢?答案当然是肯定的。


    一、为Unity编写Android插件

    首先。我们来看Unity中为Android提供的类文件UnityPlayerNativeActivity.java,该文件位于例如以下位置:

 D:ProgramFilesUnityEditorDataPlaybackEnginesandroidplayercomunity3dplayer(在不同的计算机上可能会有所不同,大家灵活运用就可以)

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);
		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); }
}
熟悉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是什么呢?我们打开它的文件:

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:

<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>

这里我们放置了两个Buttonbutton,分别用来控制Unity向前、向后切换模型,在屏幕中间有一个叫做UnityView的线性布局,它将作为Unity视图的父控件,即我们会将Unity的视图放到这个控件里。

我们注意到在UnityPlayerNativeActivity.java类中有一个UnityPlayer类型的变量mUnityPlayer,该类型博主眼下没有找到官方的相关说明。博主猜測它应该是负责Unity界面渲染的一个类吧,在这个类中有一个getView()方法,它将返回一个View类型的值。我们便能够通过这样的方法将其加入到Android视图里,由于我们已经准备好了一个UnityView的父控件,所以这一切实现起来变得相当地easy:

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文件:
<?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渲染,基本的脚本代码例如以下:

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视图中的button无法取得焦点啊,点击事件无效。

这是为什么呢?后来博主找到的解决方法是在AndroidManifest.xml文件里的activity子节点下添加例如以下两行代码:

            <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文件例如以下:

<?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很多其它的内容。大家能够參考官方的文档:点击这里.好了,今天的内容就是这样啦,希望大家喜欢。

最后再来看看程序执行的效果吧



每日箴言:这年头连梦想都是有期限的。有想法就去做,即使失败了都比没做来得好,由于失败至少给了你经验。没做仅仅会带给你后悔。

——朱德庸





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

   喜欢我的博客请记住我的名字:秦元培。我博客地址是blog.csdn.net/qinyuanpei

   转载请注明出处,本文作者:秦元培。本文出处:http://blog.csdn.net/qinyuanpei/article/details/39348677

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

   

 为方便大家研究这篇文章。最后给出源码:

Unity项目源码下载

Android项目源码下载





版权声明:本文博主原创文章,博客,未经同意不得转载。转载请注明作者和出处,谢谢。

原文地址:https://www.cnblogs.com/mengfanrong/p/4798143.html