您现在的位置是:首页 >技术教程 >【学习笔记】unity脚本学习(五)【常用的方法函数Destroy、Instantiate 、SendMessage、invoke 、Coroutine】网站首页技术教程

【学习笔记】unity脚本学习(五)【常用的方法函数Destroy、Instantiate 、SendMessage、invoke 、Coroutine】

布鲁布鲁吐泡泡 2023-05-28 20:00:03
简介【学习笔记】unity脚本学习(五)【常用的方法函数Destroy、Instantiate 、SendMessage、invoke 、Coroutine】

转载请注明出处:🔗https://blog.csdn.net/weixin_44013533/article/details/130233098
视频参考 极客学院Unity3D视频精讲课程

常用的方法函数

Object体系结构

在这里插入图片描述
在这里插入图片描述
可以看到MonoBehaviour继承自Component,Component继承自Object

MonoBehaviour复习

MonoBehaviour官网API
之前学的start awake等都是Message事件响应函数,它们都是我们在脚本中直接使用的回调函数
在这里插入图片描述

继承的变量

在这里插入图片描述

继承自Object的方法

在这里插入图片描述

Destroy 物体的销毁

比较好懂

// 删除gameObject
Destroy(gameObject);
// Removes this script instance from the game object
Destroy(this);
// Removes the rigidbody from the game object
Destroy(GetComponent<Rigidbody>());
// Kills the game object in 5 seconds after loading the object 延迟删除)
Destroy(gameObject, 5);

注意:

  • 删除gameobject也会删除其下所有子物体

DestroyImmediate 立即销毁对象(强烈建议您改用 Destroy)

public static void DestroyImmediate (Object obj, bool allowDestroyingAssets= false);

  • obj 要销毁的对象。
  • allowDestroyingAssets 设置为 true 将允许销毁资源。
    该函数应只在编写 Editor 代码时使用,因为在编辑模式下, 永远不会调用延迟销毁。 在游戏代码中,您应该改用 Object.Destroy。Destroy 始终延迟进行 (但在同一帧内执行)。 使用该函数时要务必小心,因为它可以永久销毁资源! 另请注意,切勿循环访问数组并销毁正在迭代的元素。这会导致严重的问题(这是一条通用的编程实践,而不仅仅是在 Unity 中)。

Object.DontDestroyOnLoad

public static void DontDestroyOnLoad (Object target);
在加载新的 Scene 时,请勿销毁 Object。(scene切换时会释放老场景内存,如果想不释放某个对象,就可以用这个方法)

    void Start()
    {
        DontDestroyOnLoad(gameObject);
    }
    // Update is called once per frame
    [System.Obsolete]
    void Update()
    {
        if(Input.GetKey(KeyCode.Space)){
            //Application.LoadLevel("emp");
            Application.LoadLevel(1);
        }
    }

可以用场景名称,也可以用scenes in build中scene的index
在这里插入图片描述
如图,实现点击空格切换场景,但脚本所绑定的cube未被释放内存(这个方法可以用在比如切换场景但背景音乐丝滑过渡的场合)
在这里插入图片描述
注意:

  • 物体一个组件不释放,那么这个物体和子物体都将得到保留
    void Start()
    {
        DontDestroyOnLoad(GetComponent<Rigidbody>());
    }

在这里插入图片描述
脚本绑定到cube
在这里插入图片描述
但如果但将脚本挂cube(1)或者cube(2)上,则DontDestroyOnLoad对这三个cube都不产生效果,所以要求不释放内存的对象没有父对象

Object.Instantiate 物体的生成

public static Object Instantiate (Object original);
public static Object Instantiate (Object original, Transform parent);
public static Object Instantiate (Object original, Transform parent, bool instantiateInWorldSpace);
public static Object Instantiate (Object original, Vector3 position, Quaternion rotation);
public static Object Instantiate (Object original, Vector3 position, Quaternion rotation, Transform parent);
参数

  • original 要复制的现有对象。
  • position 新对象的位置。
  • rotation 新对象的方向。
  • parent 将指定给新对象的父对象。
  • instantiateInWorldSpace When you assign a parent Object, pass true to position the new object directly in world space. Pass false to set the Object’s position relative to its new parent.

