2017-10-17 09:16:23 q764424567 阅读数 740
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

Unity3d项目要想在ios上发布,必须要有开发者账号,如果想在app store里发布,必须要花钱,这里我只是自己调试,做学习用。

要在真机上调试,首先你必须用iphone连接mac,我用的Unity版本是unity5.5,下面是我的发布过程,遇到了一些麻烦,所以写下来给大家做个参考。

1.用iphone连接上mac,发布必须要有发布ios的支持包,如果没有,要重新下载并安装,一开始我安装了之后打开的是复制到launchpad里面的app,所以还是没有ios发布这一项,只要打开原来的那个app就行了。然后点发布并运行,失败,直接点发布,发布为一个工程,用xcode打开。


2.然后运行,报如下错误:
Couldn't find developer disk image
这是因为xcode版本的问题,下载安装最新版本的xcode和系统兼容,然后再运行,报如下错误:
code signing is required for product type 'xxxxx' in SDK 'iOS 10.2’
这是因为项目的签名和开发者账号不一致,点击项目-Genernal,把Bundle Identifier改为你自己的开发者账号即可。
如果还有错误,那么参考这篇文章:点击打开链接

3.在手机上通用-设备,信任你自己的开发者账号即可。

2014-08-22 15:03:29 hackdjh 阅读数 4354
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

效果展示:


unity3d,嵌入,3d,android,展示0 unity3d,嵌入,3d,android,展示1unity3d,嵌入,3d,android,展示2

unity3d,嵌入,3d,android,展示3    unity3d,嵌入,3d,android,展示4     unity3d,嵌入,3d,android,展示5    unity3d,嵌入,3d,android,展示6


开篇废话:

我现在所在的Team每周需要一个人给大家介绍一个知识点,或者新技术。这礼拜正好轮到我了,由于我工作才一年,面对那帮老鸟讲知识点感觉有点作死。所以我就准备选个新技术介绍一下。

由于我在大学里自学过一段时间Unity3D,所以我想介绍的技术就是它,但我现在做的是应用开发,不能做个小游戏去给大家演示。所以我想到比较简单,直观,而且有可能真正能用到的就是在Android应用中展示3D模型。比如在产品展示时直接把这个产品的3D模型展示出来而不是个图片,效果应该非常棒(OpenGL应该也可以做)。

思路定下以后就发现大学时学的Unity3D的内容基本忘光了,虽然偶尔有Unity3D的文章都会点开看看,但还是得重新学。记得当时学Unity3D的时候看过一个叫雨松MOMO的博客。那时年轻,懵懂,找不到方向的我还给雨松大神发了一封邮件去请教大学应该怎么学习和做游戏相关的问题,结果人家没回,导致我更加失落于是接着学android去了。。。又扯远了。。。于是我又找到他的博客,把Unity3D基础部分的相关文章都看了一遍。

但是他博客里有介绍如何在Unity3D中调用Android,而我想做的是在Android中调用Unity3D,而且是把Unity3D嵌套在ANDROID的视图里面。最后费了九牛二虎之力才把这个Demo做出来。


准备工作:

下面是我总结的流程,目的是使本文思路更加清晰一些:

1.Android端代码可以在Eclipse中开发(AndroidStudio没有试,应该也可以)

2.Unity3D端代码要在Unity中开发

3.Android和Unity3D端,两边都需要加入一些代码从而可以使之关联交互。

4.将Android端代码编译成jar文件以插件形式放入到Unity端中

5.在Unity中将整个项目Build成apk文件,然后安装到手机或模拟器里运行

本文主要讲解1,2,3。对于4,5建议大家去看雨松MOMO的Unity博客的第17篇和第18篇。


UnityPlay:

在编写Android端和Unity3d端代码前,有必要先了解一下可以使两部分交互的类UnityPlay。

个人理解UnityPlay是个Unity提供给外部交互的一个接口类。

