首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)
2012
07-13

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)

         最近做项目需要用到这个功能,就是在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中。

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 1

 

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工程中对应的路径。如果看不懂的朋友请看我之前的文章哈。

 

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 2

 

然后看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.首次进入的画面, 这里的图片是我刚刚从相册选择的

 

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 3

 


2.打开相册我们选择一张图片

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 4

 

3. 选择一张图片,我们进行裁剪

 

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 5

 

最后我们返回到Unity中界面。新的图片Unity已经完成读取,界面上已经修改成刚刚我裁剪的啦,哇咔咔。 怎么样,还不错啦? 哈哈后。这个做用户头像肯定给力 蛤蛤。

 

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 6

如果点击打开摄像机按钮,拍照完毕后会提示裁剪,裁减完毕返回到Unity界面中同样能看到效果。
最后MOMO将本篇博文的源码放出来,  源码包括Android的工程 与Unity的工程,雨松MOMO祝大家学习愉快,大家互相学习互相进步,加油哇咔咔,啦啦啦。
最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)》有 137 条评论

  1. Mercurial说:

    收了海飞丝多少钱?

  2. hello说:

    松神,例子怎么下不了

  3. 赵川说:

    雨大,问个事啊,我把你代码拷贝下来 不管是拍照还是打开相册剪切图片点保存的时候程序会闪退 是为什么

  4. Murphy说:

    最近公司项目也需要这个内容,安卓代码有限,雨神能指点下他问的问题吗? Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 1 拜托了

  5. 张艳磊说:

    为什么一运行你的包到爆出来的apk jiu bengkui le

  6. xipeng_me说:

    请问解决了吗?

  7. xipeng_me说:

    大神,请教一下如何把截图发送到web服务器? Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三) - 雨松MOMO程序研究院 - 1

  8. 屌屌的...说:

    MOMO ,代码链接那里,那两个文件夹不打包无法下载呀!! 文章很不错,学习了。

  9. 说:

    我用你的程序竖屏是没问题的,换成横屏就出现问题了。不知道你试过没有?

  10. 小龙说:

    直接用真机测试就不会有这个问题了。

  11. 镜花水月说:

    雨神,如果要调用的 Android 方法要传递多个参数,像这样 jo.Call(“Buy”, “Test0001”, “钻石”, “金币”, “test01”, “test==”, “isnull==”, “100010”, “蜃梦楼oy”, 10, 20000, 10); 但是总是提示 AndroidJavaException: java.lang.NoSuchMethodError: ,Android 中的方法是有的

  12. 镜花水月说:

    雨神,想请教一个问题。如果我想调用的Activity 不是主activity 是不是把 AndroidJavaObject jo = jc.GetStatic(“currentActivity”);中的currentActivity换成我的activity?我这样做了但我的Activity中的方法没有被调用,已经在 AndroidManifest.xml 中加入了 ,是不是要先启动我的Activity?请雨神指点一下

    • AndroidJavaObject jo = jc.GetStatic(“currentActivity”); 或者你还可以用 AndroidJavaClass class = new AndroidJavaClass(“com.koramgame.xxsr.myclass”); 去取一个类。。

    • AndroidJavaObject jo = jc.GetStatic(“currentActivity”); 或者你还可以用 AndroidJavaClass class = new AndroidJavaClass(“com.momo.momo.myclass”); 去取一个类。。

      • 镜花水月说:

        多谢雨神, 我是这样调用的 using (AndroidJavaClass aj = new AndroidJavaClass(“com.kingnet.qmonster.AndquitActivity”)) { AndroidJavaObject jo = aj.GetStatic(“currentActivity”); xValue = jo.Call(“getNum”); }但是 好像没有获得jo对象,在 AndroidJavaObject jo = aj.GetStatic(“”)中应该传什么参数呢?

        • AndroidJavaClass myClass = new AndroidJavaClass(“com.kingnet.qmonster.AndquitActivity”);myClass.CallStatic(“startFunc”); startFunc 要是静态方法。。

          • 镜花水月说:

            多谢雨神,但是如果这样,我只能调用Android的静态方法了,如果我想调用 Android 中 public void onBackPressed() { }这样不是静态的复写方法该怎么解决呢?

          • 镜花水月说:

            雨神,怎样调用非静态方法呢?或者怎样才能获得类的实例呢? 我在 Activity 的onCreate 方法中 instanse = this ; 给这个类的静态实例,但发现 onCreate 并没有执行。求雨神指点啊

        • 雨松MOMO说:

          AndroidJavaClass myClass= new AndroidJavaClass(“com.kingnet.qmonster.AndquitActivity”);myClass.CallStatic(“startFunc”); startFunc 要是静态方法。。

    • 雨松MOMO说:

      AndroidJavaObject jo = jc.GetStatic(“currentActivity”); 或者你还可以用 AndroidJavaClass class= new AndroidJavaClass(“com.koramgame.xxsr.myclass”); 去取一个类。。

    • 雨松MOMO说:

      AndroidJavaObject jo = jc.GetStatic(“currentActivity”); 或者你还可以用 AndroidJavaClass class= new AndroidJavaClass(“com.momo.momo.myclass”); 去取一个类。。

  13. air说:

    我也遇到这个问题了,跟上面的一样,用的机型是note 3,安卓版本4.3

  14. Will_D说:

    我用你的程序竖屏是没问题的,换成横屏就出现问题了。不知道你试过没有?

  15. xu转角踩到屎说:

    你好,我是Android菜菜,这个照片剪切完后,会跳到一个显示界面,按返回键后才返回unity界面。请问我想裁剪保存后就直接跳到unity界面该怎么改?

  16. 你好,我是一个初学者,不知道为什么这个源码一直在我的eclipse和unity里都不能运行,而且在unity里调试的时候,还会显示说Exception: JNI: Init’d AndroidJavaClass with null ptr!这个,研究了好几天实在是不知道为什么,想请教一下原因。。。。。。谢谢

  17. 你好说:

    請問如果我在ASSET STORE下載的PLUGIN已經有ANDROIDMANIFEST, 那麼應該把自己PLUGIN再加上去?

  18. 刘拉拉说:

    能在Unity3d 调用 ios的相册吗

  19. 雨松MOMO博客网友说:

    怪摘 . . 不知為何標點符號全不見了 . .

  20. 雨松MOMO博客网友说:

    MOMO 你好好不容易在台灣這兒買到你的書獲益良多我想問問MOMO 是否有研究過 unity 該如何才能直接使用apple i系列產品的相機鏡頭呢 ??

留下一个回复

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