Unity基础学习(五)Mono中的重要内容(1)延时函数

发布于:2025-05-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、Mono中的延时函数Invoke

1. Invoke作用:延迟指定时间后执行一次函数。API:

2. InvokeRepeating作用:延迟后开始重复执行函数。API:

3. CancelInvoke作用:停止所有延时函数,或停止指定函数的延时执行。API:

4. IsInvoking作用:检查是否有延时函数正在运行。API:

二、 Update 循环 + 计时器实现延时

三、异步 async/await(需 .NET 4.x+)


一、Mono中的延时函数Invoke

1. Invoke
作用:延迟指定时间后执行一次函数。
API:

void Invoke(string methodName, float time);

methodName:要执行的函数名(字符串形式)。
time:延迟时间(秒)

示例:

void Start()
{
    // 3秒后执行 Shoot 函数
    Invoke("Shoot", 3f);
}

void Shoot()
{
    Debug.Log("发射子弹!");
}

注意事项:
函数名必须为字符串且严格匹配。
无法直接传递参数,需通过成员变量间接实现。

2. InvokeRepeating
作用:延迟后开始重复执行函数。
API:

void InvokeRepeating(string methodName, float delay, float repeatRate);

methodName:函数名(字符串)。
delay:第一次执行的延迟时间。
repeatRate:后续每次执行的间隔时间。

void Start()
{
    // 延迟2秒后,每隔1秒生成一个敌人
    InvokeRepeating("SpawnEnemy", 2f, 1f);
}

void SpawnEnemy()
{
    Debug.Log("敌人生成!");
}

注意:若需停止重复执行,需手动调用 CancelInvoke。 

3. CancelInvoke
作用:停止所有延时函数,或停止指定函数的延时执行。
API:

void CancelInvoke();              // 停止所有延时函数
void CancelInvoke(string methodName); // 停止指定函数名的延时

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        // 停止所有延时函数
        CancelInvoke();
        // 或仅停止 SpawnEnemy 的延时
        CancelInvoke("SpawnEnemy");
    }
}

即使函数未在运行,调用 CancelInvoke 也不会报错。 

4. IsInvoking
作用:检查是否有延时函数正在运行。
API:

bool IsInvoking();              // 检查是否有任何延时函数在执行
bool IsInvoking(string methodName); // 检查指定函数是否在延时执行中

void Update()
{
    if (IsInvoking("SpawnEnemy"))
    {
        Debug.Log("正在生成敌人...");
    }
}

关键特性与注意事项
对象状态影响:
脚本或对象失活(SetActive(false))时,延时函数仍会执行。
对象被销毁(Destroy)或脚本被移除时,延时函数自动终止。

函数要求:
必须为本脚本中声明的无参函数。
函数名需完全匹配(区分大小写)。

参数传递限制:
无法直接传参,可通过类成员变量间接实现:

private int _bulletType;

void Start()
{
    _bulletType = 2;
    Invoke("Shoot", 1f);
}

void Shoot()
{
    Debug.Log("发射类型为 " + _bulletType + " 的子弹");
}

二、 Update 循环 + 计时器实现延时

原理:在 Update 中通过累加 Time.deltaTime 手动控制计时器,触发目标函数。
优点:灵活性高,可中途修改延迟时间或逻辑。
缺点:需自行管理计时器,代码量稍多。

using UnityEngine;

public class TimerExample : MonoBehaviour
{
    private float _delay = 2f;//延时时间
    private float _timer;//定时器
    private bool _hasTriggered;//是否处于延时间隔中

    void Update()
    {
        if (!_hasTriggered)//不处于延时间隔中
        {
            _timer += Time.deltaTime;//改变定时器 直到达到指定延时
            if (_timer >= _delay)
            {
                DoSomething();
                _hasTriggered = true;
            }
        }
    }

    void DoSomething()
    {
        Debug.Log("2秒后执行!");
    }
}

例如实现一个可重复计时的定时器:

using UnityEngine;
using System;

