首页 >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. zhourui说:

    我按照上面说的,导出一个android项目,然后改代码,但是编译的时候报错,我把android版本改成了6.0又报下面这些错,这个要怎么弄
    Description Resource Path Location Type
    error: Error: String types not allowed (at ‘configChanges’ with value ‘mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density’). AndroidManifest.xml /UnityPlayerActivity line 5 Android AAPT Problem

  2. zhourui说:

    我新建了一个unity项目,然后导出为Android项目,UnityPlayerActivity.java也改了,但是导出为jar的时候总是报错,编译是报错AndroidManifest.xml:5: error: Error: String types not allowed (at ‘configChanges’ with value ‘mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density’).
    我修改了AndroidBuildTarget为Android6.0也一样报错,6.0在SDKmanager里面是完全安装了的,导出jar是报错JAR creation failed. See details for additional information.

    java/com/zhourui/test [in UnityPlayerActivity] is not on its project’s build path

    Unable to get package fragment root: UnityPlayerActivity/java/com/zhourui/test/UnityPlayerActivity.java

    java/com/zhourui/test [in UnityPlayerActivity] is not on its project’s build path

    java/com/zhourui/test [in UnityPlayerActivity] is not on its project’s build path

    Unable to get package fragment root: UnityPlayerActivity/java/com/zhourui/test/UnityPlayerActivity.java

    java/com/zhourui/test [in UnityPlayerActivity] is not on its project’s build path
    很是头疼啊,网上都查不到解决方法的

  3. avi9111说:

    而我的问题是用lua框架,正好需要File
    File.ReadAllBytes
    正在找其他解决方案

留下一个回复

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