目前已经把场景搭建完毕,登录注册界面都已做好,整个场景大概分三个模块:UI根节点的UI Root_LogOnScene以及加上背景不变的部分,注册界面panReg,登录界面panLogOn
当加载这个场景时,首先应该加载UIRoot以及大背景不变的部分,再在这个基础之上,加载登录或者注册界面。
第一步将这三部分均设置为预制体(Prefab),UI Root_LogOnScene存放到
Resources/UIPrefab/UIScene目录下。注册界面panReg,登录界面panLogOn存放到Resources/UIPrefab/UIWindows目录下方便管理。
第二步将这三个组件在场景中删除,实现在切换场景中动态加载出来,所以需要做以下几个步骤
1.创建脚本SceneMgr,主要控制场景之间的切换
2.创建脚本LogOnSceneCtrl,功能是控制登录的场景中UI的加载,然后在场景中创建一个空物体重命名为LogOnSceneCtrl,将LogOnSceneCtrl脚本挂载到物体上。
3.点开脚本LogOnSceneCtrl进行编辑
public class LogOnSceneCtrl : MonoBehaviour {
void Awake()
{
GameObject obj = Resources.Load("UIPrefab/UIScene/UI Root_LogOnScene") as GameObject;
Instantiate(obj);
}
}
点运行,就将UIRoot及背景加载出来了,但是这样有一个问题,一个游戏有很多的界面,如果每次加载一个界面都需要调用一次Resources且要输入很长的路径,然后再克隆出来,会显得很麻烦
4.所以再创建一个类ResourcesMgr,管理整个游戏中的资源加载,不需要挂载到任何物体上,且需要每个场景都能够调用以加载资源,所以需要设置成一个单例
public class ResourcesMgr
{
private static ResourcesMgr instance;
public static ResourcesMgr Instance
{
get
{
if (instance == null)
{
instance = new ResourcesMgr();
}
return instance;
}
}
}
5.因为随着游戏的开发,像这种单例势必也会有很多,所以这里再创建一个专门的单例基类Singleton
/// <summary>
/// 单例
/// </summary>
/// <typeparam name="T">子类继承时候会传给单例一个类型,且要求实例化</typeparam>
public class Singleton<T> where T:new()
{
private static T instance;
public static T Instance
{
get
{
if(instance==null)
{
instance = new T();
}
return instance;
}
}
}
将ResourcesMgr继承单例类Singleton,这样ResourcesMgr自动有一个单例的属性
public class ResourcesMgr:Singleton<ResourcesMgr>
{
}
再在ResourcesMgr上面写方法,因为每次加载资源都需要写很长的路劲,为了应对这个问题,将路径简化,所以对资源类型进行分类,添加了一个menu,并对Load函数进行一定的调整
using System.Collections;
using System.Text;
public class ResourcesMgr:Singleton<ResourcesMgr>
{
#region ResourceType 资源类型
/// <summary>
/// 资源类型
/// </summary>
public enum ResourceType
{
/// <summary>
/// 场景UI
/// </summary>
UIScene,
/// <summary>
/// UI窗口
/// </summary>
UIWindow,
/// <summary>
/// 角色
/// </summary>
Role,
/// <summary>
/// 特效
/// </summary>
Effect
}
#endregion
#region Load 加载资源
/// <summary>
/// 加载资源
/// </summary>
/// <param name="type">资源类型</param>
/// <param name="path">短路径(文件名)</param>
/// <returns>预设克隆体</returns>
public GameObject Load(ResourceType type,string path)
{
StringBuilder sbr = new StringBuilder();
switch(type)
{
case ResourceType.UIScene:
sbr.Append("UIPrefab/UIScene/");
break;
case ResourceType.UIWindow:
sbr.Append("UIPrefab/UIWindows/");
break;
case ResourceType.Role:
sbr.Append("RolePrefab/");
break;
case ResourceType.Effect:
sbr.Append("EffectPrefab");
break;
}
sbr.Append(path);
return Resources.Load(sbr.ToString) as GameObject;
}
#endregion
}
这样LogOnSceneCtrl就可以的方法就变成
public class LogOnSceneCtrl : MonoBehaviour
{
void Awake()
{
GameObject obj = ResourcesMgr.Instance.Load(ResourcesMgr.ResourceType.UIScene,"UI Root_LogOnScene");
Instantiate(obj);
}
}
6.现在已经实现了加载的一定简化,但是如果每次打开窗口都要进行创建或者销毁,会造成很大的资源的浪费,所以需要将已生成的资源放入缓存中,当要打开窗口是直接调用即可。在ResourcesMgr类中添加一个预设的哈希表,并实例化,每次创建资源的时候,判断是否哈希表是否有这个资源,有就直接加载,而不用再生成,代码如下:
public class ResourcesMgr:Singleton<ResourcesMgr>
{
#region ResourceType 资源类型
/// <summary>
/// 资源类型
/// </summary>
public enum ResourceType
{
/// <summary>
/// 场景UI
/// </summary>
UIScene,
/// <summary>
/// UI窗口
/// </summary>
UIWindow,
/// <summary>
/// 角色
/// </summary>
Role,
/// <summary>
/// 特效
/// </summary>
Effect
}
#endregion
/// <summary>
/// 预设的列表
/// </summary>
private Hashtable m_PrefabTable;
/// <summary>
/// 构造函数 实例化哈希表
/// </summary>
public ResourcesMgr()
{
m_PrefabTable = new Hashtable();
}
#region Load 加载资源
/// <summary>
/// 加载资源
/// </summary>
/// <param name="type">资源类型</param>
/// <param name="path">短路径(文件名)</param>
/// <param name="cache">是否放入缓存</param>
/// <returns>预设克隆体</returns>
public GameObject Load(ResourceType type,string path,bool cache=false)
{
StringBuilder sbr = new StringBuilder();
switch(type)
{
case ResourceType.UIScene:
sbr.Append("UIPrefab/UIScene/");
break;
case ResourceType.UIWindow:
sbr.Append("UIPrefab/UIWindows/");
break;
case ResourceType.Role:
sbr.Append("RolePrefab/");
break;
case ResourceType.Effect:
sbr.Append("EffectPrefab");
break;
}
sbr.Append(path);
//当一个窗口被频繁的销毁或创建,势必为造成资源的浪费,所以需要将已经生成的窗口放入缓存中
GameObject obj = null;
if(m_PrefabTable.Contains(path))
{
Debug.Log("资源是从缓存中加载的");
obj = m_PrefabTable[path] as GameObject;
}
else
{
obj= Resources.Load(sbr.ToString()) as GameObject;
if(cache)
{
m_PrefabTable.Add(path, obj);
}
}
return GameObject.Instantiate(obj);
}
#endregion
}
LogOnSceneCtrl调用方法为
public class LogOnSceneCtrl : MonoBehaviour
{
void Awake()
{
GameObject obj = ResourcesMgr.Instance.Load(ResourcesMgr.ResourceType.UIScene,"UI Root_LogOnScene",cache:true);
}
}
到这里就实现了,最开始的动态加载,后面会进一步简化。