Unity中获取手机图片

 项目开发笔记 (七)

很多APP中都会有个人信息,或者个人帐号 都会有设置头像功能,国内的很多APP更加是如此(很多人的帐号密码泄密,正是由于到处注册帐号,举个我当年密码泄漏的例子,在很多年的某一天 我在某个网吧的嘟嘟牛系统注册了一个帐号,当时毫无安全意识,直接用个人密码作为嘟嘟牛的密码,很多年后的今天 我在嘟嘟牛的用户数据库里找到了我当年的个人所有信息…嘟嘟牛的数据库怎么会流出来?A.黑客把嘟嘟牛黑掉,把数据库拿到地下交易市场卖,B.嘟嘟牛的某个数据库程序员离职 顺带把数据库带出来,C.公司倒闭,倒卖数据榨干最后一点价值等等..),言归正传,目前我在参与一个类APP项目的优化工作.使用Unity开发.也有这种..设置用户头像的需求

Unity Version: 5.x or 4.x

本文内容非纯干货,如果不想看可以直接下载Sample – 传送门

难点主要在几个地方:

  1. Unity与iOS/Android原生代码之间的调用
  2. iOS/Android 对摄像机的调用与图片截取
  3. iOS/Android 对图片的IO操作
  4. 上传图片

Unity与iOS/Android原生代码之间的调用

Unity对原生的调用支持相对还是很好的.也非常简单.

  • 对于Android

主要的方法是通过获取主Activity对象,然后调用主Activity中的pubilc信息,举个栗子

AndroidJavaClass androidActivityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
m_NativeObj = androidActivityClass.GetStatic("currentActivity");
m_NativeObj.Call(
"SettingAvaterFormMobile",  // Java函数名
 strObjectName,             // 传入的参数
strFuncName,                // 传入的参数
strFileName);               // 传入的参数

上面即调用主Activity上的SettingAvaterFormMobile函数.

Android原生代码要给Unity调用 还需要配置一些基本内容:

1.在Android项目的生成文件夹bin下命令行生成Jar包

在确认Android项目代码无误的情况下,到项目目录bin\classes下,打开命令行定位到这里,然后使用命令生成jar文件

jar -cvf xxx.jar * // xxx为文件名 可随意设置

2.Android项目需要取得原生jar包放到Unity项目文件夹Assets\Plugins\Android\bin目录(没有就自行创建) ,再取 AndroidManifest.xml,res文件夹 ,放到 Assets\Plugins\Android文件夹下.如图:

3.对于Android项目源码包的获取,Unity4.x 和 5.x方法不一样,如果是Unity5 需要把项目目录bin\classes下编译的R.java生成文件删掉.

  • 对于iOS

由于iOS开发可以OC与C/C++之间无缝调用,所以其实调用iOS原生函数 是通过调用C/C++函数中转的.方法非常简单.在C#脚本端声明一个C++函数,即可直接调用:

//-------------------------------------------------------------------------
// 声明
[DllImport("__Internal")]
private static extern 
void SettingAvaterFormiOS(string strObjectName, string strFuncName, string strFileName);
//-------------------------------------------------------------------------
// 调用
#elif UNITY_IPHONE
 {
    SettingAvaterFormiOS(strObjectName, strFuncName, strFileName);
 }
#else
//-------------------------------------------------------------------------

若需要调用一个Android项目的NDK开发的C++函数,原理也是一样的.

iOS原生代码要给Unity调用 还需要配置一些基本内容:

把自行编写的iOS代码复制到Unity项目文件夹文件夹Assets\Plugins\IOS\下(没有就自行创建),如图

iOS/Android 对摄像机的调用与图片截取

  • 对于Andorid

Android打开照相机和打开相册选取照片非常简单,网上代码一吨:

// 打开摄像机获取图片
if(v.getId() == m_OpenCameraID)
{
    eResult = CommonUtil.ENUM_RESULT.eResult_Camera;
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
    intent.putExtra(
        MediaStore.EXTRA_OUTPUT, 
        Uri.fromFile(new File(Environment.getExternalStorageDirectory(), m_ImageFileName)));
    m_contextAct.startActivityForResult(intent, eResult.ordinal());
}
// 打开相册选取图片
else if(v.getId() == m_GetPictureID)
{
    eResult = CommonUtil.ENUM_RESULT.eResult_Picture;
    Intent intent = new Intent(Intent.ACTION_PICK, null);  
    intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED); 
    m_contextAct.startActivityForResult(intent, eResult.ordinal());
}
  • 对于iOS

