谈谈AssetStore及其脱离Unity下载方法

AssetStore一直是Unity开发者又爱又恨的东西。首先这个生态绝对是先进的,并且越来越能够吸引高质量的开发者开发或开源他们的插件。它的缺点可能是大陆开发者尤为头疼的。

1、付费,客观的看99%的资源价格都在100刀以内,先不论个人开发者能否完成同样功能的开发,即使可以,也绝对值回工时费了。

2、网络,没有验证过在国外是否都能流畅下载,至少使用电信、网通的“局域网”都十分痛苦。

3、断点续传,AssetStore的资源下载过程中一旦网络异常或关闭Unity,都只能“从0开始”。


我们都知道AssetStore下载到Windows本地的路径是C:\Users\用户名\AppData\Roaming\Unity\Asset Store-5.x\开发者公司名\插件名\xxx.unitypackage。

下载过程中会有如下2个文件生成,在目录下持续F5,TMP文件体积会增长。



而JSON文件由url和key组成。

{"download" : {"url" : "http://d2ujflorbtfzji.cloudfront.net/download/950fd1fa-e786-4587-8180-13f83f057c52", "key" : "b370540fa8321d35e84cedafc7565512b69f06ae73cf07c589db3899a1b0cd4555c11dc3eae8561271d18d9012106263"}}


url很明显可以拷贝到任何下载器中下载,但下载完的文件即使手动添加后缀.unitypackage,也无法导入unity中使用。



后来查阅参考资料,发现可以通过key来解码,代码如下。 

using System;
using System.Reflection;

namespace Babybus.Framework.ExtensionMethods
{
    public static class AccessExtensions
    {
        public static T InvokeConstructor<T>(this Type type, Type[] paramTypes = null, object[] paramValues = null)
        {
            return (T)type.InvokeConstructor(paramTypes, paramValues);
        }

        public static object InvokeConstructor(this Type type, Type[] paramTypes = null, object[] paramValues = null)
        {
            if (paramTypes == null || paramValues == null)
            {
                paramTypes = new Type[] { };
                paramValues = new object[] { };
            }

            var constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, paramTypes, null);

            return constructor.Invoke(paramValues);
        }

        public static T Invoke<T>(this object o, string methodName, params object[] args)
        {
            var value = o.Invoke(methodName, args);
            if (value != null)
            {
                return (T)value;
            }

            return default(T);
        }

        public static T Invoke<T>(this object o, string methodName, Type[] types, params object[] args)
        {
            var value = o.Invoke(methodName, types, args);
            if (value != null)
            {
                return (T)value;
            }

            return default(T);
        }

        public static object Invoke(this object o, string methodName, params object[] args)
        {
            Type[] types = new Type[args.Length];
            for (int i = 0; i < args.Length; i++)
                types[i] = args[i] == null ? null : args[i].GetType();

            return o.Invoke(methodName, types, args);
        }

        public static object Invoke(this object o, string methodName, Type[] types, params object[] args)
        {
            var type = o is Type ? (Type)o : o.GetType();
            var method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, types, null);
            if (method == null)
                method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

            return method.Invoke(o, args);
        }

        public static T GetFieldValue<T>(this object o, string name)
        {
            var value = o.GetFieldValue(name);
            if (value != null)
            {
                return (T)value;
            }

            return default(T);
        }

        public static object GetFieldValue(this object o, string name)
        {
            var field = o.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            if (field != null)
            {
                return field.GetValue(o);
            }

            return null;
        }

        public static void SetFieldValue(this object o, string name, object value)
        {
            var field = o.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            if (field != null)
            {
                field.SetValue(o, value);
            }
        }

        public static T GetPropertyValue<T>(this object o, string name)
        {
            var value = o.GetPropertyValue(name);
            if (value != null)
            {
                return (T)value;
            }

            return default(T);
        }

        public static object GetPropertyValue(this object o, string name)
        {
            var property = o.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            if (property != null)
            {
                return property.GetValue(o, null);
            }

            return null;
        }

        public static void SetPropertyValue(this object o, string name, object value)
        {
            var property = o.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            if (property != null)
            {
                property.SetValue(o, value, null);
            }
        }
    }
}

Menu工具

using UnityEditor;
using Babybus.Framework.ExtensionMethods;
using UnityEngine;

class DecryptUtility
{
    [MenuItem("Utility/DecryptFile")]
    static void DecryptFile()
    {
        var inputFile = Application.dataPath + "/950fd1fa-e786-4587-8180-13f83f057c52";
        var key = "b370540fa8321d35e84cedafc7565512b69f06ae73cf07c589db3899a1b0cd4555c11dc3eae8561271d18d9012106263";

        var unityEditor = typeof(Editor).Assembly;

        var assetStoreUtils = unityEditor.GetType("UnityEditor.AssetStoreUtils");

        assetStoreUtils.Invoke("DecryptFile", inputFile, inputFile + ".unitypackage", key);
    }
}



这里注意修改inputFile的路径,需要解码的文件名和key值,我的代码对应路径是项目的project文件夹。点击Menu上新增的Utility→DecryptFile,执行上面的代码。进度条读完,一个同名的unitypackage已经生成在目录下了。


把这个unitypackage文件拷到C:\Users\用户名\AppData\Roaming\Unity\Asset Store-5.x\开发者公司名\插件名\ 

路径下,Unity内打开AssetStore,可以看见即使文件没有重命名,系统已经识别该文件存在。



另外笔者实验将不相关的unitypackage重命名靠过来,或低版本的该插件导进来,系统都能识别,结果分别是导入按钮变灰色,及显示更新。key值或许就控制了这些奥妙。



评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值