首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之Android同步方法读取streamingAssets(八十八)
2016
04-28

Unity3D研究院之Android同步方法读取streamingAssets(八十八)

版本Unity5.3.3

Android 小米pad1

首先非常感谢 @守着阳光 同学在下面的留言。让我解决了一个大的谜团。。

 

开始我知道 StreamingAssets 路径是这个 path = “jar:file://” + Application.dataPath + “!/assets/”;

文档在这里: http://docs.unity3d.com/Manual/StreamingAssets.html

后来我知道了一个新API Application.streamingAssetsPath

Application.streamingAssetsPath 其实就等于 “jar:file://” + Application.dataPath + “!/assets/”;

然而问题就出现在这个路径上。我打印了一下LOG

Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assets

Application.dataPath+”!assets” = /data/app/com.xxx.xxx-1.apk!assets

也就是说Application.streamingAssetsPath  多了一个   jar:file://

那么如果想在Android上同步方法AssetBundle.LoadFromFile 就得用 Application.dataPath+”!assets”这个路径。

从此这段代码就正常了。

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

	public SpriteRenderer spriteRenderer;
	void Start () {

		// /data/app/com.xxx.xxx-1.apk!assets/yusong.unity3d
		string path = Application.dataPath+"!assets/yusong.unity3d";


		AssetBundle assetbundle = AssetBundle.LoadFromFile(path);

		Sprite sprite = assetbundle.LoadAsset<Sprite>("0");

		spriteRenderer.sprite =sprite; 
	}

}

Unity的坑啊~ 55555555555555555555

还有这个路径只能用来AssetBundle.LoadFromFile 。如果想用File类操作。 比如File.ReadAllText  或者 File.Exists  Directory.Exists 这样都是不行的。

———————————-!!从今天以后下面的代码已经可以作废了!!—————————

streamingAssets 这个目录在IOS下是可以同步读取的,但是在Android下必须用www来异步读取。。这就很恶心了~所以最近我就在想办法如何能在Android下也能同步读取。如下图所示,我把一个sprite打成assetbundle并且放在StreamingAssets目录下。

Snip20160428_1

assetbundle的压缩格式 ,我使用的是unity5.x的lz4方式。

	[MenuItem ("Assets/Build AssetBundles")]
	static void BuildAllAssetBundles ()
	{
		BuildPipeline.BuildAssetBundles ("Assets/StreamingAssets",BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.Android);

		AssetDatabase.SaveAssets ();
		AssetDatabase.Refresh();
	}

然后创建一个3D Sprite 在Hierarchy里 试图把这个ab里的sprite加载上去。

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {

	public SpriteRenderer spriteRenderer;
	void Start () {
//注释掉的代码是 unity自己的同步方式, 但是在Android上不行, 可是在IOS上可以
//		AssetBundle assetbundle = 	AssetBundle.LoadFromFile(Application.streamingAssetsPath +"/yusong.unity3d");
//
//		Sprite sprite = assetbundle.LoadAsset<Sprite>("0");
//
//		spriteRenderer.sprite =sprite; 


		//以下代码通过JAVA代码来同步读取并且返回给unity
		AndroidJavaClass 	m_AndroidJavaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
		AndroidJavaObject	m_AndroidJavaObject  = null;
		if (m_AndroidJavaClass != null) {
			m_AndroidJavaObject = m_AndroidJavaClass.GetStatic<AndroidJavaObject>("currentActivity");
		}
			
		byte[] s = 	m_AndroidJavaObject.Call<byte[]>("LoadAB","yusong.unity3d");
		AssetBundle assetbundle = 	AssetBundle.LoadFromMemory(s);

		Sprite sprite = assetbundle.LoadAsset<Sprite>("0");
		spriteRenderer.sprite =sprite; 

	}

}

然后,把unity导出成android工程。。

Snip20160428_2

用eclipse打开刚刚导出的工程。找到UnityPlayerActivity.java类 添加如下代码

package com.yusong.momo;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import com.unity3d.player.*;

import android.app.Activity;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class UnityPlayerActivity extends Activity
{
	protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

	
	protected AssetManager assetManager;
	
	// Setup activity layout
	@Override protected void onCreate (Bundle savedInstanceState)
	{
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);

		getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy

		mUnityPlayer = new UnityPlayer(this);
		setContentView(mUnityPlayer);
		mUnityPlayer.requestFocus();
		
		assetManager = getAssets();
	
		
	}


	private byte[] readtextbytes(InputStream inputStream) 
	{

	  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//长度这里暂时先写成1024
	  byte buf[] = new byte [1024];

	  int len;

	  try {

	   while ((len = inputStream.read(buf)) != -1) {

	    outputStream.write(buf, 0, len);

	   }

	   outputStream.close();

	   inputStream.close();

	  } catch (IOException e) {

	  }
	  return outputStream.toByteArray();
	}
	
	

	
	//读取assetbund并且返回字节数组
	public byte[] LoadAB(String path)
	{
	
		 InputStream inputStream = null ;
		 
		  try {

			   inputStream = assetManager.open(path);

			  } catch (IOException e) {

			   Log.v ("unity", e.getMessage());

			  }

		  return readtextbytes(inputStream);
	}
	
	
	// 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); }
}

 