为什么是“个人理解”?这我不得不爆粗口了,TMD官网根本就没有相关的API和文档(如果大家有谁找到一定给我来一份,就当我骂自己了)。

在关联Android时,想拿到UnityPlay以及相关类的jar包可以从下面的地址找到:Unity安装路径\Editor\Data\PlaybackEngines\androidplayer\bin在bin文件夹下有一个classes.jar的jar文件,它就是我们想要的。

而在bin同目录下有一个src文件,点击到最后有3个类,分别是UnityPlayerActivity.java,UnityPlayerProxyActivity.java,UnityPlayerNativeActivity.java。前两个打开个后只有一行代码,说的是UnityPlayerActivity和UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity。而打开UnityPlayerNativeActivity中居然有代码,而且我估计这应该是UnityPlayerNativeActivity的源码。

由于关于UnityPlay的资料我只找到这么一个,所以我把UnityPlayerNativeActivity中的代码都贴出来,如果我注解有不对的地方希望大家指正。

  1. /** 
  2.  * UnityPlayerActivity,UnityPlayerProxyActivity都继承自UnityPlayerNativeActivity 
  3.  * 而UnityPlayerNativeActivity继承自NativeActivity 
  4.  * 在该类里定义了一些和ANDROID生命周期相同的回调方法,留给自定义的Activity子类重写。 
  5.  */  
  6. public class UnityPlayerNativeActivity extends NativeActivity  
  7. {  
  8.     //UnityPlayer的引用,并且我们不能改变这个引用变量的名字,它被native code所引用  
  9.     protected UnityPlayer mUnityPlayer;  
  10.   
  11.     protected void onCreate (Bundle savedInstanceState)  
  12.     {  
  13.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  14.         super.onCreate(savedInstanceState);  
  15.         // 设置显示窗口参数  
  16.         getWindow().takeSurface(null);  
  17.         setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);  
  18.         getWindow().setFormat(PixelFormat.RGB_565);  
  19.   
  20.         // 创建一个UnityPlayer对象,并赋值给全局的引用变量  
  21.         mUnityPlayer = new UnityPlayer(this);  
  22.         //为UnityPlayer设置一些参数  
  23.         if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar"true))  
  24.             getWindow ().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,  
  25.                                    WindowManager.LayoutParams.FLAG_FULLSCREEN);  
  26.   
  27.         int glesMode = mUnityPlayer.getSettings().getInt("gles_mode"1);  
  28.         boolean trueColor8888 = false;  
  29.         // UnityPlayer.init()方法需要在将view附加到layout之前调用。它将会调用native code  
  30.         mUnityPlayer.init(glesMode, trueColor8888);  
  31.           
  32.         // 从UnityPlayer中获取到Unity的View视图  
  33.         View playerView = mUnityPlayer.getView();  
  34.         // 将Unity视图加载到根视图上  
  35.         setContentView(playerView);  
  36.         // 使Unity视图获取焦点  
  37.         playerView.requestFocus();  
  38.     }  
  39.     protected void onDestroy ()  
  40.     {  
  41.         // 当Activity结束的时候调用UnityPlayer.quit()方法,它会卸载之前调用的native code  
  42.         mUnityPlayer.quit();  
  43.         super.onDestroy();  
  44.     }  
  45.   
  46.     // 下面几个方法都是ANDROID相关回调方法,确保在ANDROID执行相应方法时UnityPlayer也需调用相应方法  
  47.     protected void onPause()  
  48.     {  
  49.         super.onPause();  
  50.         mUnityPlayer.pause();  
  51.     }  
  52.     protected void onResume()  
  53.     {  
  54.         super.onResume();  
  55.         mUnityPlayer.resume();  
  56.     }  
  57.        
  58.     public void onConfigurationChanged(Configuration newConfig)  
  59.     {  
  60.         super.onConfigurationChanged(newConfig);  
  61.         mUnityPlayer.configurationChanged(newConfig);  
  62.     }  
  63.     public void onWindowFocusChanged(boolean hasFocus)  
  64.     {  
  65.         super.onWindowFocusChanged(hasFocus);  
  66.         mUnityPlayer.windowFocusChanged(hasFocus);  
  67.     }  
  68.     public boolean dispatchKeyEvent(KeyEvent event)  
  69.     {  
  70.         if (event.getAction() == KeyEvent.ACTION_MULTIPLE)  
  71.             return mUnityPlayer.onKeyMultiple(event.getKeyCode(), event.getRepeatCount(), event);  
  72.         return super.dispatchKeyEvent(event);  
  73.     }  
  74. }  
