Unity社交功能开发——图片上传

一、图片上传功能的意义

随着梦幻西游、大话西游、问道等回合制游戏的兴起,手游制作团队越来越重视社交功能的开发。笔者前一篇文章介绍了如何在Unity中加入语音聊天功能,本篇文章将介绍下一个社交功能——图片上传。
有了图片上传玩家就可以自定义头像、聊天发送图片甚至还可以在Unity中做一个『朋友圈』。

二、获取图片到Unity

Unity没有提供直接的Api打开移动端的相机、相册功能,所以需要调用Android/IOS的系统Api来打开移动端的相机和相册

1. Android获取相机、相册图片

主要功能实现参考雨松momo的文章Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)

第一步,建立Unity项目对应的Android工程

如果不会可以查看雨松momo的文章Unity3D研究院之与Android相互传递消息(十九)

第二步,书写打开相机、相册代码by Android

    /**
     * 打开相机 为什么要指定一个保存路径,是因为如果不指定onActivityResult只能从data参数中获取图片,可是获取到的是略缩图
     */
    public void TakePhotoChoose() {
        int hasCameraPermission = checkCallingOrSelfPermission(Manifest.permission.CAMERA);
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        File imageFile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/"
                + format.format(new Date()) + ".jpg");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
        PhotoPath = imageFile.getPath();
        startActivityForResult(intent, PIC_TAKE_PHOTO);
    }

    /**
     * 打开相册
     */
    public void TakePickChoose() {
        Intent intent = new Intent(Intent.ACTION_PICK, null);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_TYPE);
        startActivityForResult(intent, PIC_TAKE_PICK);
    }

第三步,获取到图片处理图片发送到Unity

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == 0) {
            // 取消接口
            UnityPlayer.UnitySendMessage("GameMain", "CandleShowPhoto", "");
            return;
        }
        if (requestCode == PIC_TAKE_PHOTO) {
            // 获取到图片的资源使用bitmap压缩,并且转成base64的String直接将字符串发回去
            Log.e("Unity", "GetPhotoPath");
            UnityPlayer.UnitySendMessage("GameMain", "GetPath", PhotoPath);
            Bitmap bm = PhotoUtil.getImageFromPath(PhotoPath, 40, 500, 750);
            Bitmap bmSmall = PhotoUtil.getImageFromPath(PhotoPath, 40, 60, 80);
            UnityPlayer.UnitySendMessage("GameMain", "ShowPhoto", PhotoUtil.getBitmapStrBase64(bm));
            UnityPlayer.UnitySendMessage("GameMain", "SaveMiniPhoto", PhotoUtil.getBitmapStrBase64(bmSmall));
        }
        if (requestCode == PIC_TAKE_PICK) {
            // 读取相册缩放图片
            Bitmap bm = null;
            ContentResolver resolver = getContentResolver();
            Uri originalUri = data.getData();
            Cursor cursor = getContentResolver().query(originalUri, null, null, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
                bm = PhotoUtil.getImageFromPath(path, 40, 500, 750);
                Bitmap bmSmall = PhotoUtil.getImageFromPath(path, 40, 60, 80);
                UnityPlayer.UnitySendMessage("GameMain", "ShowPhoto", PhotoUtil.getBitmapStrBase64(bm));
                UnityPlayer.UnitySendMessage("GameMain", "SaveMiniPhoto", PhotoUtil.getBitmapStrBase64(bmSmall));
            } else {
                try {
                    bm = MediaStore.Images.Media.getBitmap(resolver, originalUri);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (bm != null) {
                    Thread thread = new YaSuoPicThread(bm);
                    thread.start();
                }
            }
        }
    }

在对图片处理过程中与鱼松momo有些不同,我没有将图片再次保存,而是将bitmap转成了string直接发送回了unity,省去了保存图片的时间,并且效果上看运行速度还是可以接受的。大家可以自己试试。

第四步,Unity显示图片

