注意 此 Invoke unity的接口方法,不是 .net里的 invoke 方法,
https://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html
场景
遇到需要间隔一段时间再来调用的情况,如果不想用协程,可以使用 Invoke()实现。
MonoBehaviour.Invoke 延迟调用 方法签名: void Invoke(string methodName, float time);
在time秒后,延迟调用方法methodName。
Invoke() 方法是 Unity3D 的一种委托机制
如: Invoke("SendMsg", 5); 它的意思是:5 秒之后调用 SendMsg() 方法;
使用 Invoke() 方法需要注意 3点:
1 :它应该在 脚本的生命周期里的(Start、Update、OnGUI、FixedUpdate、LateUpdate)中被调用;
2:Invoke(); 不能接受含有 参数的方法;
3:在 Time.ScaleTime = 0; 时, Invoke() 无效,因为它不会被调用到
Invoke() 也支持重复调用:InvokeRepeating("SendMsg", 2 , 3);
这个方法的意思是指:2 秒后调用 SendMsg() 方法,并且之后每隔 3 秒调用一次 SendMsg () 方法
写在void start()函数里的InvokeRepeating("printMessage",2f,3f);方法和写在void update()的Invoke()方法效果可能一样。
还有两个重要的方法:
- IsInvoking:用来判断某方法是否被延时,即将执行
- CancelInvoke:取消该脚本上的所有延时方法
using UnityEngine; using System.Collections;
public class DelayScript : MonoBehaviour { //当前时间 private float nowTime; //执行重复方法的次数 private int count; // Use this for initialization void Start () { nowTime = Time.time; Debug.Log("时间点:"+nowTime); this.Invoke("setTimeOut", 3.0f); this.InvokeRepeating("setInterval", 2.0f, 1.0f); }
private void setTimeOut() { nowTime = Time.time; Debug.Log("执行延时方法:" + nowTime); }
private void setInterval() { nowTime = Time.time; Debug.Log("执行重复方法:" + nowTime); count += 1; if(count==5) this.CancelInvoke(); } }
using UnityEngine; using System.Collections; using UnityEngine.Events;
public class InvokeTest : MonoBehaviour { public GameObject Prefabs; private Vector3 v3; public int i = 5; // Use this for initialization void Start () { v3 = new Vector3(0, 0, 0); Invoke("TestIns", 1); //InvokeRepeating("TestIns", 2, 1); //调用InvokeRepeating时候解开 }
// Update is called once per frame void Update () { if (v3.x == 20) CancelInvoke("TestIns"); }
void TestIns() { //v3.x += i; //调用InvokeRepeating时候解开 Instantiate(Prefabs,v3,Quaternion.identity); }
}
Invoke还有一个用法就是可以激活UnityEvent。
下面是例子。
using UnityEngine; using System.Collections; using UnityEngine.Events;
public class TestLoader : MonoBehaviour { [SerializeField] protected UnityEvent onLoad = new UnityEvent(); [SerializeField] protected UnityEvent unLoad = new UnityEvent(); // Use this for initialization void Start () { Load(); UnLoad(); }
// Update is called once per frame void Update () {
}
[ContextMenu("Load")] public void Load() { onLoad.Invoke(); }
[ContextMenu("unLoad")] public void UnLoad() { unLoad.Invoke(); } }
这里有两个序列化的UnityEvent,可能看代码不是很直观,直接上图。
是不是感觉很眼熟。对就是,像我们经常看到的Button下边的OnClick其实就是这种东西。
我们为这个东西挂上我们自己的测试脚本。
但是这时候我们想要调用测试脚本的方法了,这时候就用到了Invoke。
这里会自动调用UnityEvent下的脚本的指定方法。
测试脚本的代码如下。
using UnityEngine; using System.Collections;
public class onLoadScripts1 : MonoBehaviour { public void systemLoadMessage() { Debug.Log("=======WhiteTaken======="); }
public void systemLoadMessage(int i) { Debug.Log("=====WhiteTaken:" + i + "======"); } }
using UnityEngine; using System.Collections;
public class onLoadScripts2 : MonoBehaviour { public void systemLoadLog() { Debug.Log("--------WhiteTaken----------"); } }
using UnityEngine; using System.Collections;
public class unLoadScripts : MonoBehaviour { public void systemUnLoad(string name) { Debug.Log("===----+ "+name+"卸载:-----====="); } }
下面两个脚本都挂在摄像机上,3秒后调用本类的私有和公共方法都成功了,而调用另一个类的私有和公共方法的时候Console面板都有灰色的字提示找不到方法。
所以Invoke用来调用本类中的方法。
关于生命周期引起的Bug:
有一个游戏对象,上面挂着 3 个脚本,如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SaveAndRead : MonoBehaviour {
public static SaveAndRead instance;
///
/// 保存所有 单独的DataStore
///
public List allAloneDataStores = new List();
///
/// 初始化SaveAndRead类
///
public void InitSaveAndRead()
{
if (instance == null)
{
instance = new SaveAndRead();
}
}
void Awake()
{
InitSaveAndRead();
GetAll_AreaDataStoresInTrigger();
}
void GetAll_AreaDataStoresInTrigger()
{
//拿到所有子物体的 AreaAloneDataStores类
AreaAloneDataStores[] temp_list02 = GetComponentsInChildren();
for (int i = 0; i < temp_list02.Length; i++)
{
for (int j = 0; j < temp_list02[i].aloneDataStores.Count; j++)
{
allAloneDataStores.Add(temp_list02[i].aloneDataStores[j]);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistinguishDataStoresInTrigger : MonoBehaviour {
///
/// 存放单独游戏对象的类
///
AreaAloneDataStores temp_AreaAloneDataStorest;
///
/// 存放触发Trigger的所有游戏对象的List
///
public List objs = new List();
void Start()
{
//拿到存放单独游戏对象的类
temp_AreaAloneDataStorest = gameObject.GetComponent();
//确保先通过 OnTriggerEnter 拿到游戏对象 , 再调用 GetAllDataStores 拿到游戏对象上的 DataStore
Invoke("GetAllDataStores", 1f);
}
///
/// 拿到所有触发Trigger的游戏对象
///
///
void OnTriggerEnter(Collider other)
{
if (other.gameObject.layer == 8)
{
objs.Add(other.gameObject);
other.gameObject.SetActive(false);
}
}
///
/// 拿到所有的DataStore
///
void GetAllDataStores()
{
for (int i = 0; i < objs.Count; i++)
{
//拿到 Trigger 的 DataStoreSaveToTrigger 脚本里面存的 DataStore
DataStore temp_DataStore = objs[i].GetComponent().dataStore;
//Debug.Log(this.name + "--" + objs[i].name);
//判断 DataStore 是否是Alone的,并加到不同的类的List里
if (temp_DataStore.isAloneSave == true)
{
temp_AreaAloneDataStorest.aloneDataStores.Add(temp_DataStore);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AreaAloneDataStores : MonoBehaviour {
///
/// 单独的DataStore的List
///
public List aloneDataStores = new List();
}
Bug:
代码的意思是通过 DistinguishDataStoresInTrigger 脚本拿到一些游戏对象,然后对这些游戏对象上的 DataStoreSaveToTrigger 脚本里的 DataStore进行分类,DataStore里的isAloneSave是true时,就把 DataStore 放在 AreaAloneDataStores类 的 aloneDataStores 里。
当时是有2个 DataStore 的 isAloneSave 是 true ,在 DistinguishDataStoresInTrigger 脚本 进行判断后,也确实放了2个 DataStore 到 AreaAloneDataStores类 的 aloneDataStores 里,但是在 SaveAndRead脚本 里通过 temp_list02[i].aloneDataStores 来获得AreaAloneDataStores类 的 aloneDataStores 时,里面却一个也没有。
原因:
是 DistinguishDataStoresInTrigger脚本 里的 Invoke("GetAllDataStores", 1f);
因为它所以在 DistinguishDataStoresInTrigger脚本 执行了 Start 方法 1 秒之后,才开始执行 GetAllDataStores 方法,才把 2 个 DataStore 放到 AreaAloneDataStores类 的 aloneDataStores 里。
而 SaveAndRead脚本 在Awake 方法里就通过 GetAll_AreaDataStoresInTrigger 方法拿到了 AreaAloneDataStores类 的 aloneDataStores 。
所以在 SaveAndRead脚本里拿到 AreaAloneDataStores类 的 aloneDataStores 的时候,DistinguishDataStoresInTrigger脚本 还没有把 2 个 DataStore 放到 AreaAloneDataStores类 的 aloneDataStores 里。
解决方法:
把 SaveAndRead类 Awake 方法里的 GetAll_AreaDataStoresInTrigger(); 改为 Invoke("GetAll_AreaDataStoresInTrigger", 2f); 就可以了。