读一读

在Unity每次启动的时候都会进行一次导入操作,例如将纹理文件压缩、切割成Sprite等的操作,资源导入的结果是一个或多个UnityEngine.Objects。资源导入的过程是一个十分耗时的操作,所以,Unity会将导入的结果缓存在Library的metadata文件夹中,以资源的GUID的头两个字母分类的文件夹和完整GUID命名的缓存文件。这也是为什么第一次打开不包含Library的项目时需要很大耗时,再次打开就很快的原因。


照我的理解,资源文件Asset就是Unity3d退出后,在计算机文件系统中的项目Assets文件夹可以看到的东西,例如:纹理图片(png,jpg等)、内置材质文件、内置的prefab文件等。使用GUID标志。

Object应该就是Unity启动的时候,将资源导入(Unity一顿操作)而在Unity内存中生成的对象再序列化成可以使用的资源文件,Asset可以对应多个这样的Object,例如纹理的sprite切割,所以每个Object都是由Asset的GUID和它自身的本地ID而确定唯一的。


我在Unity工程中导入一张纹理并设置为Sprite,并切割它成

blob.png

则查看它的meta文件,可以看到:

blob.png

新建场景,并新建一个Image,Sprite赋值为:Tile-Example_0,保存(序列化)Image为一个Prefab,保存场景

在文件夹中用文本文件打开这个Prefab,可以看到:

blob.png


通过设置序列化资源格式可以查看他们的依赖关系,本地ID和GUID。

通过Edit/Project Settings/Editor打开设置窗口,在Inspector中设置

blob.png

blob.png


GUID标识的是Asset资源文件,通过这个值可以映射到它的具体路径,在Unity中可以随意移动从而不影响依赖关系。

本地ID标识的是由GUID标识的资源产生的子资源,例如Sprite切割成的子Sprite。

因为GUID标志的Asset文件可以产生多个Object的序列化文件,所以引进本地ID,利用GUID和本地ID两个确定唯一的资源。


想要在Inspector中显示脚本组件的私有变量但是又不想让人修改,可以在Inspector面板中,点击右上角的下三角,勾选Debug模式,这样就可以在Inspector中显示私有变量了,但是不允许修改。


一、单个对象池单元

using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class GameObjectPool {

    [SerializeField]
    public string name;
    [SerializeField]
    private int maxNum;
    [SerializeField]
    private GameObject prefab;

    [NonSerialized]
    private List<GameObject> UnUseList = new List<GameObject>();

    public GameObject Out() {
        
        if (UnUseList.Count > 0) {
            GameObject go = UnUseList[0];
            UnUseList.RemoveAt(0);
            go.SetActive(true);
            return go;
        }

        GameObject newGo = GameObject.Instantiate(prefab);
        return newGo;
    }

    public void In(GameObject obj) {
        
        if (UnUseList.Count >= maxNum) {
            GameObject.Destroy(obj);
            return;
        }

        obj.SetActive(false);
        UnUseList.Add(obj);
    }
}

二、对象池列表

using System.Collections.Generic;
using UnityEngine;

//可编辑的列表,可视化新加对象池
public class GameObjectPoolList : ScriptableObject {
    public List<GameObjectPool> pools = new List<GameObjectPool>();
}

三、对象池管理器

using System.Collections.Generic;
using UnityEngine;

public class PoolManager {

    private static PoolManager _instance;

    public static PoolManager Instance {
        get {
            if (_instance == null)
                _instance = new PoolManager();

            return _instance;
        }
    }

    private Dictionary<string, GameObjectPool> pools;
    public PoolManager() {
        //加载序列化添加的对象池,添加到字典中
        GameObjectPoolList gameObjectPoolList = Resources.Load<GameObjectPoolList>("gameobjectpool");

        pools = new Dictionary<string, GameObjectPool>();
        foreach (GameObjectPool pool in gameObjectPoolList.pools) {
            pools.Add(pool.name, pool);
        }
    }

    public object Out(string Name) {
        GameObjectPool pool;
        pools.TryGetValue(Name, out pool);

        if (pool != null) {
            return pool.Out();
        }

        return null;
    }

    public void In(string Name,GameObject obj) {

        GameObjectPool pool;
        pools.TryGetValue(Name, out pool);

        if (pool != null)
        {
            pool.In(obj);
            return;
        }

        Debug.LogWarning("PoolName:" + Name + "不存在!");
    }
}

生成序列化类的变量的文件(点这里)。对象池的名字可以用配置的形式规定,例如用枚举来做名字等,方便修改等。


使用多个单例模式的Manager来管理不同的模块。

例如LevelManager,用来管理关卡的加载逻辑,提炼方法为上一关和下一关等,关数的顺序使用配置表来确定,此时删除关卡或则改名字等或则修改关卡的连续都可以直接通过修改配置完成。

PoolManager,管理多种SpawnPool类型对象池,SpawnPool管理多个这个类型的PrefabPool,PrefabPool单一的对它的Prefab进行管理。

通过这些单例模式的管理器,任何地方都可以通过这个实例来实现交互操作。


使用Ctrl+Shift开启碰撞模式,拖动黄色小方框,到光标所指的对象上,两碰撞体会相对齐。例如,用来叠放东西,放置东西到一个平面上等。


选择物体后,使用移动工具,按住V会使变换工具切换为顶点吸附模式,这个时候可以移动鼠标到合适的点,点击选择拖动到合适的地方吸附。可以用Ctrl+Shift+V一直开启顶点吸附模式,按Ctrl+V关闭。