看完这个类后就知道了为什么在自定义的Activity中继承了UnityPlayerActivity等类以后,只要重写了onCreate并调用super.onCreate()方法后不需要任何其他的代码就会自动的显示出Unity3D的视图。因为初始化Unity视图的代码都在UnityPlayerNativeActivity父类中实现了。 

ANDROID端代码:

在写ANDROID代码的时候,一定要导入Unity3D提供给我们的jar包,jar包的位置我在上面说了。引入jar包加入到buildpath中这些最基本的我就不多说了。

要想和Unity交互,我们就不能继承ANDROID提供给我们的Activity,我们需要继承刚才jar包中引入的Unity提供的Activity类,一共有这么3个:

UnityPlayerActivity,UnityPlayerProxyActivity,UnityPlayerNativeActivity。具体区别不知道,因为没有文档,没有API,没有源码(这里再次鄙视一下)。刚才我们看过UnityPlayerNativeActivity的代码(虽然很短,但我觉得这个就是源码),知道UnityPlayerActivity,UnityPlayerProxyActivity都是它的子类,而且最终父类为NativeActivity。所以我们继承Unity提供的最外层的子类是最好的选择,我这里选择的是UnityPlayerActivity,因为名字最简单,觉得该封装的都应该封装好了。

  1. public class MainActivity extends UnityPlayerActivity {  
  2.       
  3.     private Button topButton;  
  4.     private Button bottomButton;  
  5.       
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.           
  10.         // 设置test为我们的根布局  
  11.         setContentView(R.layout.test);  
  12.           
  13.         // 通过刚才的源码分析,知道mUnityPlayer为一个全局的引用变量,而且已经在父类中设置好了,所以直接拿来用就可以了  
  14.         View playerView = mUnityPlayer.getView();  
  15.         // 将Unity的视图添加到我们为其准备的父容器中  
  16.         LinearLayout ll = (LinearLayout) findViewById(R.id.unityViewLyaout);  
  17.         ll.addView(playerView);  
  18.           
  19.         // 上面的button设置监听器  
  20.         topButton = (Button) findViewById(R.id.topButton);  
  21.         topButton.setOnClickListener(new View.OnClickListener() {  
  22.   
  23.             @Override  
  24.             public void onClick(View v) {  
  25.                 //发送消息给Unity端,该函数第一个参数为接受消息的类对象,第二个该类对象用接受消息的方法,第三个参数为传递的消息  
  26.                 //所以下面的意思就为:调用Main Camera下面的Previous方法,传送的消息为空  
  27.                 UnityPlayer.UnitySendMessage("Main Camera","Previous","");   
  28.             }  
  29.         });  
  30.           
  31.         // 为下面的button设置监听器  
  32.         bottomButton = (Button) findViewById(R.id.bottomBtn);  
  33.         bottomButton.setOnClickListener(new View.OnClickListener() {  
  34.               
  35.             @Override  
  36.             public void onClick(View v) {  
  37.                 //调用Main Camera下面的Next方法,传送的消息为空  
  38.                 UnityPlayer.UnitySendMessage("Main Camera","Next","");     
  39.             }  
  40.         });  
  41.     }  
  42. }  

