阅读国外Coder教程时的笔记,Object Management篇(三)

原文地址:https://catlikecoding.com/unity/tutorials/object-management/multiple-scenes/
阅读国外Coder教程时的笔记,Object Management篇(一)
阅读国外Coder教程时的笔记,Object Management篇(二)

1.相信很多人都知道用 UnityEngine下的Instantiate(GameObject)去创建预制体。当一大堆物体被创建出来,都会出现在Hierarchy面板,非常的杂乱,对开发有一定的影响。有人会说通过Transform.setParent为之设一个父级物体就好了,这样可以折叠起来就不会显得那么乱了。但是这样做有一个弊端,就是把创建的预制体和父级绑定了,如果你想改变预制体的位置信息等等,父级都会有一定的影响。这里有一个办法,就是动态创建一个预制体专用场景,这个场景只有游戏运行时才会被创建出来。主要通过命名空间UnityEngine.SceneManagement实现。
比如我在脚本ShapeFactory里写下面一段话:

Scene poolScene;
poolScene = SceneManager.CreateScene(name);
//这里的name是脚本的一个属性,代表脚本的名字

游戏运行时就会创建出名为ShapeFactory的场景,并在游戏结束时消失:
在这里插入图片描述
然后通过SceneManager.MoveGameObjectToScene(GameObject)方法即可把创建去物体都拖着新创建出的场景中。

2.如果你要从场景里移除预制体,则相应的需要使用Destroy(GameObject)。这里假设有一个shapes的List,我们通过以下代码可以

		if (shapes.Count > 0) {
    
    
			int index = Random.Range(0, shapes.Count);
			Destroy(shapes[index]);
			shapes.RemoveAt(index);
		}
		//通过Random.Range随机一个索引销毁并移除出List,前提时List的长度必须大于0

咋一看是一个简单的操作,其实我们忽略了一个过程就是,系统会自动把空位一位接一位的往后移,直到移除List。其实也是一种资源的浪费。
在这里插入图片描述
如果我们加入以下代码,就更高效得完成了删除操作,附上概念图:

		if (shapes.Count > 0) {
    
    
			int index = Random.Range(0, shapes.Count);
			Destroy(shapes[index].gameObject);
			int lastIndex = shapes.Count - 1;
			shapes[index] = shapes[lastIndex];
			shapes.RemoveAt(lastIndex);
		}

在这里插入图片描述
4.UI Slider的操作
以前我只知道slider下添加事件的功能,都是在事件里添加方法。比如button下在OnClick下添加一个自定义方法,这样点击时就会触发那个方法。今天才知道除了添加方法外还可以直接添加变量,比如在Slider的OnValueChanged里,直接就把变量值和slider值绑定了。具体如下:

public float DestructionSpeed {
    
     get; set; }

在这里插入图片描述

5.一般情况下,用Destroy销毁物体的操作是没有问题的,但是当一个游戏中需要大量创建和销毁预制体的时候,用以上方法就会造成资源的浪费。可以通过Windows/Profiler or Window/Analysis/Profiler 去观察游戏运行下各资源的占用,或者直接在Build Setting里 勾上Development Build 和Autoconnect Profiler,直接选择Build and Run,也可以达到观察资源消耗的目的。观察发现GC即资源消耗的80%都来自 Instantiate。
在这里插入图片描述
由于凡是Object都会带有SetActive的方法,可以通过设置true和false来关闭和显示物体。我们可以想象创建一个池子,这里叫对象池,来存放这些物体。其实就是建立一个数组,可以是List也可以是Dictionary,也可以是Array。注意,它们都需要引入命名空间System.Collections.Generic。这里我们以数组和List举例:

using System.Collections.Generic;
...
List<Shape>[] pools;
...
void CreatePools(){
    
    
	pools=new List<Shape>[prefabs.Length]
	for(int i=0;i<pools.Length;i++)
	{
    
    
	pools[i]=new List<Shape>();
	}
}
//这里其实就是为不同预制体即Prefabs数组里的成员,各自创建了一个List用于存放对象

在确认完池子已经创建完毕之后,我们就可以改变物体的实例化模式了:

Shape instance;	
	List<Shape> pool = pools[shapeId];//这里shapeID代表预制体在数组里的索引
	int lastIndex = pool.Count - 1;//通过把实例化List最后一个对象,提高效率
	if (lastIndex >= 0) {
    
    
		instance = pool[lastIndex];		
		instance.gameObject.SetActive(true);//改变SetActive的值=把物体从池中取出
		pool.RemoveAt(lastIndex);
	}
	else{
    
    
	instance=Instatiate(prefabs[shapeID]);//当池子没有对象的时候,可以创建对象
	}

改变销毁物体的方式

	public void Reclaim (Shape shapeToRecycle) {
    
    
		if (pools == null) {
    
    
			CreatePools();
		}
		pools[shapeToRecycle.ShapeId].Add(shapeToRecycle);//把物体放回池子
		shapeToRecycle.gameObject.SetActive(false);//物体Active属性设为false,物体消失
	}

网上还有很多种写法可供参考,中心思想都是差不多的。

以上,希望对大家学习有帮助。

猜你喜欢

转载自blog.csdn.net/Jay12243/article/details/113114933