由于显示图片功能放在了Unity,Android和IOS是公用的所以下文IOS中不再重复

    /// <summary>
    /// 从Android/IOS获取图片的base64串并显示图片
    /// </summary>
    /// <param name="base64">图片的base64串</param>
    void ShowPhoto(string base64)
    {
        int targetHeight = 400;
        int targetWidth = 600;
        byte[] inputBytes = System.Convert.FromBase64String(base64);
        base64 = "";
        Texture2D text = BttetoPicByByte(inputBytes);
        int curHeight = text.height;
        int curWidth = text.width;
        int newHeight = 0;
        int newWidth = 0;
        GetCutSize(targetHeight, targetWidth, curHeight, curWidth, ref newHeight, ref newWidth);

        if (BigImage != null)
        {
            BigImage.gameObject.SetActive(true);
            BigImage.rectTransform.sizeDelta = new Vector2(newWidth, newHeight);
            BigImage.texture = text;
            m_Stream = new MemoryStream(inputBytes);
            inputBytes = null;
        }
        CloseDialogJuHua();
    }

    /// <summary>
    /// 工具:将string转成图片
    /// </summary>
    /// <param name="base64"></param>
    /// <returns></returns>
    Texture2D BttetoPic(string base64)
    {
        Texture2D pic = new Texture2D(600, 400);
        //将base64转码为byte[]  
        byte[] data = System.Convert.FromBase64String(base64);
        //加载byte[]图片 
        pic.LoadImage(data);
        return pic;
    }

    /// <summary>
    /// 工具:将bytes转成图片
    /// </summary>
    /// <param name="bytes"></param>
    /// <returns></returns>
    Texture2D BttetoPicByByte(byte[] bytes)
    {
        Texture2D pic = new Texture2D(600, 400);
        //加载byte[]图片 
        pic.LoadImage(bytes);
        return pic;
    }

    /// <summary>
    /// 工具:返回给定高宽内的图片高度和宽度 比如给定400*600 肯定返回一个400*600之内的照片
    /// </summary>
    /// <param name="targetHeight"></param>
    /// <param name="targetWidth"></param>
    /// <param name="curHeight"></param>
    /// <param name="curWidth"></param>
    /// <param name="newHeight"></param>
    /// <param name="newWidth"></param>
    public static void GetCutSize(int targetHeight, int targetWidth, int curHeight, int curWidth, ref int newHeight, ref int newWidth)
    {
        int TargetHeight = targetHeight;
        int TargetWidth = targetWidth;
        int width = curWidth;
        int height = curHeight;
        float bili = 1f;
        if (width > TargetWidth)
        {
            bili = (float)TargetWidth / (float)width;
        }
        newHeight = (int)((float)height * bili);
        if (newHeight > TargetHeight)
        {
            bili = (float)TargetHeight / (float)height;
        }

        newHeight = (int)((float)height * bili);
        newWidth = (int)((float)width * bili);
    }

2. IOS获取相机、相册图片

如果不会IOS与Unity交互的同学请戳这里

第一步,书写打开相机、相册代码by IOS

需要用到Assetslibrary.framework

//
//  GetPhotoControl.m
//  image2
//
//  Created by tengjiang on 16/4/26.
//  Copyright © 2016年  All rights reserved.
//
#import <Foundation/Foundation.h>

#import "GetPhotoManager.h"
#include <sys/param.h>
#include <sys/mount.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <AVFoundation/AVCaptureDevice.h>
#import <AVFoundation/AVMediaFormat.h>

extern "C"
{
    void _GetPhotoControl(int index);
}

static GetPhotoManager *getPhotoControl;

void _GetPhotoControl(int index)
{
    if(getPhotoControl == NULL)
    {
        getPhotoControl = [[GetPhotoManager alloc] init];
    }
    [getPhotoControl GetPhotoChoose:index];
}

@implementation GetPhotoManager
typedef enum {
    kCLAuthorizationStatusNotDetermined = 0, // 用户尚未做出选择这个应用程序的问候
    kCLAuthorizationStatusRestricted,        // 此应用程序没有被授权访问的照片数据。可能是家长控制权限
    kCLAuthorizationStatusDenied,            // 用户已经明确否认了这一照片数据的应用程序访问
    kCLAuthorizationStatusAuthorized         // 用户已经授权应用访问照片数据} CLAuthorizationStatus;
};

- (void)GetPhotoChoose:(NSInteger)ChooseIndex{
    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    // 判断是否支持相机
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        switch (ChooseIndex) {
            case 0:
                //来源:相机
                if([self GetCameraPermission] == -1)
                {
                    UnitySendMessage("GameMain", "CandleShowPhoto", "");
                    return;
                }
                sourceType = UIImagePickerControllerSourceTypeCamera;
                break;
            case 1:
                if([self GEtPickPermission] == -1)
                {
                    UnitySendMessage("GameMain", "CandleShowPhoto", "");
                    return;
                }
                //来源:相册
                sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
                break;
            case 2:
                UnitySendMessage("GameMain", "CandleShowPhoto", "");
                return;
        }
    }
    else
    {
        if (ChooseIndex == 2) {
            UnitySendMessage("GameMain", "CandleShowPhoto", "");
            return;
        }
        else
        {
            if([self GEtPickPermission] == -1)
            {
                UnitySendMessage("GameMain", "CandleShowPhoto", "");
                return;
            }
            sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        }
    }
    // 跳转到相机或相册页面
    UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
    imagePickerController.delegate = self;
    //imagePickerController.allowsEditing = NO;
    imagePickerController.sourceType = sourceType;
    //仔细看这句话Unity中如果获取presentViewController并跳转,需要这样写
    [UnityGetGLViewController() presentViewController:imagePickerController animated:YES completion:^{

    }];
    //↑↑↑↑↑↑↑↑↑↑↑↑↑仔细看↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