描述
克隆 original 对象并返回克隆对象。
此函数会通过与编辑器中的复制命令类似的方式创建对象的副本。如果要克隆 GameObject,则可以指定其位置和旋转(否则,这些默认为原始 GameObject 的位置和旋转)。如果要克隆 Component,则也会克隆它附加到的 GameObject(同样可指定可选的位置和旋转)。

克隆 GameObject 或 Component 时,也将克隆所有子对象和组件,它们的属性设置与原始对象相同。
注意

  • 使用instantiate方法生成对象,比较消耗资源,尽管可以配合使用destroy销毁对象释放。后续优化可以使用对象池概念(估计类似java连接池这种)
  • 一般不生成场景中存在的物体,因为它需要加载,在其他场景就没法复用——>使用prefab预设体(hierarchy中将对象拉到assets中形成pref ab)
类子弹生成案例
    public Object obj;
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)){
            //Quaternion.identity:旋转角默认0
            Object.Instantiate(obj,new Vector3(0,5,0),Quaternion.identity);
        }
    }

在这里插入图片描述
添加物体自销毁、施加一个脉冲力

    public float survivalTime = 3;
    void Start()
    {
        Destroy(gameObject,survivalTime);
    }
    public Rigidbody rig;
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.Space)){
            //Quaternion.identity:旋转角默认0
            // 声明为Rigidbody,注意这里输入的是Rigibody,返回的也是Rigibody,如果输入GameObject输出Rigibody,会报NullReferenceException异常
            Rigidbody copyRig = Object.Instantiate(rig,new Vector3(0,5,0),Quaternion.identity) as Rigidbody;
            copyRig.AddForce(Vector3.forward*5,ForceMode.Impulse);
        }
    }

在这里插入图片描述

继承自Component的方法

Component.tag和GameObject.tag都表示此游戏对象的标签。

Component.CompareTag 比较tag

public bool CompareTag (string tag);
Checks the GameObject’s tag against the defined tag.

注意比较tag有两种方法

  • CompareTag(“a”)比xxx.tag == "a"高效,原因是省去tag赋值的操作

消息推送

  • SendMessage
  • SendMessageUpwards
  • BroadcastMessage

使用场景:只要我们得到了一个类的引用,便可以向它的子类/类本身/父类传递某些消息,最终达到直接执行某个函数的目的,这样非常方便实现一个引用,简化开发复杂程度。

Component.SendMessage 消息推送

public void SendMessage (string methodName);
public void SendMessage (string methodName, object value);
public void SendMessage (string methodName, object value, SendMessageOptions options);
public void SendMessage (string methodName, SendMessageOptions options);

参数

  • methodName 要调用的方法的名称。
  • value 该方法的可选参数。
  • options 如果目标对象没有为消息实现该方法,是否应报错?

描述
调用此游戏对象中的每个 MonoBehaviour 上名为 methodName 的方法。
通过具有零参数,该接收方法可选择忽略此参数。 如果选项设置为 SendMessageOptions.RequireReceiver,则在任何组件均未拾取此消息时输出错误。
注意,不会将消息发送到非活动对象(即,在 Editor 中或使用 GameObject.SetActive 函数已停用的对象)。

Component.SendMessageUpwards

public void SendMessageUpwards (string methodName, SendMessageOptions options);
public void SendMessageUpwards (string methodName, object value= null, SendMessageOptions options= SendMessageOptions.RequireReceiver);

调用此游戏对象中的每个 MonoBehaviour 上或此行为的每个父级上名为 methodName 的方法。

Component.BroadcastMessage

public void BroadcastMessage (string methodName, object parameter= null, SendMessageOptions options= SendMessageOptions.RequireReceiver);
public void BroadcastMessage (string methodName, SendMessageOptions options);