最后看一下Android端的布局文件,布局很简单,上下各有一个button按钮,两个按钮中间是Unity的视图。

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <Button  
  8.         android:id="@+id/topButton"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_alignParentTop="true"  
  12.         android:text="PREVIOUS" />  
  13.   
  14.     <LinearLayout  
  15.         android:id="@+id/unityViewLyaout"  
  16.         android:layout_width="match_parent"  
  17.         android:layout_height="match_parent"  
  18.         android:layout_above="@+id/bottomBtn"  
  19.         android:layout_below="@+id/topButton"  
  20.         android:orientation="horizontal" >  
  21.     </LinearLayout>  
  22.   
  23.     <Button  
  24.         android:id="@+id/bottomBtn"  
  25.         android:layout_width="match_parent"  
  26.         android:layout_height="wrap_content"  
  27.         android:layout_alignParentBottom="true"  
  28.         android:text="NEXT" />  
  29.   
  30. </RelativeLayout>  
Android端的代码就介绍完了,很简单。唯一的难点就是UnityPlayerActivity和UnityPlayer的使用,就这两个破玩意花了我好几天的时间,很简单的东西不知道为什么官方不给个文档或者API(也可能我太挫没找到。。。) 

Unity3D端代码:

先看一下我的项目结构:

unity3d,嵌入,3d,android,展示7

JavaScript存放的是脚本

Models存放的是我在Assert Store中下载的免费的一些模型文件

Plugins下是我的Android工程,具体做法参考网上教程(这里推荐雨松大神的第17篇)

Prefab我是调整模型后定义的预制体

在场景中,我只有一个摄像机,和一个直射光。将脚本绑定到摄像机上,然后将之前调整好的5个预设模型添加到脚本的相应对象中。

unity3d,嵌入,3d,android,展示8

