Unity全面入门笔记8-了解GameObject(2)

深入了解GameObject

GameObject的属性

  • Tag

Name下方的选项组,对应GameObject.tag,多用在控制碰撞和查找物体上。在创建标签时可以使用系统自带的标签,也可以在下拉菜单中点击New Tag来创造自定义标签。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pNepgVu7-1569681832645)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568358774570.png)]

点击Add Tag…后,Inspector会跳转到Tags&Layers界面,在该界面下创建自定义标签后,需要重新点选GameObject再将其tag设置为新创建的Tag。

在脚本中我们可以使用Tag来查找GameObject,使用Tag来查找比使用Name来查找消耗的资源更少一些:

GameObject GameObject.FindGameObjectWithTag(string tag);
GameObject[] GameObject.FindGameObjectsWithTag(string tag);

CompareTag方法可以用于判断物体的Tag:

bool GameObject.CompareTag(string tag);

这个函数比简单的使用字符串判断句this.tag == tag要高效的多。

CompareTag最常用于碰撞的判定,如下面的例子:

void OnCollisionEnter(collider other)
{
    if(other.CompareTag("bullet"))
    {
        GameOver();
    }
}
  • Layer

Tag右侧的选项组,对应脚本中的GameObject.layer。它的作用涉及到了摄像机的渲染和物理碰撞的检测。

同样的我们可以自定义Layer。Unity最多支持32层Layer,因为Unity经常需要使用Int32作为掩码来输入Layer信息。比如说我需要使用射线检测系统来跟踪鼠标指向的目标,但只需要检测Unit而不需要检测其它物体,我们就可以将Unit设置为单独的一个Layer,比如User Layer8 = Unit,然后使用下面的代码来实现:

readonly LayerMask mask = 1<<8;
void Update()
{
    if(Input.GetMouseButtonDown(0))
    {
        RaycastHit hit;
        if(Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit, mask.value));
        {
            Debug.Log("Hit on a Unit!");
            DoSomething();
        }
	}
}

上面的代码中,Raycast等射线检测的知识将等到物理章节中解释。作为参数的mask作为一个掩码,从最低位到最高位分别表示Layers列表中的第0个元素到第31个元素,每一位如果是1则表示选中该层Layer,为0表示未选中,而Raycast函数中接收mask传入的掩码,只对第8层进行检测,从而实现目标效果。

层与层的碰撞可以在主界面菜单栏的"Edit"->“Project Settings”->“Physics"中修改,2D游戏则是"Edit”->“Project Settings”->“Physics 2D”。在这个窗口页面的最下方有一个碰撞检测表格,在这个表格中勾除某些格就可以使对应的两个层间不再发生碰撞,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JOdeMgID-1569681832646)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360459997.png)]

在上图的情况中,标签为New Layer的物体不再和其他标签为New Layer或Default的物体发生碰撞。

可以通过锁定某一层来防止不小心移动该层的物体。具体的做法是在Unity主界面的工具栏中点击Layers下拉菜单并改变对应层最右边的锁定选项。在这个菜单中的眼睛图标代表了一个层的可见性,当该选项被勾除则这一层不会被渲染。后面的锁是锁定性,被勾选时该层物体不能被选中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bbEKrVnx-1569681832647)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360534346.png)]

在改变一个父物体的Layer属性时,会弹出如图所示的窗口,询问是否要将改变应用到所有子物体上。选择Yes则子物体的Layer也会一起改变,选择No则只改变父物体的Layer。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCnIefKZ-1569681832647)(C:\Users\UPiracy\AppData\Roaming\Typora\typora-user-images\1568360576672.png)]

生成与销毁GameObject

  • 使用Instantiate实例化新的GameObject

GameObject是不能使用new来实例化的,必须使用Instantiate函数:

GameObject Instantiate(GameObject obj);
GameObject Instantiate(GameObject obj, Transform father);
GameObject Instantiate(GameObject obj, Vector3 position, Quaternion rotation);
GameObject Instantiate(GameObject obj, Vector3 position, Quaternion rotation, Transform father);
class InstantiateTest : MonoBehavior
{
	[SerilizedField]
	private GameObject bullet;
	
	void Start()
	{
		GameObject newBullet = Instantiate(bullet, transform.position, transform.rotation);
		newBullet.GetComponent<Bullet>().Shoot();
	}
}
  • 使用Destroy销毁GameObject
void Destroy(GameObject obj);
void Destroy(GameObject obj, float delay);
class ExplosionEffectManager : MonoBehavior
{
	[SerilizedField]
	private GameObjcet explosionEffect;
	
	public void ShowEffect(Vector3 center)
	{
		GameObjcet newEffect = Instantiate(explosionEffect, center + Random.insideUnitSphere, transform.rotation);
		Destroy(newEffect, 5f);
	}
}
  • 使用物品池来管理物体

每次调用Instantiate都会申请新的内存空间,而每次Destroy都会将这些内存遗弃等待GC处理。对于经常生成又经常被摧毁的物品,如子弹、特效等,快速而大量的Instantiate和Destroy会导致大量的GC消耗。这些物体最好使用一个物品池来管理,物品池可以是栈也可以是队列或其它的什么结构,其根本思路在于通过改变activeSelf而非直接使用Destroy来实现类似销毁的效果,以此减轻GC负担。

下面的例子是用一个栈封装的子弹池,其它脚本在创建或销毁子弹时不再使用Instantiate和Destroy,转而使用BulletPool.Born和BulletPool.Dies:

public class BulletPool
{
    public static Stack<GameObject> pool = new Stack<GameObjcet>();
    
    public static GameObject Born(GameObject bullet, Vector3 position)
    {
        if(pool.Count == 0)
        {
            GameObject obj = Instantiate(bullet, position, new Quaternion(0,0,0,0));
            obj.Initialize();
            return obj;
        }
        else
        {
            GameObject obj = pool.Pop();
            obj.SetActive(true);
            obj.Initialize();
            return obj;
        }
    }
    
    public static void Dies(GameObject bullet)
    {
        pool.Push(bullet);
        bullet.SetActive(false);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章