iOS打开照相机和打开相册选取照片非常简单,网上代码也一吨:

//-------------------------------------------------------------------------
// 关键实现
- (void)showPicker:(UIImagePickerControllerSourceType)type
{
    UIImagePickerController *picker = [[[UIImagePickerController alloc] init] autorelease];
    picker.delegate = self;
    picker.sourceType = type;  // 打开摄像机或者打开相册
    picker.allowsEditing = YES;// 图片是否可编辑
    
    if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
    {
        Class popoverClass = NSClassFromString( @"UIPopoverController" );
        if( !popoverClass )
            return;
        
        _popoverViewController = [[popoverClass alloc] initWithContentViewController:picker];
        [_popoverViewController setDelegate:self];

        [_popoverViewController presentPopoverFromRect:CGRectMake( 0, 0, 128, 128 )
                                                inView:UnityGetGLViewController().view
                              permittedArrowDirections:UIPopoverArrowDirectionAny
                                              animated:YES];
    }
    else
    {
        UIViewController *vc = UnityGetGLViewController();
        [vc presentModalViewController:picker animated:YES];
    }
}
//------------------------------------------------------------------------- 

iOS/Android 对图片的IO操作

获取的图片文件,可大可小,我们不能直接传string回Unity,所以可以事先保存到本地目录下.最佳的目录是Application.persistentDataPath.

  • 对于Android

这个目录就是:"/mnt/sdcard/Android/data/" + packageID + "/files".(packageID为APP包名)保存图片文件代码片段:

FileOutputStream fOut = null;
try
{
    String strPackgeName = getApplicationInfo().packageName;
    String path = "/mnt/sdcard/Android/data/" + strPackgeName + "/files";
    File destDir = new File(path);
    if (!destDir.exists()) 
    {
        destDir.mkdirs();
    }
    
    fOut = new FileOutputStream(path + "/" + m_ImageFileName) ;
} 
catch (FileNotFoundException e)
{
    e.printStackTrace();
}

photo.compress(Bitmap.CompressFormat.PNG, 100, fOut);

try 
{
    fOut.flush();
} 
catch (IOException e) 
{
    e.printStackTrace();
}

try 
{
    fOut.close();
} 
catch (IOException e) 
{
    e.printStackTrace();
}
  • 对于iOS

这个目录就是:Application/packageID/Documents.(packageID为APP包名)
代码片段:

NSString * DocumentsPath = 
[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];

NSFileManager *fileManager = 
[NSFileManager defaultManager];

[fileManager createDirectoryAtPath:DocumentsPath
 withIntermediateDirectories:YES 
attributes:nil 
error:nil];

[fileManager createFileAtPath:[DocumentsPath stringByAppendingString:m_pstrFileName] 
contents:imgData attributes:nil];

如此获取的图片就保存到了指定的目录中.这个时候只要告诉回调到Unity通知一下 即可.

上传图片

设置了用户头像,一般都会有上传头像到服务器的功能,关于C#上传图片到服务器网上也也有一吨的版本,但是Unity官方文档中的是如下:

//-------------------------------------------------------------------------
IEnumerator __UploadPNG()
{
    var bytes = m_texture2D.EncodeToPNG();
    var form = new WWWForm();
    form.AddField("frameCount", Time.frameCount.ToString());
    form.AddBinaryData("avatarPicture", bytes, "avatar.png", "image/png");
    // m_ImageUpDownURL URL上传接口
    var w = new WWW(m_ImageUpDownURL, form);
    yield return w;
    if (!string.IsNullOrEmpty(w.error))
    {
        Debug.Log(w.error);
    }
    else
    {
        Debug.Log("Finished Uploading Image");
    }
}
//-------------------------------------------------------------------------

参考博文:

  1. http://blog.csdn.net/poem_qianmo/article/details/40723789
  2. http://onevcat.com/2013/07/shader-tutorial-1/
  3. http://docs.unity3d.com/Manual/SL-SurfaceShaders.html
  4. http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter05.html
  5. http://blog.csdn.net/candycat1992/article/category/1782159
  6. http://www.xuanyusong.com/archives/1493

-EOF-