下面是脚本的代码,关于模型的旋转缩放是直接用了雨松MOMO的一篇文章中的代码,然后再加上了本例中的一些逻辑而组成的。

  1. #pragma strict  
  2.   
  3. //5个模型,从外部传入  
  4. var car : GameObject;  
  5. var helicopter : GameObject;  
  6. var suv : GameObject;  
  7. var plane : GameObject;  
  8. var tank : GameObject;  
  9.   
  10. //模型数组下标  
  11. private var index : int;  
  12. //模型数组  
  13. private var models : GameObject[];  
  14. //当前模型对象  
  15. private var mCurrentGameObject : GameObject;  
  16.   
  17. /******************************************/  
  18. /*分割线之下的变量用于触摸手势镜头控制旋转和缩放*/  
  19. /******************************************/  
  20.   
  21. //缩放系数  
  22. var distance = 10.0;  
  23. //左右滑动移动速度  
  24. var xSpeed = 250.0;  
  25. var ySpeed = 120.0;  
  26. //缩放限制系数  
  27. var yMinLimit = -20;  
  28. var yMaxLimit = 80;  
  29. //摄像头的位置  
  30. var x = 0.0;  
  31. var y = 0.0;  
  32. //记录上一次手机触摸位置判断用户是在左放大还是缩小手势  
  33. private var oldPosition1 : Vector2;  
  34. private var oldPosition2 : Vector2;  
  35.   
  36.   
  37. function Start () {  
  38.     //初始化模型数组  
  39.     index = 0;  
  40.     models = new GameObject[5];  
  41.     models[0] = car;  
  42.     models[1] = helicopter;  
  43.     models[2] = suv;  
  44.     models[3] = plane;  
  45.     models[4] = tank;  
  46.     //克隆一个初始模型对象  
  47.     mCurrentGameObject = Instantiate(models[index], Vector3(0,0,0), Quaternion.Euler(-20,0,0));   
  48.       
  49.     //初始化镜头参数  
  50.     var angles = transform.eulerAngles;  
  51.     x = angles.y;  
  52.     y = angles.x;  
  53. }  
  54.   
  55. function Update () {  
  56.   
  57.     //判断触摸数量为单点触摸  
  58.     if(Input.touchCount == 1)  
  59.     {  
  60.         //触摸类型为移动触摸  
  61.         if(Input.GetTouch(0).phase==TouchPhase.Moved)  
  62.         {  
  63.             //根据触摸点计算X与Y位置  
  64.             x += Input.GetAxis("Mouse X") * xSpeed * 0.02;  
  65.             y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;  
  66.    
  67.         }  
  68.     }  
  69.    
  70.     //判断触摸数量为多点触摸  
  71.     if(Input.touchCount > 1 )  
  72.     {  
  73.         //前两只手指触摸类型都为移动触摸  
  74.         if(Input.GetTouch(0).phase==TouchPhase.Moved||Input.GetTouch(1).phase==TouchPhase.Moved)  
  75.         {  
  76.                 //计算出当前两点触摸点的位置  
  77.                     var tempPosition1 = Input.GetTouch(0).position;  
  78.                 var tempPosition2 = Input.GetTouch(1).position;  
  79.                 //函数返回真为放大,返回假为缩小  
  80.                 if(isEnlarge(oldPosition1,oldPosition2,tempPosition1,tempPosition2))  
  81.                 {  
  82.                     //放大系数超过3以后不允许继续放大  
  83.                     //这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改  
  84.                        if(distance > 3)  
  85.                        {  
  86.                            distance -= 0.5;  
  87.                        }  
  88.                    }else  
  89.                 {  
  90.                     //缩小洗漱返回18.5后不允许继续缩小  
  91.                     //这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改  
  92.                     if(distance < 18.5)  
  93.                     {  
  94.                         distance += 0.5;  
  95.                     }  
  96.                 }  
  97.             //备份上一次触摸点的位置,用于对比  
  98.             oldPosition1=tempPosition1;  
  99.             oldPosition2=tempPosition2;  
  100.         }  
  101.     }  
  102. }  
  103.   
  104. //函数返回真为放大,返回假为缩小  
  105. function isEnlarge(oP1 : Vector2,oP2 : Vector2,nP1 : Vector2,nP2 : Vector2) : boolean  
  106. {  
  107.     //函数传入上一次触摸两点的位置与本次触摸两点的位置计算出用户的手势  
  108.     var leng1 =Mathf.Sqrt((oP1.x-oP2.x)*(oP1.x-oP2.x)+(oP1.y-oP2.y)*(oP1.y-oP2.y));  
  109.     var leng2 =Mathf.Sqrt((nP1.x-nP2.x)*(nP1.x-nP2.x)+(nP1.y-nP2.y)*(nP1.y-nP2.y));  
  110.     if(leng1 < leng2)  
  111.     {  
  112.          //放大手势  
  113.          return true;  
  114.     }else  
  115.     {  
  116.         //缩小手势  
  117.         return false;  
  118.     }  
  119. }  
  120.    
  121. //Update方法一旦调用结束以后进入这里算出重置摄像机的位置  
  122. function LateUpdate () {  
  123.    
  124.     //mCurrentGameObject为我们当前模型对象,缩放旋转的参照物  
  125.     if (mCurrentGameObject.transform) {          
  126.    
  127.         //重置摄像机的位置  
  128.          y = ClampAngle(y, yMinLimit, yMaxLimit);  
  129.         var rotation = Quaternion.Euler(y, x, 0);  
  130.         var position = rotation * Vector3(0.00.0, -distance) + mCurrentGameObject.transform.position;  
  131.    
  132.         transform.rotation = rotation;  
  133.         transform.position = position;  
  134.     }  
  135. }  
  136.    
  137. static function ClampAngle (angle : float, min : float, max : float) {  
  138.     if (angle < -360)  
  139.         angle += 360;  
  140.     if (angle > 360)  
  141.         angle -= 360;  
  142.     return Mathf.Clamp (angle, min, max);  
  143. }  
  144.   
  145. // 当android中按下next,显示下一个模型  
  146. function Next () {  
  147.     index = index+1;  
  148.     if (index > models.Length-1) {  
  149.         index = 0;  
  150.     }  
  151.     Debug.Log("next");  
  152.     // 摧毁当前对象  
  153.     Destroy(mCurrentGameObject);  
  154.     // 建立新的模型对象  
  155.     mCurrentGameObject = Instantiate(models[index]);  
  156. }  
  157.   
  158. // 当android中按下previous,显示上一个模型  
  159. function Previous () {  
  160.     index = index-1;  
  161.     if (index < 0) {  
  162.         index = models.Length-1;  
  163.     }  
  164.     Debug.Log("previous");  
  165.     // 摧毁当前对象  
  166.     Destroy(mCurrentGameObject);  
  167.     // 建立新的模型对象  
  168.     mCurrentGameObject = Instantiate(models[index]);  
  169. }  