//获取图片后的回调
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    [picker dismissViewControllerAnimated:YES completion:^{

    }];
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    NSData *fData = [self imageCompressForWidth:image targetWidth:600 targetHeight:400];
    NSString *stringPhoto = [fData base64EncodedStringWithOptions:0];
    UnitySendMessage("GameMain", "ShowPhoto", [stringPhoto UTF8String]);

    NSData *fDataSmall = [self imageCompressForWidth:image targetWidth:80 targetHeight:60];
    NSString *stringPhotoSmall = [fDataSmall base64EncodedStringWithOptions:0];
    UnitySendMessage("GameMain", "ShowMiniPhoto", [stringPhotoSmall UTF8String]);
}
//取消的回调
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    NSLog(@"您取消了选择图片");
    [picker dismissViewControllerAnimated:YES completion:^{

    }];
    UnitySendMessage("GameMain", "CandleShowPhoto", "");
}
//工具:图片裁剪
- (NSData *) imageCompressForWidth:(UIImage *)sourceImage targetWidth:(CGFloat)defineWidth targetHeight:(CGFloat)defineHeight
{
    CGSize imageSize = sourceImage.size;
    CGFloat width = imageSize.width;
    CGFloat height = imageSize.height;
    CGFloat targetWidth = defineWidth;
    CGFloat targetHeight = (targetWidth / width) * height;
    if(targetHeight > defineHeight)
    {
        targetHeight = defineHeight;
        targetWidth = (targetHeight / height) * width;
    }
    UIGraphicsBeginImageContext(CGSizeMake(targetWidth, targetHeight));
    [sourceImage drawInRect:CGRectMake(0,0,targetWidth,  targetHeight)];
    UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    NSData *fData = UIImageJPEGRepresentation(newImage, 1);
    return fData;
}
//判断是否开启访问相机权限
-(int) GetCameraPermission
{
    NSString *mediaType = AVMediaTypeVideo;
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];

    if(authStatus == AVAuthorizationStatusRestricted|| authStatus == AVAuthorizationStatusDenied){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"请在设备的设置-隐私-相机中允许访问相机。"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil];
        [alert show];
        return -1;
    }
    else if(authStatus == AVAuthorizationStatusAuthorized){//允许访问
        return 0;

    }
    return 0;
}
//判断是否开启访问相册权限
-(int) GEtPickPermission
{
    ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus];
    if (author == kCLAuthorizationStatusRestricted || author == kCLAuthorizationStatusDenied){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
                                                        message:@"请在设备的设置-隐私-照片中允许访问照片。"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil];
        [alert show];
        return -1;
    }
    return 0;
}

@end

三、上传图片

已经拿到了图片文件的Steam可以直接上传到服务器,生成CDN链接,将链接分发的其他用户。
如果觉得麻烦或者觉得没有必要自己实现,可以通过云储存功能,将图片直接存到云服务。笔者用的是UCloud。当然市场上还有很多厂家提供云储存功能:七牛、阿里云、腾讯云等。。
使用云储存的好处:
1.奉行“专业的人做专业的事”原则
2.节省了开发时间,减少服务器压力
3.云储存都会提供图像再次处理功能,可以再次压缩图片,使用户下来的图片更小,节省用户流量提高用户体验。

四、下载图片并显示

成功上传到云服务器会自动生成一个链接,可以通过链接直接访问图片
Unity加载外部资源主要依靠www协议。可以直接铜鼓www协议加载图片到游戏中,但是用户不可能永远只点开一个图片一次。笔者先将图片下载到本地,然后通过www协议将本地的图片加载到Unity,如果用户再次点开,可以节省用户的流量。

  public void LoadPicByUrlBySmallImage(string _url)
    {
        string url = "";
#if UNITY_STANDALONE_WIN
        url = "file:///" + _url;
#else
        url = "file://" + _url;
#endif
        StartCoroutine(LoadTextureBySmallImage(url));
    }

    /// <summary>
    /// load大图携程
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    IEnumerator LoadTexture(string name)
    {
        string path = name;
        WWW www = new WWW(path);
        yield return www;
        if (www.isDone)
        {
            Texture2D txt = www.texture;

            int width = txt.width;
            int height = txt.height;
            int targetHeight = 400;
            int targetWidth = 600;     
            int newHeight = 0;
            int newWidth = 0;
            GetCutSize(targetHeight, targetWidth, height, width, ref newHeight, ref newWidth);
            if (BigImage != null)
            {
                BigImage.gameObject.SetActive(true);
                BigImage.rectTransform.sizeDelta = new Vector2(newWidth, newHeight);
                BigImage.texture = txt;
            }
            CloseDialogJuHua();
        }
    }

最后笔者将本篇博文的源码放出来, 源码包括Android的工程、Plugins/IOS目录下的IOS代码和Unity的工程

下载地址:https://github.com/Prince-Ling/Unity_PushPhoto

猜你喜欢

转载自blog.csdn.net/tj134679258/article/details/51345562