最近做项目需要用到这个功能,就是在Unity中调用Android本地相册或直接打开摄像机拍照并且裁剪一部分用于用户头像,今天研究了一下,那么研究出成果了MOMO一定要分享给大家。Unity与Android的交互还有谁不会?? 如果有不会的朋友请看MOMO之前的文章喔,Unity3D研究院之打开Activity与调用JAVA代码传递参数(十八)这里有关交互的方式就不详细说明,主要将如何在Unity中打开摄像机、在Unity中打开本地相册,选一个照片后如何进行裁剪,最后将图片转换成Texture显示在U3D的世界当中。
首先看看Eclipse中的Android插件部分,我的包名是com.xys请大家与MOMO保持一致,Unity工程中也需要是这个包名噢。
UnityTestActivity.java 这个类是Unity的插件主类,在这里调用是打开摄像机 还是本地相册的方法。
package com.xys;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.unity3d.player.UnityPlayerActivity;
public class UnityTestActivity extends UnityPlayerActivity {
//public class UnityTestActivity extends Activity {
Context mContext = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
//Unity中会调用这个方法,用于区分打开摄像机 开始本地相册
public void TakePhoto(String str)
{
Intent intent = new Intent(mContext,WebViewActivity.class);
intent.putExtra("type", str);
this.startActivity(intent);
}
}
然后是WebViewActivity.java 这里主要处理用户打开摄像机或本地相册后如何进行裁剪图片,并且把裁剪的图片储存在本地文件中。
package com.xys;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import com.unity3d.player.UnityPlayer;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.KeyEvent;
import android.widget.ImageView;
public class WebViewActivity extends Activity
{
ImageView imageView = null;
public static final int NONE = 0;
public static final int PHOTOHRAPH = 1;// 拍照
public static final int PHOTOZOOM = 2; // 缩放
public static final int PHOTORESOULT = 3;// 结果
public static final String IMAGE_UNSPECIFIED = "image/*";
public final static String FILE_NAME = "image.png";
public final static String DATA_URL = "/data/data/";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imageView = (ImageView) this.findViewById(R.id.imageID);
String type = this.getIntent().getStringExtra("type");
//在这里判断是打开本地相册还是直接照相
if(type.equals("takePhoto"))
{
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg")));
startActivityForResult(intent, PHOTOHRAPH);
}else
{
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED);
startActivityForResult(intent, PHOTOZOOM);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == NONE)
return;
// 拍照
if (requestCode == PHOTOHRAPH) {
//设置文件保存路径这里放在跟目录下
File picture = new File(Environment.getExternalStorageDirectory() + "/temp.jpg");
startPhotoZoom(Uri.fromFile(picture));
}
if (data == null)
return;
// 读取相册缩放图片
if (requestCode == PHOTOZOOM) {
startPhotoZoom(data.getData());
}
// 处理结果
if (requestCode == PHOTORESOULT) {
Bundle extras = data.getExtras();
if (extras != null) {
Bitmap photo = extras.getParcelable("data");
imageView.setImageBitmap(photo);
try {
SaveBitmap(photo);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void startPhotoZoom(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, IMAGE_UNSPECIFIED);
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 300);
intent.putExtra("outputY", 300);
intent.putExtra("return-data", true);
startActivityForResult(intent, PHOTORESOULT);
}
public void SaveBitmap(Bitmap bitmap) throws IOException {
FileOutputStream fOut = null;
//注解1
String path = "/mnt/sdcard/Android/data/com.xys/files";
try {
//查看这个路径是否存在,
//如果并没有这个路径,
//创建这个路径
File destDir = new File(path);
if (!destDir.exists())
{
destDir.mkdirs();
}
fOut = new FileOutputStream(path + "/" + FILE_NAME) ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//将Bitmap对象写入本地路径中,Unity在去相同的路径来读取这个文件
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
try {
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0)
{
//当用户点击返回键是 通知Unity开始在"/mnt/sdcard/Android/data/com.xys/files";路径中读取图片资源,并且现在在Unity中
UnityPlayer.UnitySendMessage("Main Camera","message",FILE_NAME);
}
return super.onKeyDown(keyCode, event);
}
}
注解1:主要是路径”/mnt/sdcard/Android/data/com.xys/files”,如下图所示,我们在这里把文件保存在这个路径下。为什么要把图片2进制文件写在这里呢? 还记得以前MOMO给大家说过在Unity中访问Android或IOS本地2进制文件时用到的这个路径,
Application.persistentDataPath 该路径等价于 /mnt/sdcard/Android/data/com.xys/files ,当然后者的包名是对应的工程包名,这样在Unity中可以找到对应裁剪后的图片文件,并且显示在Unity中。
AndroidManifest.xml 这个文件也没什么好说的,大家看看吧。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xys"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="10" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".UnityTestActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".WebViewActivity">
</activity>
</application>
<!-- 连接互联网的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
然后把上面的Android工程打包做成插件放在Unity中。如下图所示,这个我的Unity工程中对应的路径。如果看不懂的朋友请看我之前的文章哈。
然后看Test.cs脚本,它直接挂在摄像机身上。
using UnityEngine;
using System.Collections;
using System.IO;
public class Test : MonoBehaviour
{
public GUISkin skin;
Texture texture;
void Update ()
{
if (Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown(KeyCode.Home))
{
Application.Quit();
}
}
void OnGUI()
{
GUI.skin = skin;
if(GUILayout.Button("打开手机相册"))
{
//调用我们制作的Android插件打开手机相册
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("TakePhoto","takeSave");
}
if(GUILayout.Button("打开手机摄像机"))
{
//调用我们制作的Android插件打开手机摄像机
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("TakePhoto","takePhoto");
}
if(texture != null)
{
//注意! 我们在这里绘制Texture对象,该对象是通过
//我们制作的Android插件得到的,当这个对象不等于空的时候
//直接绘制。
GUI.DrawTexture(new Rect(100,300,300,300),texture);
}
}
void messgae(string str)
{
//在Android插件中通知Unity开始去指定路径中找图片资源
StartCoroutine(LoadTexture(str));
}
IEnumerator LoadTexture(string name)
{
//注解1
string path = "file://" + Application.persistentDataPath +"/" + name;
WWW www = new WWW(path);
while (!www.isDone)
{
}
yield return www;
//为贴图赋值
texture = www.texture;
}
}
注解1:请大家一定要注意这个路径的写法, 前面一定要加 “File://” 不然无法读取。OK说了这么多我们看看这个项目运行的效果,激动人心的时刻来临啦 嚯嚯嚯嚯!!!
1.首次进入的画面, 这里的图片是我刚刚从相册选择的
3. 选择一张图片,我们进行裁剪
最后我们返回到Unity中界面。新的图片Unity已经完成读取,界面上已经修改成刚刚我裁剪的啦,哇咔咔。 怎么样,还不错啦? 哈哈后。这个做用户头像肯定给力 蛤蛤。
- 本文固定链接: https://www.xuanyusong.com/archives/1480
- 转载请注明: 雨松MOMO于 雨松MOMO程序研究院 发表







请教下 我unity截图保存到了手机一个文件夹,但是在图库看不见,必须要手动把sd卡刷新下才可以 怎么办?发布为android的
你好,这个问题解决了吗
而且 我看你androoid工程的时候 有好多错·
雨神·····为啥我发布的时候出现了CommandInvokationFailure: Unable to convert classes into dex format. See the Console for details.
C:Program Files (x86)Javajdk1.8.0_45binjava.exe -Xmx1024M -Dcom.android.sdkmanager.toolsdir=”D:/android-sdktools” -Dfile.encoding=UTF8 -jar “D:/Unity5.0/Editor/Data/BuildTargetTools/AndroidPlayersdktools.jar” –
stderr[这情况,怎么解决?[抓狂]
把lib下的class文件添加依赖
请问总是显示默认问号贴图是怎么解决的呢?
说明图片地址返回的不对,Unity没有找到贴图。
解决了吗 我也有这个问题 在unity里面图片是一个红色问号。
解决了吗 同求
请教一下,我现在unity里面的工程里已经包含了一个jar文件 并且有自己的manifest配置文件了,现在想把你这里的相机调用也整合到一起,请问该怎么弄呢?对安卓不熟悉 希望解答一下~!
貌似是吧jar丢进去,然后配置文件加上这个jar里的活动,不知道对不对
每次打开相册或者相机,按返回键都会跳到安卓的界面上,不能直接跳到unity的界面上吗?
同求啊 你解决了没
我是在sendmessage回调unity的函数之后Finish()一下
Finish()?具体代码呀,对安卓不熟悉
问题解决了吗?求告知!
你把webvieactivity 里面的setcontentview 那行注释掉试试呢
在 5.2.1 版本不适用
我和你一样啊,Exception: JNI: Init’d AndroidJavaClass with null ptr!UnityEngine.AndroidJavaClass..ctor (IntPtr jclass) (at C:/buildslave/unity/build/Runtime/Export/AndroidJavaImpl.cs:539)UnityEngine.AndroidJavaObject.get_JavaLangClass () (at C:/buildslave/unity/build/Runtime/Export/AndroidJavaImpl.cs:517)UnityEngine.AndroidJavaObject.FindClass (System.String name) (at C:/buildslave/unity/build/Runtime/Export/AndroidJavaImpl.cs:508)UnityEngine.AndroidJavaClass._AndroidJavaClass (System.String className) (at C:/buildslave/unity/build/Runtime/Export/AndroidJavaImpl.cs:528)UnityEngine.AndroidJavaClass..ctor (System.String className) (at C:/buildslave/unity/build/artifacts/EditorGenerated/AndroidJava.cs:93)Test.OnGUI () (at Assets/Test.cs:35)报这个错误,烦死人的问题
调通了,非常感谢,请问裁剪框要怎么弄成可拉伸大小的那种
你是按他那样代码复制过去的让后打包到unity里面运行可以吗。。。我的怎么一直有问题
我是复制到eclipse,然后clean一下,把bin目录下的jar文件拷到unity工程的Plugins/Android
大神,我的怎么打成apk之后,一运行就直接退出,你是怎么解决的呢 ?
估计包名不对
我去,搞了半天原来是你的message函数名写错了,莫非你是故意的?
我记得之前我更真过来过啊。。
IOS下怎样调用呀?
大神,求IOS的代码。
而且就算我能够发布apk成功,还是不能够在手机上使用!!!!
你好,我这边用Unity5一导入就会出现——Error importing folder (The pathName assets/plugins is already mapped to 0c7674125e022374aa44cc8ca91914a6. But the meta data wants it to be mapped to 85cb79cb52f1644f1a72b47f15a4aa79)这个错误,请问我应该怎么解决???
太早了, 这篇当时写的时候还是unity3.x 现在都5了 我也不知道还能不能用。。
呃……我想问一下,现在的话我应该怎么用Unity打开手机相册的功能??是否还需要用Android封装一个插件来使用??
因为我现在用的就是Unity5,而对于读取手机相册这个功能我已经查阅了网上许多资料,但是一律都不能用……所以实在是有点捉急啊……
拍照、剪切和保存都没有问题,但最后的显示不成功啊,好像是string path = “file://” Application.persistentDataPath “/” name;没能读到这个图片。请问雨神,你在android测试过吗,是不是playerseting的问题?
拍照、剪切和保存都没有问题,但最后的显示不成功啊,好像是string path = “file://” + Application.persistentDataPath +”/” + name;没能读到这个图片。请问雨神,你在android测试过吗,是不是playerseting的问题?
确实啊,我把playersetting的write access改成SDCARD就读取成功了
请教下,为啥我发布的时候出现了CommandInvokationFailure: Unable to convert classes into dex format. See the Console for details.C:Program Files (x86)Javajdk1.8.0_45binjava.exe -Xmx1024M -Dcom.android.sdkmanager.toolsdir=”D:/android-sdktools” -Dfile.encoding=UTF8 -jar “D:/Unity5.0/Editor/Data/BuildTargetTools/AndroidPlayersdktools.jar” -stderr[这情况,怎么解决?
我也遇到这样的问题 你解决了吗
我想在unity里获取Android系统相册的路径怎么弄啊
你弄好了么,求分享
为什么unity里面收不到message呢?
Test.cs中的48行,message写成messgae了…