最后就是在Unity3D中将工程Build成APK文件,然后再手机或模拟器中运行(如果手机或模拟器连着Eclipse则可以打出log方便调试找错)。

最后附上代码Demo:

Unity端代码太大了,所以我就把Android端和Unity端代码上传到百度云了。而apk文件上传到csdn,如果只想看效果的可以下载来试试。

代码点击下载

2016-10-31 17:00:42 zjw1349547081 阅读数 4709
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

1、集成步骤

1、下载最新版本ShareREC for Unity3D的SDK,解压以后可以找到ShareREC的unitypackage。

2、双击之并将其中的文件导入您的项目。

3、进入目录plugin/ShareRec,选择其中的ShareRec脚本,将它拖到游戏的MainCamera下,并填写上您的AppKey、AppSecret,并进行一些基本的配置,如下图:

srec_and_u3d_qi_3_150

其中MaxFrameSize表示录制出来的视频最大分辨率;VideoQuality表示视频质量,会根据视频分辨率自动调整视频码率;MinDuration是视频最短时长,单位为秒;RecordAudioFromMic表示从麦克风捕获声音输入;SoftwareAudioEncoder和SoftwareVideoEncoder表示强制使用软件音视频编码器对视频进行编码,勾选后兼容性更高;CacheFolder表示视频文件输出目录的路径。除了Appkey和AppSecret外的字段都是可选的。

2、添加代码

1、在启动视频录制前,需要先判断目标设备是否已被ShareREC支持:

此方法将返回一个bool结果,true则表示支持,否则为不支持。

3、启动、暂停、恢复和停止录制的方法分别为:

3、其它配置

1、录制结束后,您可以调用下面的方法上传和分享您的视频:

2、在分享前您还可以使用下面的方法为您的视频设置简短描述:

SetText的优先级高于AddCustomAttr,因此一旦两者都调用,后者将不起作用。

3、除了进入分享页面,您还可以在非录制期间进入个人资料页面和应用视频列表页面:

4、注意事项

1、ShareRec的分享功能基于ShareSDK,如果您需要自定义分享功能,可以参考ShareSDK的相关文档进行定制

2、ShareRec的短信验证功能基于SMSSDK,如果您需要自定义此模块,可以参考SMSSDK的相关文档

3、ShareRec只能在Android 4.0.4(或Level 14)以上的系统运行

4、ShareRec需要联网

2013-12-31 16:25:02 iteye_9512 阅读数 21
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚
简介

有一些手机功能,Unity没有提供相应的接口,例如震动,例如不锁屏,例如GPS,例如...

有太多的特殊功能Unity都没有提供接口,这时候,我们就需要通过使用Android原生的ADT编辑器去编写Java代码,然后打包jar导入到Unity工程,然后通过Unity提供的调用Android接口的函数去调用jar包中的函数即可。

这里我们就介绍一下使用Unity实现手机震动效果


实现内容简要说明:

1. Android提供jar文件,xml文件,res目录,libs目录

2. 通过AndroidJavaClass去调用Android函数



另外,这些功能只能在真机上运行!


具体实现:
Android部分

1.下载AndroidSDK(需要包含ADT编辑器的)

官方下载连接:http://developer.android.com/sdk/index.html





2 创建Android项目

菜单位置:File ->New->Android Application Project