/// <summary>
/// 可重复触发的定时器(基于游戏时间)
/// </summary>
public class GameTimer
{
    // 定时器触发事件
    public event Action OnTimerTriggered;

    private float _interval;      // 触发间隔(秒)
    private float _nextTriggerTime; // 下次触发时间
    private bool _isActive;       // 定时器是否激活
    private bool _isRepeating;    // 是否重复触发

    /// <summary>
    /// 初始化定时器
    /// </summary>
    /// <param name="interval">触发间隔(秒)</param>
    /// <param name="isRepeating">是否重复</param>
    public GameTimer(float interval, bool isRepeating)
    {
        _interval = interval;
        _isRepeating = isRepeating;
    }

    /// <summary>
    /// 启动/重启定时器
    /// </summary>
    public void Start()
    {
        _isActive = true;
        _nextTriggerTime = Time.time + _interval;
    }

    /// <summary>
    /// 停止定时器
    /// </summary>
    public void Stop()
    {
        _isActive = false;
    }

    /// <summary>
    /// 每帧更新定时器状态
    /// </summary>
    public void Update()
    {
        if (!_isActive || Time.time < _nextTriggerTime) return;

        // 触发事件
        OnTimerTriggered?.Invoke();

        if (_isRepeating)
        {
            // 更新下次触发时间(避免误差累积)
            _nextTriggerTime = Time.time + _interval;
        }
        else
        {
            Stop();
        }
    }
}

 测试:

using UnityEngine;

public class TimerExample : MonoBehaviour
{
    private GameTimer _gameTimer;
    private FrameTimer _frameTimer;

    void Start()
    {
        // 创建每5秒触发一次的重复定时器(精准时间)
        _gameTimer = new GameTimer(5f, true);
        _gameTimer.OnTimerTriggered += HandleGameTimer;
        _gameTimer.Start();

        // 创建每1秒触发一次的重复定时器(帧累积)
        _frameTimer = new FrameTimer(1f, true);
        _frameTimer.OnTimerTriggered += HandleFrameTimer;
        _frameTimer.Start();
    }

    void Update()
    {
        _gameTimer.Update();
        _frameTimer.Update();
    }

    void HandleGameTimer()
    {
        Debug.Log($"精准定时器触发,当前时间:{Time.time}");
    }

    void HandleFrameTimer()
    {
        Debug.Log($"帧累积定时器触发,运行时间:{Time.time}");
    }

    void OnDestroy()
    {
        _gameTimer.Stop();
        _frameTimer.Stop();
    }
}

三、异步 async/await(需 .NET 4.x+)

        这个是多线程的知识,主线程也是线程嘛,都可以直接作用。后面再详细介绍关于Task类的知识,这里简单了解即可。只需要知道 这样子也可以延时,后面我们学了协同,那个也可以延时。

async 修饰符:标记方法为异步方法,允许内部使用 await。
await 关键字:暂停当前异步方法的执行,直到其等待的任务完成,期间释放控制权给调用者。

原理:使用 C# 原生异步语法,通过 Task.Delay 实现延时。

优点:代码简洁,支持复杂异步逻辑。

缺点:需 Unity 2018+ 并启用 .NET 4.x 运行时版本。

环境配置

Player Settings -> Configuration -> Scripting Runtime Version -> .NET 4.x Project Settings -> Player -> Api Compatibility Level -> .NET 4.x 新版本可能不叫这个名字选.NET Framwork也是一样的

using UnityEngine;
using System.Threading.Tasks;

public class AsyncAwaitExample : MonoBehaviour
{
    async void Start()
    {
        Debug.Log("开始等待...");
        await Task.Delay(2000); // 毫秒单位
        Debug.Log("2秒后执行!");
        // 注意:此处代码仍在主线程执行
    }
}

注意事项
若需操作 Unity 对象(如 GameObject),需在主线程执行,避免使用 Task.Run。
与协程不同,async/await 不依赖 Unity 的生命周期,但需处理对象销毁时的 Task 终止。 


网站公告

今日签到

点亮在社区的每一天
去签到