状态加载模块

前言

        而达到项目优化的效果,在3维场景中漫游的时候,同大多的的2维场景游戏一样,需要在不同的区域需要加载出不同的资源对象。然而2维场景游戏和伪3D的类型的游戏由于视角有限及资源相对较小,加载资源基本都是计算视角区域,进行计算并并加载相关的资源。在3维场景中大多数还是整体加载为主以细节资源加载为辅助,一是能在进入场景后就可眼观全局,二是在进入一个小区域或房间后可以看到具体的资源。为些本文将介绍一个用于快速加载一种状态下的资源的模块,这个模块将不仅仅用于场景漫游,而将用于所有使用状态模式的3维程序。

一、功能介绍

如图1.1所示,利用ScrptObject制作了一个可视的窗口,如按扭所示,提供了prefab和bundle两种资源加载的方式。在配制过程中,可以直接将prefab资源拖动到其中,在不同的scriptObject之间也可以相互拖动。一但将资源放置其中,相关的信息会自动进行填充,除非需要重置其坐标或旋转到指定的位置。由于状态有包含交叉等情况,所以这里也会也状态一和状态二都需要共用状态的情况,如果你传入到状态一那么状态一和共用状态的资源都会加载出来。


(图1.1)

二、基本状态

由于状态的相互引用,为防止状态一引用共用状态,同时共用状态又引用状态一的问题,所以做了一定的处理。一当填充一个状态下所需要的资源时,如果已经存在那么就不会重复加载了,脚本源码如下:

  private StateItem[] LoadBundleListGroupsItems(string stateName, List<string> loadedKeys = null)
        {
            if (loadedKeys == null)
            {
                loadedKeys = new List<string>() { stateName };
            }
            else if (!loadedKeys.Contains(stateName))
            {
                loadedKeys.Add(stateName);
            }
            else
            {
                return null;
            }

            List<StateItem> items = new List<StateItem>();
            var find = bundleList.FindAll(x => x.stateName == stateName);
            if (find != null)
            {
                var groups = find .ToArray();

                foreach (var bitem in groups)
                {
                    foreach (var sitem in bitem.itemList)
                    {
                        if (!string.IsNullOrEmpty(sitem.assetName) && !string.IsNullOrEmpty(sitem.assetBundleName))
                        {
                            items.Add(sitem);
                        }
                    }
                    ///subState
                    foreach (var item in bitem.subStateNames)
                    {
                        var subItems = LoadBundleListGroupsItems(item, loadedKeys);
                        if (subItems != null)
                        {
                            items.AddRange(subItems);
                        }
                    }
                }
            }
            return items.ToArray();
        }
    }
一但记录了指定的资源,就不会重复记录了。当需要一个状态下加载同一个预制体到不同的位置,也不会有问题,因为ID中包含了坐标的信息的HashCode.但一次状态加载如果加载了两个完全相同的资源就会报错,当然这也是为了防止资源重复加载。

三、缓存功能

在状态进行切换的时候,如果加载的比较慢,而且用户需要来回切换,那么最后能有暂时不销毁指定对象的方式,则会自动进行隐藏操作。控制的脚本如下:

 /// <summary>
        /// 计算当前需要下载的资源
        /// </summary>
        /// <param name="state"></param>
        private void ResetLoadingState()
        {
            //停止正在下载的资源
            itemLoadCtrl.CansaleLoadAllLoadingObjs();
           
            needDownLand.Clear();
            var loadedKeys = new string[loadedDic.Count];
            loadedDic.Keys.CopyTo(loadedKeys, 0);

            ///删除新状态下不再需要的对象
            foreach (var item in loadedKeys)
            {
                var info = CurrentItems.Find(x => x.ID == item);
                if (info == null)
                {
                    if (loadedDic[item] != null)
                    {
                        if (catchStates.Contains(lastState))
                        {
                            loadedDic[item].gameObject.SetActive(false);
                            if (log) Debug.Log("隐藏1:" + item);
                        }
                        else
                        {
                            delyDestroyObjects.Add(loadedDic[item]);
                            loadedDic.Remove(item);
                            if (log) Debug.Log("销毁1:" + item);
                        }
                    }
                }
                else
                {
                    loadedDic[item].gameObject.SetActive(true);
                    if (log) Debug.Log("保留:" + item);
                }
            }

            ///记录需要加载的资源
            for (int i = 0; i < CurrentItems.Count; i++)
            {
                var info = CurrentItems[i];
                if (!loadedDic.ContainsKey(info.ID))
                {
                    needDownLand.Enqueue(info);
                }
            }
        }

    }
实现这种功能后的效果如图3.1所示


四、资源类型

由于一些工程资源比较少,加载的时候也都是本机,所以没有必要使得AssetBundle,但有些工程需要从AssetBundle资源加载,并实现动态更新,所以这个模块也将支持两种加载的方式,在使用的时候,如果没有AssetBundleTools这个库,就只能使用Prefab加载了,为防止报错可将宏定义AssetBundleTools删除。

其中数据模型上,资源包加载和预制体加载都使用 以下这个类为模板:

    public abstract class StateItem
    {
        protected string _id;
        public abstract string ID { get; }
        public bool reset;
        public Vector3 position;
        public Vector3 rotation;
    }
可以自行定义坐标和放旋转等信息,如果需要其他信息,可自行添加。不过要在对应的绘制脚本中修改相应的绘制方式。

五、引擎Bug

在开发以上这个模块的时候,遇到了非常难过的一天。最终发现了一个非同寻常的Bug,目前在unity5.3和unity5.6上都会出现,在编辑器完美运行而在打包出来后,不论是pc端还是webgl端都不能正常运行。主要是序列化的问题。从发现问题到找到问题后,提炼bug如下

一个可序列化的类:

public class Main : MonoBehaviour {
    public Child item;
}
一个父级带宏定义的类:

using UnityEngine;

public class Parent
{
#if UNITY_EDITOR
    public int a;
#endif
}
[System.Serializable]
public class Child:Parent
{
    public string c;
}


在空场景中将Main脚本挂在一个对象上,将a输入任意的一个负数,然后打包运行,你将会发现和

sharedassets0.assets' is corrupted

The file 'resources.assets' is corrupted

类似的问题,反正就是资源加载失败了

六、源码说明

由于开发过程积累的小的非核心的模块都放置在github上面了,兴趣的同行可以参看源码:

https://github.com/zouhunter/StateLoader


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值