18

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

 3 years ago
source link: https://www.xuanyusong.com/archives/1480
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
首页 > Unity3D频道 > 【Unity3D研究院之游戏开发】 > Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)

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);  
             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";
  //查看这个路径是否存在,
  //如果并没有这个路径,
  //创建这个路径
  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);
fOut.flush();
} catch (IOException e) {
e.printStackTrace();
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工程中对应的路径。如果看不懂的朋友请看我之前的文章哈。

然后看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. 009ab2053ed0c0803c3177c8fb22483e?s=80123 说:

    layout布局没放出来来,不会安卓的有点惨啊

  2. 18352064a699b140bee97ccb4ed6d3ca?s=80bluphy 说:

    MOMO大大,网盘挂了,能麻烦重新上传一下吗?谢谢啦

  3. 56f378d638906d4ed490a1523d38e8b8?s=80下城C骆 说:

    网盘挂了,layout布局文件能看一下吗?

  4. 297dca9e4aa51bf054136bfdcf09ad1e?s=80aiaiaiaiaia 说:

    有谁能告诉我这两句你这里写有什么用呀 ,我创建一个空的class不行么
    setContentView(R.layout.main);
    imageView = (ImageView) this.findViewById(R.id.imageID);

  5. 214be951669e302f5a34162908730a3b?s=80徐dada 说:

    雨松大佬,我看你这个帖子然后下载你的源码弄了好几天,能问您一共问题么,就是打包后安装到我的手机里,(包名是跟你安卓一样,我手机是小米的)为什么一直闪退啊。。

  6. ?s=80猫猫 说:

    楼主,如果想把裁剪框做成可以拖动成任意宽高比的,不要正方形的,在这个例子的基础上应该怎么改呢?

  7. ?s=80邹超 说:

    直接使用路径”/mnt/sdcard/Android/data/com.xys/files”,小米手机会找不到,应该改为 Environment.getExternalStorageDirectory().toString() “/Android/data/com.xys/files”

  8. ?s=80邹超 说:

    直接使用路径”/mnt/sdcard/Android/data/com.xys/files”,小米手机会找不到,应该改为 Environment.getExternalStorageDirectory().toString()+”/Android/data/com.xys/files”

  9. ?s=80杰拉德 说:

    问题:遇到这样一个报错:“Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=3, result=-1, data=Intent { (has extras)}} to activity {com.xys/com.xys.WebViewActivity}: java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.widget.ImageView.setImageBitmap(android.graphics.Bitmap)’ on a null object reference”问题原因:没能通过findtypebyid 获取到imageView对象,Unity 打包的时候打乱的 id 的值导致无法找到。解决方案:参见:http://johncookie.iteye.com/blog/1997636

  10. ?s=80忆动 说:

    大大,有没有什么办法检测游戏是否已经授权摄像机权限?我用这个好像不行啊Application.HasUserAuthorization(UserAuthorization.WebCam)

  11. ?s=80肖锦招 说:

    截好的图为什么没办法保存啊,在次打开软件就不见了

  12. ?s=80唐岚 说:

    最近项目需要使用Android的一些功能,但是我发现Unity和Android相互调用的时候,会出现channel die的情况,动不动就崩溃掉,测试机热得吓人。雨神,求指点!!

  13. ?s=80洛辰 说:

    最近项目刚好需要,看了momo大神的讲解,获益良多,可是再调用taskphoto的函数时就报错了。有人碰到过吗?UnityEngine.AndroidJavaExcepion:java.lang.NoSuchMethodError:no non-staticmethod “Lcom/unity3d/player/UnityPlayerActivity;.TaskPhoto(Ljava/lang/String;)V”

  14. ?s=80--EventBus 说:

    麻烦问一下, 如果我这个android 工程用了其他jar 包, 比如v4包, 我在unity 工程结构上, 怎么更新目录结构。 需要注意什么。 可能我我放置不对, 一进入引用第三方jar 包的activity就崩 。 求回复

    • 93f5f8354da50ae18db5fdb6c91614c6?s=80哇哈哈 说:

      同求,使用provider属性,调用v4的包(为了解决Android7.0无法调用相机的问题),apk安装后,运行就闪退啊。这是为什么呢?

  15. ?s=80COCO 说:

    在savebitmap最后加个this.finish();

  16. 雨神,问下我只想读取指定文件夹的图片,怎么弄?

  17. ?s=80COCO 说:

    我想把截过的图当素材用,但是缩放之后,图片变得十分模糊,这是什么原因啊?求解决

  18. ?s=80_话不在多 说:

    我在照完相之后截图貌似并没有截出来,在选择相册截图直接Unity崩溃了 怎么回事,

  19. ?s=80蒲林峰 说:

    请教下 我unity截图保存到了手机一个文件夹,但是在图库看不见,必须要手动把sd卡刷新下才可以 怎么办?发布为android的

  20. ?s=80蒲林峰 说:

    而且 我看你androoid工程的时候 有好多错·

  21. ?s=80蒲林峰 说:

    雨神·····为啥我发布的时候出现了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[这情况,怎么解决?[抓狂]

  22. ?s=80黄帅 说:

    请问总是显示默认问号贴图是怎么解决的呢?

?s=80近月 说:

我和你一样啊,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)报这个错误,烦死人的问题

?s=80希愿炫 说:

调通了,非常感谢,请问裁剪框要怎么弄成可拉伸大小的那种

?s=80希愿炫 说:

我去,搞了半天原来是你的message函数名写错了,莫非你是故意的?

留下一个回复 取消回复

你的email不会被公开。

评论

姓名 *

电子邮件 *

站点


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK