自定义头像

好久没写博客了,真的好忙啊,没有一点下班时间,这公司好拼!!
游戏中用户的头像不仅能显示系统定义好的头像,而且如果能显示用户自定义的头像肯定能丰富游戏的表现。今天就来讨论下Unity游戏如何实现游戏中显示用户自定义头像的实现。


流程分析

  1. Unity中触发选择自定义头像(相机or相册)
  2. 调用系统原生接口弹出相机或相册供用户获取头像图片
  3. 对用户得到的头像进行裁剪压缩
  4. 上传CDN服务器或者存到本地目录(Unity项目一般存到Application.persistentDataPath目录里,这个路径不同平台的具体路径网上都有详细的介绍)
  5. 用3W从CDN服务器或本地下载头像并渲染

    关于cdn服务器,游戏运营商一般会提供给游戏开发商。其大致流程是:游戏客户端上传成功后会返回一个图片的url,之后cdn服务器会进入审核流程(有自动审核和人工审核)以防止用户上传一些涉黄的头像,审核通过与否会通知(这个说法可能不好,有些cdn服务器并不支持把游戏服务器的一个函数注册到cdn服务器,这样可以主动通知游戏服务器。我们现在的做法是游戏服务器会定时去查询cdn服务器头像是否审核通过,这种轮询的做法我感觉很不好)游戏服务器,再由游戏服告诉客户端,此时客户端再做处理。
    下载的时候可以做一些缓存处理,即每下载到一个新的头像,可以把它存到本地,这样做的好处是避免频繁访问cdn服务器、节省用户流量,从本地下载的速度快。坏处就是会占用设备一定的内存空间。我们现在的项目一张头像大概4kb,一千个头像大概4M,也还行。现在的手机不缺这点内存了。当然如果觉得这样不好,也可以只缓存特定的头像,比如好友的。
    至于上传和下载要不要开线程那要看需求了,如果一次下载量不是很大就算了,我们现在也没开线程。如果一次下载量很大的话最好还是单开线程来处理比较稳。

实践

Android

写一个头像管理类,负责调用系统相机和相册,裁剪压缩,保存图片。

public class MY_HeadImage {
    public static int HEAD_IMAGE_TAKEPHOTO = 1;
    public static int HEAD_IMAGE_PICK = 2;
    public static int HEAD_IMAGE_CROP = 3;

    public MY_HeadImage()
    {
    }

    /**
     * 使用摄像机拍照
     */
    public void takePhoto()
    {
        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
        intent.putExtra("output", Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg")));
        Activity act = (Activity) UnityPlayerActivity.MainContext;
         act.startActivityForResult(intent,HEAD_IMAGE_TAKEPHOTO);
    }

    /**
     * 从相册中选择
     */
    public void pickFromAlbum()
    {
        Log.i("TEST","pickFromAlbum");
        Intent intent = new Intent("android.intent.action.PICK");
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        Activity act = (Activity) UnityPlayerActivity.MainContext;
        act.startActivityForResult(intent, HEAD_IMAGE_PICK);
    }

    /**
     * 裁剪
     * @param uri
     */
    public void startPhotoZoom(Uri uri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 256);
        intent.putExtra("outputY", 256);
        intent.putExtra("return-data", true);
        Activity act = (Activity) UnityPlayerActivity.MainContext;
        act.startActivityForResult(intent, HEAD_IMAGE_CROP);
    }

    public void SaveBitmap(Bitmap bitmap) throws IOException {
        Log.i("TEST", "保存文件");
        FileOutputStream fOut = null;

        Activity act = (Activity) UnityPlayerActivity.MainContext;
        String packageName = act.getPackageName();
        //注解
        String path = "/mnt/sdcard/Android/data/"+ packageName +"/files/";
        try {
            //查看这个路径是否存在,
            //如果并没有这个路径,
            //创建这个路径
            File destDir = new File(path);
            if (!destDir.exists())
            {
                destDir.mkdirs();
            }
            fOut = new FileOutputStream(path + "/image.jpg") ;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //将Bitmap对象写入本地路径中,Unity在去相同的路径来读取这个文件
        bitmap.compress(Bitmap.CompressFormat.JPEG, 5, fOut);
        try {
            fOut.flush();
            Log.i("TEST", "保存路径:" + path + "/image.jpg");
            Log.i("TEST", "success");
            //告诉Unity选择头像成功
            UnityPlayer.UnitySendMessage("Camera", "PickHeadImgSucc","success!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

在java的主类里,注意需要事先(onCreate里)实例化headImage =new MY_HeadImage()

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if(resultCode != 0) {
            File uri;
            if(requestCode == 1) {
                uri = new File(Environment.getExternalStorageDirectory() + "/temp.jpg");
                headImage.startPhotoZoom(Uri.fromFile(uri));
            }

            if(intent != null) {
                if(requestCode == 2) {
            headImage.startPhotoZoom(intent.getData());
                }

                Bitmap e;
                if(requestCode == 3) {
                    Bundle uri2 = intent.getExtras();
                    if(uri2 != null) {
                        e = (Bitmap)uri2.getParcelable("data");
                        try {
                            headImage.SaveBitmap(e);
                        } catch (IOException var8) {
                            var8.printStackTrace();
                        }
                    }
                }
            }
        }
    }

当然还需要在主类里提供一个方法给C#调用,这个方法会弹出系统的一个Alert,供用户选择拍照还是相册里选择头像。

public void TakePhoto()
    {
        Dialog dlg = new AlertDialog.Builder(this).setIcon(R.drawable.app_icon)
                .setTitle("选择图像").setPositiveButton("相机", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        headImage.takePhoto();
                    }
                }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                }).setNeutralButton("相册", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        headImage.pickFromAlbum();
                    }
                }).create();
        dlg.show();
    }

IOS

IOS就比较简单了,写一个.mm文件即可,一样的做一个头像管理类,负责调用系统相机和相册,裁剪压缩,保存头像等工作

@interface MY_HeadImage : UIViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>

+(MY_HeadImage *)sharedInstance;
-(void)MenuSelect;

@end
//暴露接口,供C#调用
extern "C" void MY_OpenHeadImage(){[[MY_HeadImage sharedInstance] MenuSelect];}

@implementation MY_HeadImage
static MY_HeadImage *instance = nil;

UIViewController * selfView;

+(MY_HeadImage *)sharedInstance{
    @synchronized(self) {
        if(instance == nil) {
            instance = [[[self class] alloc] init];
            selfView = UnityGetGLViewController();
        }
    }
    return instance;
}

-(void)MenuSelect{
    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"选择头像" message:@"" preferredStyle:UIAlertControllerStyleActionSheet];
   // UIAlertControllerStyleAlert在中央屏幕。
   // UIAlertControllerStyleActionSheet在屏幕底部。
    UIAlertAction *useCamera = [UIAlertAction actionWithTitle:@"相机" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"拍照");
        [instance pickFromCamera];
    }];
    UIAlertAction *usePhoto = [UIAlertAction actionWithTitle:@"相册" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"相册");
        [instance pickFromAlbum];
    }];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
    [alertController addAction:useCamera];
    [alertController addAction:usePhoto];
    [alertController addAction:cancelAction];
    [selfView presentViewController:alertController animated:YES completion:nil];
}

//从相机选择
-(void)pickFromCamera
{
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate = self;
    imagePicker.allowsEditing = YES;
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
    [selfView presentViewController:imagePicker animated:YES completion:nil];
}

//从相册选择
-(void)pickFromAlbum
{
    UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
    imagePicker.delegate = self;
    imagePicker.allowsEditing = YES;
    imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    [selfView presentViewController:imagePicker animated:YES completion:nil];
}

//选择完成回调(系统自己调用的,别找了)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSLog(@"选择头像完成");
    UIImage *img = [info objectForKey:UIImagePickerControllerEditedImage];
    [self performSelector:@selector(saveImage:)  withObject:img afterDelay:0.5];
    [picker dismissViewControllerAnimated:YES completion:nil];
}

//保存图片
- (void)saveImage:(UIImage *)image {
    BOOL success;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *imageFilePath = [documentsDirectory stringByAppendingPathComponent:@"image.jpg"];
    NSLog(@"头像写入[IOS]->>%@",imageFilePath);
    success = [fileManager fileExistsAtPath:imageFilePath];
    if(success) {
        success = [fileManager removeItemAtPath:imageFilePath error:&error];
    }
    UIImage *smallImage = [self thumbnailWithImageWithoutScale:image size:CGSizeMake(93, 93)];
    [UIImageJPEGRepresentation(smallImage, 1.0f) writeToFile:imageFilePath atomically:YES];//写入文件
    UnitySendMessage("Camera", "PickHeadImgSucc", "image.jpg");
}

//保持原来的长宽比,生成一个缩略图
- (UIImage *)thumbnailWithImageWithoutScale:(UIImage *)image size:(CGSize)asize
{
    UIImage *newimage;
    if (nil == image) {
        newimage = nil;
    }
    else{
        CGSize oldsize = image.size;
        CGRect rect;
        if (asize.width/asize.height > oldsize.width/oldsize.height) {
            rect.size.width = asize.height*oldsize.width/oldsize.height;
            rect.size.height = asize.height;
            rect.origin.x = (asize.width - rect.size.width)/2;
            rect.origin.y = 0;
        }
        else{
            rect.size.width = asize.width;
            rect.size.height = asize.width*oldsize.height/oldsize.width;
            rect.origin.x = 0;
            rect.origin.y = (asize.height - rect.size.height)/2;
        }
        UIGraphicsBeginImageContext(asize);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
        UIRectFill(CGRectMake(0, 0, asize.width, asize.height));//clear background
        [image drawInRect:rect];
        newimage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    return newimage;
}


@end

Unity

写一个类挂摄像机上(Camera),主要逻辑:

#if UNITY_ANDROID && !UNITY_EDITOR
    private AndroidJavaClass jc = null;
    public void OpenSelectorMenu()
    {
        jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
         AndroidJavaObject JO = jc.GetStatic<AndroidJavaObject>("currentActivity");
        JO.Call("TakePhoto");
    }
#elif UNITY_IPHONE && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void MY_OpenHeadImage ();
    public void OpenSelectorMenu ()
    {
        MY_OpenHeadImage();
    }
#else
    public void OpenSelectorMenu ()
    {
    }
#endif
//在android或者IOS原生代码里调用了
    //所以Unity侧引用为0,勿删
    //在移动设备上选择好头像后回调到unity
    public void PickHeadImgSucc ( string str )
    {
        Debug.Log("保存图片成功 image.jpg: " + str);
        string url = "file://" + Application.persistentDataPath + "/image.jpg";
        //在此用3W下载本地头像并渲染
    }

效果图就不上了,项目已用。
另外上传的时候需要把Texture2D转化为byte[],方法是Texture2D.EncodeToJPG()。

如果您想要自定义WordPress头像,可以使用以下方法: 1. 使用插件 有许多可用的WordPress插件,可以让您自定义头像。以下是一些常用的插件: - WP User Avatar:这个插件可以让您上传自己的头像,或者使用默认的Gravatar头像。 - Avatar Manager:这个插件可以让您上传自己的头像,并且可以调整头像的大小和形状。 - Simple Local Avatars:这个插件可以让您上传本地头像,并将其作为默认头像。 2. 自定义代码 如果您想使用自定义代码来实现自定义头像,可以使用以下代码: ``` function custom_avatar( $avatar_defaults ) { $custom_avatar = array( 'my_avatar' => __( 'My Avatar', 'textdomain' ), ); $avatar_defaults = array_merge( $avatar_defaults, $custom_avatar ); return $avatar_defaults; } add_filter( 'avatar_defaults', 'custom_avatar' ); ``` 上面的代码将“my_avatar”添加到默认头像列表中,并将其显示为“My Avatar”。然后,您可以使用以下代码来显示自定义头像: ``` <?php echo get_avatar( 'example@example.com', 64, 'my_avatar', 'Custom Avatar' ); ?> ``` 上面的代码将使用自定义头像“my_avatar”和名称“Custom Avatar”显示头像。您可以将“example@example.com”替换为您的电子邮件地址。如果您想使用本地图像作为头像,请使用以下代码: ``` <?php $user_id = get_current_user_id(); echo get_avatar( $user_id, 64, '', 'Custom Avatar' ); ?> ``` 上面的代码将使用当前用户的ID作为头像,您可以将“64”替换为您想要的图像大小。请注意,如果用户没有上传头像,则将显示默认的Gravatar头像
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值