25 评论

  1. 你好,我试了代码,编译到安卓平台上可以正常使用,但是在编辑图片时,如果选择“舍弃”,那么会崩溃。

      1. 在UserAvatarActivity.java 文件197行附近,将“// 保存头像图片 ”注释下的内容改成如下,重写生成class,把生成的UserAvatarActivity.class直接替换,就可以了
        // 保存头像图片
        if (requestCode == CommonUtil.ENUM_RESULT.eResult_Success.ordinal())
        {

        if (null != data)
        {
        Bundle extras = data.getExtras();
        Bitmap photo = extras.getParcelable(“data”);
        try
        {
        __SavePicture(photo);
        } catch (IOException e)
        {
        e.printStackTrace();
        }

        UnityPlayer.UnitySendMessage(
        m_UnityObjectName,m_UnityFuncName,CommonUtil.ENUM_RESULT.eResult_Success.toString());
        finish();
        }
        }

  2. 安卓的可以用,但是ios上,编译的时候会出现下面的错误!
    [exec] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDXs/iPhoneOS9.2.sdlc/usr/include/objc/NSObject.h:37:1: note: ‘release* has been explicitly unavailable here
    [exec] – (oneway void)release 03JC_ARC_UNAVAILA3LE;
    [exec] ~
    [exec] /Users/feifan/Des)ctop/wor}c/zzhx/trun}c/client/GaineClient_build_ios/Libraries/Plugins/iOS/UserAvatarController.irjn:54:9: error: ARC forbids explicit message send of ‘release* [execj [sheet release];
    [exec] 一一M A
    [execj /Users/feifan/DesJctop/worJc/zzhx/trunJc/client/GaineClieiit—builtios/Libraries/Plugins/iOS/UserAveitarController.mnjS^yiiej error: ARC forbids explicit message send of * autorelease * [exec] UllmagePicJccrController *pic)cer » [[[UllmagePickerController alloc] inicj autorelease],
    [exec] /Users/feifan/Deskcop/work/zzhx/crunJc/client/GameClient_build_ios/Librarie»/Plugins/iOS/U»erAvacarConcroller.ran:87:76: error: ’autorelease, is unavailable: not available in Jtomacic reference counting mode
    [execj In file included from /Users/feifan/DesJctop/work/zzhx/crunk/client/GameClien^build^ios/Libraries/Plugins/iOS/U»erAvatarController.mn:l:
    [exec] In file included from /U»ers/fei£an/De»Jccop/worlc/zzhx/trun)c/client:/GaiiieClient_build_ios/Cla33e3/Prefix.pch:6:
    [exec] In file included from

    在编译ios的时候我们又不能禁用自动引用计数

    【上述文本原先是截图了,因此用了图片转文字评论在这里,有些符号可能显示不正确】

    1. 在链接获取图片源码的地方设置一下 Build Phases – Complie Sources – xxxx – Compiler Flags 设置为 -fobjc-arc 试试。

  3. 你好,对于Android版本,我的项目修改包名之后打开相册就会闪退请问是什么原因。

        1. 不用调试,手机运行程序的时候 ,连接电脑,打开USB调试,打开Eclipse的logcat就可以看到输出

  4. 你好 例子中读取出的图片分辨率被调低了吗?请问怎么更改才能读取原图

      1. 额。。。我是学C#的,与Ios交互的代码我看不懂,昨天琢磨了一天,我想去掉选择图片之后对图片的编辑,直接导入原图,分辨率也不变,但是没成功,希望大神能指点一下

        1. 1.如果要修改图片的大小 ,请修改 UserAvatarActivity.java 中的函数 __PictureScaleHandle.里面有裁剪参数
          2.如果要原图 在有些设备上可能会很大(几M),建议慎重考虑.

  5. 在上传图片的时候。要把图片的format格式换成RGB24什么的。直接用byte[] bytes = tex.EncodeToPNG();不管用。怎么破额。大神回复一下吧

  6. 使用你的例子是正常的,但是unity上换了Bundle Identifier就闪退,请问怎么换成我自己的Bundle Identifier地址也正常啊?

      1. 我将AndroidManifest.xml里面使用Bundle Identifier——就是“”com.rect.avatar“”这个东西——的地方都改了,还是不行,是安卓那里需要改什么东西吗?

发表评论

电子邮件地址不会被公开。 必填项已用*标注