《Unity游戏优化》笔记(17)[21/03/18_周四][237_]

目录

8.4.15 预制池

1.可持化的组件

2.预制池系统

3.预置池

4.生成对象

5.预先生成实例

6.对象的回收

7.预制池测试

8.预制池和场景加载

9.预制池总结

8.4.16 IL2CPP优化

IL2CPP optimizations

8.4.17 WebGL优化

8.5 Unity、Mono和IL2CPP的未来

即将到来的C# Job System

8.6 小结

第9章 提示与技巧


前3天看了一下淘宝上购买的视频教程,书本的内容已经都包括了,一般来说还是书本比较详细,但是时间不足的情况下,看视频效率更高。

8.4.15 预制池

前面的对象池方案对于传统C#对象非常有用,但不适用于GameObject和MonoBehaviour等专门的Unity对象。这些对象往往会占用大量运动时内存,当创建和销毁它们时,会消耗大量CPU,在运行时还可能导致大量垃圾回收。

我们的目标是将绝大多数对象的实例化推到场景初始化时进行,而不是让他们在运行时创建。这可以节省大量运行时CPU,并避免由于对象创建和销毁以及垃圾回收带来的的大量CPU峰值,但代价是场景加载时间和运行时内存消耗的增加。AssertStore有一些处理该任务的对象池解决方案。

回收的对象依然存在于内存中。

为了避免运行时为GameObject分配内存,最重要的是提前知道需要多少对象,以及有足够的内存空间一次容纳他们。

根据所讨论的对象类型有所不同,需要偶尔测试和健全性检查,以确保运行时每个预制体实例化的数量是合适的。

1.可持化的组件

public interface IPoolableComponent 
{
    void Spawned();
    void Despawned();
}

对象被回收时,重置附加的刚体

public class ResetPooledRigidbodyComponent : MonoBehaviour,IPoolableComponent
{
  [SerializeField] Rigidbody _body;
  
  public void Spawned()
  {
    
  }
  public void Despawned()
  {
    if(_body==null){
        _body=GetComponent<Rigidbody>();
        if(_body==null){
            return;
        }
    }
    _body.velocity=Vector3.zero;
    _body.angularVelocity=Vector3.zero;
  }
}

与前面的消息系统交互

public class PooableTestMessageListener : MonoBehaviour,IPoolableComponent
{
  public void Spawned()
  {
    MessagingSystem.Instance.AttachListener(typeof(MyCustomMesage),this.HandleMyCustomMessage);
  }
 
  bool HandleMyCustomMessage(BaseMessage msg){
      MyCustomMessage castMsg=msg as MyCustomMessge;
      Debug.Log(string.Format("Got the message! {0},{1}",castMsg._intValue,castMsg._floatValue));
      return true;
  }
 
  public void Despawned()
  {
    MessagingSystem.Instance.DetachListener(typeof(MyCustomMesage),this.HandleMyCustomMessage);
  }
}

2.预制池系统

public static class PrefabPoolingSystem 
{
    static Dictionary<GameObject,PrefabPool> _prefabToPoolMap=new Dictionary<GameObject, PrefabPool>();
    static Dictionary<GameObject,PrefabPool> _goToPoolMap=new Dictionary<GameObject, PrefabPool>();
 
    public static GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation)
    {
        if(!_prefabToPoolMap.ContainsKey(prefab)){
            _prefabToPoolMap.Add(prefab,new PrefabPool());
        }
        PrefabPool pool=_prefabToPoolMap[prefab];
        GameObject go=pool.Spawn(prefab,position,rotation);
        _goToPoolMap.Add(go,pool);
        return go;
    }
 
    public static GameObject Spawn(GameObject prefab){
        return Spawn(prefab,Vector3.zero,Quaternion.identity);
    }
 
    public static bool Despawn(GameObject obj){
        if(_goToPoolMap.ContainsKey(obj)){
            Debug.LogError(string.Format("Object {0} not managed by pool system!",obj.name));
            return false;
        }
        PrefabPool pool=_goToPoolMap[obj];
        if(pool.Despawned(obj)){
            _goToPoolMap.Remove(obj);
            return true;
        }
        return false;
    }
}

PrefabPool代码在后面

3.预置池

public struct PoolablePrefabData 
{
    public GameObject go;
    public IPoolableComponent[] poolableComponents;
}
public class PrefabPool
{
  Dictionary<GameObject,PoolablePrefabData> _activeList=new Dictionary<GameObject, PoolablePrefabData>();
  Queue<PoolablePrefabData> _inactiveList=new Queue<PoolablePrefabData>();
}

4.生成对象

public GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation)
  {
    PoolablePrefabData data;
    if(_inactiveList.Count>0)
    {
        data=_inactiveList.Dequeue();
    }
    else
    {
        GameObject newGO=GameObject.Instantiate(prefab,position,rotation) as GameObject;
        data=new PoolablePrefabData();
        data.go=newGO;
        data.poolableComponents=newGO.GetComponents<IPoolableComponent>();
    }
    data.go.SetActive(true);
    data.go.transform.position=position;
    data.go.transform.rotation=rotation;
    for(int i=0;i<data.poolableComponents.Length;++i){
        data.poolableComponents[i].Spawned();
    }
    _activeList.Add(data.go,data);
    return data.go;
  }

5.预先生成实例

将主要的内存分配推到场景生命周期的开始时刻。

PrefabPoolingSystem 添加:

public static void Prespawn(GameObject prefab,int numToSpawn)
    {
        List<GameObject> spawnedObjects=new List<GameObject>();
        for(int i=0;i<numToSpawn;i++){
            spawnedObjects.Add(Spawn(prefab));
        }
        for(int i=0;i<numToSpawn;i++){
            Despawn(spawnedObjects[i]);
        }
        spawnedObjects.Clear();
    }

使用:

public class OrcPreSpawner : MonoBehaviour
{
    [SerializeField] GameObject _orcPrefab;
    [SerializeField] int _numToSpawn=20;
    // Start is called before the first frame update
    void Start()
    {
        PrefabPoolingSystem.Prespawn(_orcPrefab,_numToSpawn);
    }
}

6.对象的回收

PrefabPool 添加:

public bool Despawned(GameObject objToDespawn)
  {
    if(!_activeList.ContainsKey(objToDespawn)){
        Debug.LogError("This Object is not managed by this object pool! :"+objToDespawn);
        return false;
    }
    PoolablePrefabData data=_activeList[objToDespawn];
    for(int i=0;i<data.poolableComponents.Length;++i){
        data.poolableComponents[i].Despawned();
    }
    data.go.SetActive(false);
    _activeList.Remove(objToDespawn);
    _inactiveList.Enqueue(data);
    return true;
  }

7.预制池测试

public class PrefabPoolingTestInput : MonoBehaviour
{
    [SerializeField] GameObject _orcPrefab;
    [SerializeField] GameObject _trollPrefab;
    [SerializeField] GameObject _ogrePrefab;
    [SerializeField] GameObject _dragonPrefab;
    List<GameObject> _orcs=new List<GameObject>();
    List<GameObject> _trolls=new List<GameObject>();
    List<GameObject> _ogres=new List<GameObject>();
    List<GameObject> _dragons=new List<GameObject>();
 
    void Start()
    {
        PrefabPoolingSystem.Prespawn(_orcPrefab,11);
        PrefabPoolingSystem.Prespawn(_trollPrefab,8);
        PrefabPoolingSystem.Prespawn(_ogrePrefab,5);
        PrefabPoolingSystem.Prespawn(_dragonPrefab,1);
    }
 
    void SpawnObject(GameObject prefab,List<GameObject> list){
        GameObject obj=PrefabPoolingSystem.Spawn(prefab,5.0f*Random.insideUnitSphere,Quaternion.identity);
        list.Add(obj);
    }
 
    void DespawnRandomObject(List<GameObject> list){
        if(list.Count==0){
            return;
        }
        int i=Random.Range(0,list.Count);
        PrefabPoolingSystem.Despawn(list[i]);
        list.RemoveAt(i);
    }
 
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Alpha1))
        {
            SpawnObject(_orcPrefab,_orcs);
        }
        if(Input.GetKeyDown(KeyCode.Alpha2))
        {
            SpawnObject(_trollPrefab,_trolls);
        }
        if(Input.GetKeyDown(KeyCode.Alpha3))
        {
            SpawnObject(_ogrePrefab,_ogres);
        }
        if(Input.GetKeyDown(KeyCode.Alpha4))
        {
            SpawnObject(_dragonPrefab,_dragons);
        }
        if(Input.GetKeyDown(KeyCode.Q))
        {
            DespawnRandomObject(_orcs);
        }
        if(Input.GetKeyDown(KeyCode.W))
        {
            DespawnRandomObject(_trolls);
        }
        if(Input.GetKeyDown(KeyCode.E))
        {
            DespawnRandomObject(_ogres);
        }
        if(Input.GetKeyDown(KeyCode.R))
        {
            DespawnRandomObject(_dragons);
        }
    }
}

8.预制池和场景加载

PrefabPoolingSystem 添加