OK 大功告成, 我的sprite已经可以同步加载了。

如下图所示,那么实际上unity把已经把streamingAssets目录下的资源放在了android的assets目录下。

Snip20160428_3

 

那么我们同步加载的原理也是利用Android的AssetManager这个类来读取的。

刚和同事讨论了一下,如果有效率的问题,我们可以在ndk里读取assets下的资源。 比如向这样~  c#  调用  ndk 读取完直接返回给c# 这样就可以不通过java这一层。。

http://www.cppblog.com/johndragon/archive/2012/12/28/196754.html

最后希望大家可以帮忙多多测试看看,谢谢啦~~

 

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之Android同步方法读取streamingAssets(八十八)》有 77 条评论

  1. 石叶说:

    string path = Application.dataPath + “!/assets/bgm.unity3d”;AssetBundle asset = AssetBundle.LoadFromFile(path);这些代码我在安卓上测试根本不能去读。。。即便是txt文件,用LoadFromFile也无法读取,只能用www,不知道为什么。有代码例子吗

  2. 石叶说:

    .TXT文件有这个方法试,怎么不行啊。说文件未找到。

  3. 朱贤智说:

    求救,为什么我的加载不了。 string path = Application.dataPath “!assets/bgm.unity3d”; AssetBundle asset = AssetBundle.LoadFromFile(path);确定文件名以及文件没问题;5.3,4P5

  4. 朱贤智说:

    求救,为什么我的加载不了。 string path = Application.dataPath + “!assets/bgm.unity3d”; AssetBundle asset = AssetBundle.LoadFromFile(path);确定文件名以及文件没问题;5.3,4P5

  5. 后面更新的方法,果然把之前的不优雅的做法干掉了。~~

  6. 外聲说:

    我项目用的是assetmanager效率还可以,也不算太慢

  7. Android可以用AssetBundle.LoadFromFile,只是路径问题

  8. 郭袁说:

    我们之前也确实是这样实现的通过java直接读取byte[]。但是后边需要在后台线程加载资源 就必须用NDK去直接去数据,就你后边提到的ndk读取asset 目前有什么好的方案吗?

  9. 李幸涛说:

    AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath “/yusong.unity3d”);这个在android 上不行,不是因为路径的原因吗?? Application.streamingAssetsPath 在android 下的路径格式是 jar:///****用 Application.dataPath 拼一下路径, 再加载应该就可以了~

  10. 李幸涛说:

    AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath +”/yusong.unity3d”);这个在android 上不行,不是因为路径的原因吗?? Application.streamingAssetsPath 在android 下的路径格式是 jar:///****用 Application.dataPath 拼一下路径, 再加载应该就可以了~

  11. 太子说:

    AssetBundle.LoadFromMemory(s); 依然会产生WebStream ,AssetBundle.CreatFromFile还是不能用啊 你用LoadFromMemory 大ab包占内存,小的还行。

  12. long说:

    你好,请问这个方法比用AssetBundle.CreatFromFile好在哪儿

  13. 为何不直接放到 persist 目录呢? 这样也统一

    • Application.persistentDataPath 安装完包以后才有这个目录。。 没法提前放进去啊。。

      • 刘剑寒说:

        我知道的一般做法是,运行第一次的时候把所有Assetbundle拷贝到这个目录,后续更新也放到这个目录下

        • 我是把原始资源放在resources下。 加载资源的策略是优先查看assetbundle如果没有则去resources下加载。这样就不需要拷贝的。。 可是android平台下resources文件夹会被切割成很多份。感觉影响安装的速度, 所以准备用这种方式试试。。

          • 雨凇大大 既然StreamingAssets这个目录下可以用AssetBundle.LoadFromFile读取了,为什么还要拷贝到Application.persistentDataPath这个目录下呢?用AssetBundle.CreatFromFile 这个方式读取这两个目录 有什么区别呀?最后确认一个问题就是IOS和Android 都能用AssetBundle.LoadFromFile读取是吗?IOS:AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath +”/yusong.unity3d”); Android :AssetBundle assetbundle = AssetBundle.LoadFromFile( Application.dataPath + “!assets/yusong.unity3d”);

          • 雨凇大大 既然StreamingAssets这个目录下可以用AssetBundle.LoadFromFile读取了,为什么还要拷贝到Application.persistentDataPath这个目录下呢?用AssetBundle.CreatFromFile 这个方式读取这两个目录 有什么区别呀?最后确认一个问题就是IOS和Android 都能用AssetBundle.LoadFromFile读取是吗?IOS:AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath “/yusong.unity3d”); Android :AssetBundle assetbundle = AssetBundle.LoadFromFile( Application.dataPath “!assets/yusong.unity3d”);

  14. 张洁勇说:

    话说雨凇大大 mac下用的啥编辑器哇?

  15. Chiuan说:

    这样用蛮久了,效率ok的就是文章中您说用NDK里面替换? 看了那个文章介绍,有必要么? 和 android的am管理有啥区别

  16. 这样写有啥子好处么? Unity3D研究院之Android同步方法读取streamingAssets(八十八) - 雨松MOMO程序研究院 - 1

留下一个回复

你的email不会被公开。 必填项已用 * 标注。