调用此游戏对象或其任何子项中的每个 MonoBehaviour 上名为 methodName 的方法。

案例

up说比如足球踢到某个物体,那么就可以用这个方法触发被状物体的某个函数。
这几个方法使用的是反射方式,类似监听者模式,是官方提供的方法,后续应该会有自己实现的更高效的使用监听者模式实现的方法(delegate +event方式)。

这个sendMessage看起来非常重要,后续花个时间做个专题,这里就简单过一下。

MonoBehaviour的invoke方法

Invoke 延迟执行函数

public void Invoke (string methodName, float time);
在 time 秒后调用 methodName 方法。
注意:

  • invoke 的time是根据游戏的时间轴变化的,改变timeScale延迟会相应改变
  • 被调用的方法不能有参数
   public GameObject obj;
    public GameObject obj2;
    public float timeDelay = 2;
    public float myTimeScale = 1;

    // Update is called once per frame
    void Update()
    {
        Time.timeScale = myTimeScale;
        if(Input.GetKeyDown(KeyCode.Space)){
            Object.Instantiate(obj2,new Vector3(0,5,0),Quaternion.identity);
            Invoke("spawnCube",timeDelay);
        }
    }
    void spawnCube(){
        GameObject copyObj = Object.Instantiate(obj,new Vector3(0,5,0),Quaternion.identity);
            Rigidbody rig = copyObj.GetComponent<Rigidbody>();
            rig.AddForce(Vector3.forward*5,ForceMode.Impulse);
    }
    private void OnGUI() {
        GUILayout.TextArea("gameTime:" + Time.time.ToString());
        GUILayout.TextArea("realTime:" + Time.realtimeSinceStartup.ToString());
    }

在这里插入图片描述

InvokeRepeating

public void InvokeRepeating (string methodName, float time, float repeatRate);
time 秒后调用 methodName 方法,然后每repeatRate秒调用一次。

*注意:*如果将时间刻度设置为 0,该函数不起作用。

CancelInvoke

public void CancelInvoke ();
取消该 MonoBehaviour 上的所有 Invoke 调用。

IsInvoking

public bool IsInvoking (string methodName);
是否有任何待处理的 methodName 调用?

MonoBehaviour的Coroutine方法

Coroutine协同执行程序

  • 在主程序运行时同时开启另一段逻辑处理,来协同当前程序的执行
  • 开启协同程序就是类似于开启一个线程
  • 但是协程并不是线程

参数IEnumerator对象,通常有三种方式获得。

  • 第一种方式,也是最常用方式,是使用带有yield指令的协程函数。
    在这里插入图片描述
    Unity-C# 协程 IEnumerator 用法梳理
  • 第二种方式,继承Unity提供的类CustomYieldInstruction,但其实CustomYieldInstruction是实现了IEnumerator。
  • 第三种方式,就是自己实现IEnumerator接口,手动new出一个IEnumerator接口实现类。(后面测试会用到这个类)

StartCoroutine 启动协程。

public Coroutine StartCoroutine (IEnumerator routine);
启动协程。

public float step = 0.01f;
    private void OnMouseDown() {
        StartCoroutine(Move(step));
    }
    IEnumerator Move(float step){
        for(int i=0;i<500;i++){
            this.transform.Translate(step,0,0);
            yield return null;
        }
        this.transform.position = new Vector3(0,1,0);
        Debug.Log(this.name +"移动结束");
    }

在这里插入图片描述

StopAllCoroutines

public void StopAllCoroutines ();
停止在该行为上运行的所有协同程序。

StopCoroutine

public void StopCoroutine (string methodName);
public void StopCoroutine (IEnumerator routine);
public void StopCoroutine (Coroutine routine);
停止在该行为上运行的第一个名为 methodName 的协同程序或存储在 routine 中的协同程序。

小结

这里message传递,协程和方法延迟调用这三个还有待继续深入

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。