public static void Reset(){
        _prefabToPoolMap.Clear();
        _goToPoolMap.Clear();
    }

在新场景加载前调用

9.预制池总结

注意事项和系统扩展

8.4.16 IL2CPP优化

https://www.oreilly.com/library/view/unity-2017-game/9781788392365/3d11cbcf-b0e8-4aed-997d-1f8f3f0980be.xhtml

IL2CPP optimizations

Unity Technologies have released a few blog posts on interesting ways to improve the performance of IL2CPP in some circumstances, but they can be difficult to manage. If you're using IL2CPP and need to eke out the last little bit of performance from our application that we can, then check out the blog series at the following links:

8.4.17 WebGL优化

https://blogs.unity3d.com/2016/09/20/understanding-memory-in-unity-webgl/

https://blogs.unity3d.com/2016/12/05/unity-webgl-memory-the-unity-heap/

8.5 Unity、Mono和IL2CPP的未来

相关技术讨论

新技术,弃用技术

路线图:

https://unity3d.com/unity/roadmap

即将到来的C# Job System

现在2019、2020这个系统还没引入到我们实际项目中,仅仅是之前调研了一下。

8.6 小结

性能优化工作的一个不变成本是开发时间。

如果能够加快开发工作,在工作中比较烦琐的部分节省一些时间,就有希望节省足够的时间,来实际实现尽可能多的优化技术。

 

第9章 提示与技巧

9.1 编辑器热键提示

9.1.1 GameObject

Ctrl+D:复制GameObject

Ctrl+Shift+N:新建空的GameObject

Ctrl+Shift+A:打开AddComponent菜单 (和钉钉的截图冲突....)

9.1.2 Scene窗口

Shift+F/双击Hierarchy:跟随聚焦选中的物体

Alt+左键:环绕选中物体

Alt+右键:拉近拉远

Ctrl+左键拖动:对齐网格移动

Ctrl+左键旋转/缩放:按设定数值旋转/缩放

按住V键移动:根据顶点对齐物体

9.1.3 数组

Ctrl+D:复制元素并插入当前选择之后。

Shift+Delete:从引用数据中删除条目,并压缩数组。第一次删除条目,第二次压缩数组。

9.1.4 界面

Alt+点击箭头:展开对象的全部层级

Ctrl+P:Play/Stop

Ctrl+Shift+P:Pause

Ctrl+Alt+0:在我的电脑上没有效果,其他的都有用

9.1.5 在编辑器内撰写文档 

VS中按Ctrl+Alt+M 接着 Ctrl+H :在Unity文档中搜索给定的关键字或类

9.2 编辑器UI提示

9.2.1 脚本执行顺序

9.2.2 编辑器文件

每个人使用相同版本的元数据文件是有必要的。

资源数据转换为文本

Editor日志文件打开:Open Editor Log

AddTab

锁定Inspector

9.2.3 Inspector窗口

Debug Mode:显示原始数据和私有字段等。

如果数组元素是类/结构,类/结果的子元素的第一个元素是字符串,在Inspector中该元素名称就是字符串名称,而不是Element 0。

独立Preview:右键点击顶部

9.2.4 Project窗口

拖动资源到脚本中的序列化引用进行赋值。

一列布局

Select Dependencies

9.2.5 Hierarchy窗口

过滤组件

t:<component name>

t:render

9.2.6 Scene和Game窗口

9.2.7 Play模式

保存Play模型下的变动,Copy和创建预设

Frame Skip,逐帧调试

9.3 脚本提示

9.3.1 一般情况

修改脚本、着色器和Compute Shader文件的不同模板

Windows:<Unity install>\Editor\Data\Resources\ScriptTemplates\

基于断言的调试

9.3.2 特性

[Range]

[FormerlySerializedAs]

[SelectionBase]

[RequireComponent]

[ExecuteInEditMode]

9.3.3 日志

可以为调试字符串添加富文本标签

Debug.Log("<color=red>[ERROR]</color>This is a <i>very</i><size=14><b>specific</b></size> kind of log message");
 
 

9.3.4 有用的链接

https://unity3d.com/learn/tutorials/topics/scripting

http://answers.unity3d.com/questions/723845/what-are-the-c-error-messages.html

嵌套协程

API历史页面

9.4 自定义编辑脚本和菜单提示

自定义菜单热键

[MenuItem("My Menu/Menu Item _k")] K键触发

%,#,&使用Ctrl,Shfit,Alt

MenuItem.html

EditorGUIUtility.PingObject()

PropertyDrawer

[ContextMenu][ContextMenuItem]

AssetImporter.userData

9.5 外部提示

 

其他提示

9.6 本章小结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值