打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
[Unity3D学习]Unity代码热更新解决方案测试结果总结

这几天一直在研究热更新方案

主要思路是:

1.先将代码打包成dll,然后用unity 打包成assetsbundle,

2.WWW加载进入主程序,

3使用System.Reflection.Assembly来创建程序集,

4.然后通过GetType(className),来获取这个类

5.AddComponent进入主程序,加载的dll就执行起来了。

ExportAssetBundles.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//打包工具,该工具是网上找来都。谢谢作者!
public class ExportAssetBundles : MonoBehaviour {
    //在Unity编辑器中添加菜单  
    [MenuItem("Custom Editor/Create AssetBunldes ALL")]
    static void ExportResource()
    {
        // 打开保存面板,获得用户选择的路径  
        string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "assetbundle");
        if (path.Length != 0)
        {
            // 选择的要保存的对象  
            Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
            //打包  
            BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.StandaloneWindows);
      
        }
    }  
}

 

Index.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using UnityEngine;
using System.Collections;
using System.Reflection;
//代码加载器
public class Index : MonoBehaviour
{
    private WWW www;
    public static WWW uiWWW;
    private System.Reflection.Assembly assembly;
// Use this for initialization
void Start ()
    {
        StartCoroutine(loadScript());
}
    private IEnumerator loadScript()
    {
        //加载我的代码资源
        www = new WWW("http://localhost/Main.assetbundle");
        yield return www;
        AssetBundle bundle = www.assetBundle;
        TextAsset asset = bundle.Load("Main", typeof(TextAsset)) as TextAsset;
        assembly = System.Reflection.Assembly.Load(asset.bytes);
        Assembly[] assLis = System.AppDomain.CurrentDomain.GetAssemblies();
        System.Type script = assembly.GetType("Main");
        gameObject.AddComponent(script);
    }
}

 

 

因为在加载的时候遇见安全沙箱问题,所以我将这个策略文件记录下来,方便下次复制粘贴

crossdomain.xml
XHTML
1
2
3
4
5
6
<?xml version="1.0"?>
<cross-domain-policy>
<site-control permitted-cross-domain-policies=”master-only” />
<allow-access-from domain="blog.gamerisker.com" />
<allow-access-from domain="*"/>
</cross-domain-policy>

本地调试程序时解决跨域问题的方法:

Edit->Project Settings->Eidtor

 

刚开始的时候想使用序列化来存储一些数据,但是后来却连一个很简单的类序列化dll里面都没法获得

官方对象序列号
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[MenuItem("Custom Editor/WriteSpriteData")]
    static void FileWriteSpriteData()
    {
        TextAsset textasset = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.txt", typeof(TextAsset)) as TextAsset;
        Atlas atlas = ScriptableObject.CreateInstance<Atlas>();
//Json其实是NGUIJson这个类,我只是把他提出来。改了个名字
        atlas.mList = Json.LoadSpriteData(textasset as TextAsset);
        if (atlas.mList == null)
            return;
        string path = "Assets/Resources/Packer/Packer.asset";
        AssetDatabase.CreateAsset(atlas, path);
//Atlas是一个只有一个mList属性都类 mList = new List<UISpriteData>();
        Object o = AssetDatabase.LoadAssetAtPath(path, typeof(Atlas));
        Object texture = AssetDatabase.LoadAssetAtPath("Assets/Resources/Packer/Packer.mat", typeof(Material));
        Object[] t = {texture};
        BuildPipeline.BuildAssetBundle(o, t, "Assets/Resources/Packer/Packer.assetbundle");
        
        //AssetDatabase.DeleteAsset(path);
    }

 

这是使用序列化数据的加载方式,在不用反射的情况下,下面代码加载能够成功,但是使用了反射,下面的代码就加载不成功了。这个问题我也很费解,暂时我没办法解决

读取序列化对象
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
IEnumerator LoadAtlas()
    {
        www = new WWW("http://localhost/Packer.assetbundle");
        //WoodenAtlas.assetbundle
        //Packer.assetbundle
        yield return www;
//用来断点都时候看看里面所包含都数据
        Object[] os = www.assetBundle.LoadAll();
        Material mete = www.assetBundle.Load("Packer", typeof(Material)) as Material;
        Atlas atlas = www.assetBundle.mainAsset as Atlas;
        GameObject go = new GameObject("UIAtlas");
        UIAtlas uiatlas = go.AddComponent<UIAtlas>();
        uiatlas.spriteMaterial = mete;
        uiatlas.spriteList = atlas.mList;
        GameObject sprite = new GameObject("Sprite");
        UISprite ui = NGUITools.AddChild<UISprite>(sprite);
        ui.atlas = uiatlas;
        ui.spriteName = "dynamite";
        Debug.Log("Load");
        www.assetBundle.Unload(false);
        www.Dispose();
        
    }

 

因为要看一下代码的执行效率,所以我寻找到了这个类。感觉还可以。使用josn数据,mat文件创建一个UIAtlas的时间大概是30毫秒左右。

C#
1
2
3
4
5
System.Diagnostics.Stopwatch;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Thread.Sleep(10000);
stopWatch.Stop();

总结:

我使用没有任何Unity环境以外代码来实现壳的制作(我们暂且将其称为Index,其实他就是上面的Index类,代码少得可怜。)

然后主程序是在另一个Unity项目中(这个项目在发布的时候打包成Main.dll)

Main.dll项目通过上面的Index来加载,然后添加到一个GameObject上,主程序的Awake()方法就会执行(Awake是整个程序的主入口)

这个时候所有的资源加载都会在Main.dll里面完成。

在这个过程中,遇到了一个比较麻烦的问题就是,我打包的一些UIAtlas.prefab文件上的UIAtlas这个类,无法找到。

这让我有一些无法理解,因为NGUI的代码已经打包进入了Main.dll,那么为什么我加载prefab的时候,却找不到UIAtlas这个类呢?

最后我只能动态的制作UIAtlas对象来完成这样工作。

那么这样的话,以后做界面,做任何prefab都不能绑定脚本了。都只能加载到内存中动态AddComponent了。这样界面也得用配置文件了。

不过对于我这种从页游转过来的程序,这到不是问题,我有现成的界面编辑器(我博客里有RookieEditor),直接生成XML在游戏中进行组装了。对于能够热更新来说,这点麻烦,其实应该不算麻烦了。

动态生成UIAtlas后,创建了几个Sprite、Button,基本的功能都已经实现,说明这个解决方案是可行的。接下来我将把这个方案运用到我的项目中,更加全面的去实验一下。

这样设计的优点:

1、对IOS的打包也是比较方便。打包IOS 直接拿Main项目打包就可以了。因为不需要热更新了。把代码打包在本地就行了。

2、打包Android项目的时候发布apk只需要发布Index,项目发布版本和没写代码一样大,想到这里我想吐槽一下,Unity就算不写任何代码,发布一个apk也得有7M左右。这也太大了点吧。我可啥都没做啊。

求助:

1、哪位大神能给我说说上面我遇到的那个问题,为什么找不到绑定在prefab上的类呢?这是程序集的问题么?哎,刚转C#的人伤不起。

 

下一篇: 《[Unity3D学习]Unity代码热更新 源码下载》

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Unity手游之路<十三>手游代码更新策略探讨
unity3d动态加载图片
Unity新功能Sprite Atlas与Sprite Mask详解
Unity热更新方案探索与讨论
关于Unity手游更新的思考
Android平台的代码热更新
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服