创建过程中,只有一下这个界面的内容需要更改,其他的使用默认的就行
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7030/39359e56-3091-3cdd-85ba-f49e54bec91c.jpg[/img]
[/img]

生成后解决方案的目录如图
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7032/6365d43d-d2a3-38b2-b599-a40f385f17f4.jpg[/img]
[/img]

3. 导入Unity相关jar

Jar目录:unity的安装路径\Editor\Data\PlaybackEngines\androidplayer\bin下的classes.jar

通过AddExternalArchives添加,如图:
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7034/36ef6592-ae2a-3445-a778-d637afbe974d.jpg[/img]
[/img]

4. 导入jar后,就可以开始编辑相关的函数了,打开MainActivity
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7036/e18e12f8-db1c-3bc1-a416-c33f2d5bb461.jpg[/img]
[/img]

添加unityActivity,使MainActivity继承UnityPlayerActivity,并把SetContentView函数删掉(不然到手机上启动程序的时候就会只看到一句HelloWorld),这里用一个比较函数Max作为例子,有参数,有返回值

5. 到这里,需要的函数已经写完,再修改一下xml文件
Xml文件在工程文件目录下,叫AndroidManifest.xml
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7038/5464acc4-9f58-3e4e-84a2-6fff6b124f7b.jpg[/img]
[/img]

6. 接下来到处jar包
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7040/c0bf3eb0-7110-374d-9b3b-4cd5a1d7f991.jpg[/img]
[/img]
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7042/44aeb071-c731-314f-a05f-33c4ae432a82.jpg[/img]
[/img]
点击Export后选择Java目录下的Jar file,选择好路径之后按默认选项去到处jar文件即可


7. 到这里Android部分结束了


Unity部分

1. 设置PlayerSetting,要与Android的package对应上
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7044/9a34a7a6-2ff9-3418-b2ce-448713ff957c.jpg[/img]
[/img]

2. 在Assets目录下添加Plugins\Android目录,把Android的jar文件,res文件夹,libs文件夹,xml文件放入该目录
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7046/c60ae481-09f0-3d25-ba0e-25bdd1185455.jpg[/img]
[/img]

3. 调用相关函数
[img]
[img]http://dl2.iteye.com/upload/attachment/0092/7048/64d2ea90-a9a7-3606-9a47-a01275db6307.jpg[/img]
[/img]
(注:初步了解这里的参数之所以填”com.unity3d.player.UnityPlayer”和”currentActivity”,是因为在生成jar文件的时候,已经指定了MainActivityclass继承UnityPlayerActivity,也在XML指定了MainActivity是Main class)
2016-11-18 16:19:34 a770150657 阅读数 2448
  • Unity3D入门到精通-(3)Unity资源管理精讲

    本次系列课程的目标是让Unity3D初学者掌握Unity3d的资源管理技术进行了全面介绍,特别对AssetBundle资源如何进行更新,以及加载(依赖资源加载)进行了系统的介绍。 适合对象:Unity初学开发者,Unity中级开发者,网络程序开发者,所有对游戏开发有兴趣的人员。 学习条件:有一定的Unity3D基础,了解C#的基本开发知识。

    4645 人正在学习 去看看 张刚

Android端

