项目开发笔记 (七)
很多APP中都会有个人信息,或者个人帐号 都会有设置头像功能,国内的很多APP更加是如此(很多人的帐号密码泄密,正是由于到处注册帐号,举个我当年密码泄漏的例子,在很多年的某一天 我在某个网吧的嘟嘟牛系统注册了一个帐号,当时毫无安全意识,直接用个人密码作为嘟嘟牛的密码,很多年后的今天 我在嘟嘟牛的用户数据库里找到了我当年的个人所有信息…嘟嘟牛的数据库怎么会流出来?A.黑客把嘟嘟牛黑掉,把数据库拿到地下交易市场卖,B.嘟嘟牛的某个数据库程序员离职 顺带把数据库带出来,C.公司倒闭,倒卖数据榨干最后一点价值等等..),言归正传,目前我在参与一个类APP项目的优化工作.使用Unity开发.也有这种..设置用户头像的需求
Unity Version: 5.x or 4.x
本文内容非纯干货,如果不想看可以直接下载Sample – 传送门
难点主要在几个地方:
- Unity与iOS/Android原生代码之间的调用
- iOS/Android 对摄像机的调用与图片截取
- iOS/Android 对图片的IO操作
- 上传图片
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"); } } //-------------------------------------------------------------------------
参考博文:
- http://blog.csdn.net/poem_qianmo/article/details/40723789
- http://onevcat.com/2013/07/shader-tutorial-1/
- http://docs.unity3d.com/Manual/SL-SurfaceShaders.html
- http://http.developer.nvidia.com/CgTutorial/cg_tutorial_chapter05.html
- http://blog.csdn.net/candycat1992/article/category/1782159
- http://www.xuanyusong.com/archives/1493
-EOF-
这全一个比较好的轮子了
额 本来不想造的,但是发现大家的轮子都比较不全
亲测不能用
哦?能抓个打印么?检查下权限设置?
你好,我试了代码,编译到安卓平台上可以正常使用,但是在编辑图片时,如果选择“舍弃”,那么会崩溃。
解决了吗?怎么解决?
在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();
}
}
安卓的可以用,但是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的时候我们又不能禁用自动引用计数
【上述文本原先是截图了,因此用了图片转文字评论在这里,有些符号可能显示不正确】
在链接获取图片源码的地方设置一下 Build Phases – Complie Sources – xxxx – Compiler Flags 设置为 -fobjc-arc 试试。
好的,我试试!!
你好,对于Android版本,我的项目修改包名之后打开相册就会闪退请问是什么原因。
打开eclipse 或者 AndroidStudio 看看闪退的时候输出什么Log?
Unity 打包成Eclipse工程之后再调试吗?
不用调试,手机运行程序的时候 ,连接电脑,打开USB调试,打开Eclipse的logcat就可以看到输出
你好 例子中读取出的图片分辨率被调低了吗?请问怎么更改才能读取原图
请详细看代码,有压缩选项可以修改的.
额。。。我是学C#的,与Ios交互的代码我看不懂,昨天琢磨了一天,我想去掉选择图片之后对图片的编辑,直接导入原图,分辨率也不变,但是没成功,希望大神能指点一下
1.如果要修改图片的大小 ,请修改 UserAvatarActivity.java 中的函数 __PictureScaleHandle.里面有裁剪参数
2.如果要原图 在有些设备上可能会很大(几M),建议慎重考虑.
好的,谢谢了
在上传图片的时候。要把图片的format格式换成RGB24什么的。直接用byte[] bytes = tex.EncodeToPNG();不管用。怎么破额。大神回复一下吧
EncodeToJPG 呢?
使用你的例子是正常的,但是unity上换了Bundle Identifier就闪退,请问怎么换成我自己的Bundle Identifier地址也正常啊?
闪退的时候报什么错?看看 androidstudio中的 log
我将AndroidManifest.xml里面使用Bundle Identifier——就是“”com.rect.avatar“”这个东西——的地方都改了,还是不行,是安卓那里需要改什么东西吗?