public void SelectPhoto()
   {


       //Intent就是应用之间,应用不同Activity之间交互。
       Intent getAlbum = new Intent(Intent.ACTION_GET_CONTENT);  //新建Intent,让用户选择特定类型的数据,并返回该数据的URI.


       getAlbum.setType("image/*");  //类型为图片


       startActivityForResult(getAlbum, 108); //调用相册,结果码108,如果有结果会返回108,这个值随便设置,只要>=0就行
   }




   //重写Activity里的onActivityResult方法,这个方法和startActivityForResult是一对出现。
   @Override
   protected void onActivityResult(int requestCode, int resultCode, Intent intent) {


       // 用户没有进行有效的设置操作,返回系统固定的常量RESULT_CANCELED=0
       if (resultCode == RESULT_CANCELED) {
           Toast.makeText(getApplication(), "没有选择图片", Toast.LENGTH_LONG).show();
           return;
       }


       switch (requestCode) {
           case 108:
               Toast.makeText(getApplication(), "图片挑选完成", Toast.LENGTH_LONG).show();
               //裁剪图片
               CropPhoto(intent.getData());//获取资源的Uri,安卓Uri通用资源标识符,表示一个图片或视频
               break;
           case 109:
               Toast.makeText(getApplication(), "图片裁剪完成", Toast.LENGTH_LONG).show();
               //保存图片
               //可能抛出异常方法IOException方法必须用Try
               try {
                   SaveImage(intent);
               }
               catch (IOException e) {
                   e.printStackTrace();
               }
               break;
       }
       super.onActivityResult(requestCode, resultCode, intent);
   }


   //裁剪图片
   public void CropPhoto(Uri uri) {
       //调用裁剪器
       Intent intent = new Intent("com.android.camera.action.CROP");
       intent.setDataAndType(uri, "image/*");
       // 开启裁剪功能
       intent.putExtra("crop", "true");
       //设置宽高的比例
       intent.putExtra("aspectX", 1);
       intent.putExtra("aspectY", 1);
       //裁剪图片宽高
       intent.putExtra("outputX", 100);
       intent.putExtra("outputY", 100);
       //请求返回数据
       intent.putExtra("return-data", true);


       startActivityForResult(intent, 109);  //结果码109
   }




    //保存裁剪图片供U3D读取 使用 FileOutputStream 必须要捕获和处理错误 抛出异常IOException
   private void SaveImage(Intent intent) throws IOException {
       //获取intent传过来的数据
       //Bundle类是键值对形式,传递数据  Intent也可以传递数据,两个区别:intent是Bundle的封装
       //Intent主要数据传递用,bundle主要存取数据用。
       Bundle extras = intent.getExtras();


       if (extras != null) {
           //取出数据
           Bitmap bitmap = extras.getParcelable("data");


           //文件路径=U3D里的Application.persistentDataPath路径
           File destDir = new File("/mnt/sdcard/Android/data/com.zou.chongyang/files");
           //如果没有这个目录,创建一个
           if(!destDir.exists())
           {
               destDir.mkdirs();
           }
           //创建图片数据输出流
           FileOutputStream bitmapOut = new FileOutputStream("/mnt/sdcard/Android/data/com.zou.chongyang/files/headImage.png");
           //压缩图片 参数1:图片格式    参数2:压缩率100表示不压缩   参数3:写入输出流
           bitmap.compress(Bitmap.CompressFormat.PNG, 100, bitmapOut);
           //讲输出流刷新写出到磁盘
           bitmapOut.flush();
           //关闭流
           bitmapOut.close();


           //通知U3D可以读取图片了  参数1:U3D物体名称   参数2:方法名   参数3:要传递的参数,这里传图片名称
           UnityPlayer.UnitySendMessage("headImage","AndroidSaveHeadImageOver","headImage.png");


       }
   }


unity3d 端


  public void OnSelectPhoto()
    {
        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");   //获取unity的Java类,只能调用静态方法,获取静态属性
        AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");      //获取当前的Activity对象,能调用公开方法和公开属性      
        //一开始就调用安卓选择图片
        jo.Call("SelectPhoto"); 
    
    }

Android读取照片后,传递给unity3d里面的方法,显示照片

void AndroidSaveHeadImageOver(string str)
    {
       
        StartCoroutine(LoadTexture(str));


    }
    //从安卓中读取贴图
    IEnumerator LoadTexture(string name)
    {
    
        string path = "file://" + Application.persistentDataPath + "/" + name;


        WWW www = new WWW(path);
        while (!www.isDone)
        {
            //没有完成什么都不做,等待
        }
        yield return www;
        //显示贴图
        GetComponent<RawImage>().texture = www.texture;
    }

没有更多推荐了,返回首页