Unity Learning for Coroutine(协程)

本文深入探讨Unity中的StartCoroutine和yield return用法,解释了协程如何在多帧操作中提高效率,以及它们如何通过yield语句实现代码暂停与恢复。同时,文章还介绍了嵌套协程的执行顺序,并给出了具体的示例。

Unity StartCoroutine 和 yield return 深入研究

StartCoroutine和yield return表面意思很好理解,StartCoroutine就是开启一个协程,yield return是迭代器块返回迭代调用的地方。

MonoBehaviour.StartCoroutine

public Coroutine StartCoroutine(IEnumerator routine);

Description

Starts a coroutine.

The execution of a coroutine can be paused at any point using the yield statement. The yield return value specifies when the coroutine is resumed. Coroutines are excellent when modelling behaviour over several frames. Coroutines have virtually no performance overhead. StartCoroutine function always returns immediately, however you can yield the result. This will wait until the coroutine has finished execution. There is no guarantee that coroutines end in the same order that they were started, even if they finish in the same frame.

意思是:一个协程的执行可以在任何地方用yield语句来暂停,yield return的值决定了什么时候协程恢复执行。协程在协调在几帧中执行的操作时有极大的用处.协程几乎没有任何性能开销。

StartCoroutine一般都会立即返回,然而你也可以获得返回结果的值。但是这一步会等到协程结束执行才能生效。

public class MainTest:MonoBehaviour {
    void Start() {
        Debug.Log("start1");
        StartCoroutine(Test());
        Debug.Log("start2");
    }

    IEnumerator Test()
    {
        Debug.Log("test1");
        yield return null;
        Debug.Log("test2");
    }

//运行结果

    start1
    test1
    start2
    test2
void Start () {
        Debug.Log("start1");
        StartCoroutine(Test());
        Debug.Log("start2");
    }

    IEnumerator Test()
    {
        Debug.Log("test1");
        yield return StartCoroutine(DoSomething());
        Debug.Log("test2");
    }

    IEnumerator DoSomething()
    {
        Debug.Log("load 1");
        yield return null;
        Debug.Log("load 2");
    }



//执行结果:

start1

test1

load1

start2

load2

test2

这种StartCoroutine中嵌套一个yield return StartCoroutine,第一个StartCoroutine会等到第二个StartCoroutine中所有代码结束后再继续执行,而第二个StartCoroutine中的yield语句会先返回第一个,然后立即返回他的调用处,也就是调用处会继续执行,而第一个StartCoroutine会等待第二个执行完再继续执行。

如果还想继续深入,可以看一下C#中的迭代器,那里面说明了yield和IEnumerator根本到底是什么。

总结

协程是一种更加灵活的Update()替代品,避免过多的游戏逻辑堆砌在一个Update()中导致最终的可读性下降。同时还提供了yield return new WaitForSeconds(float)这样方便的等待功能,不需要在外部申明一堆计时器变量然后在逻辑中使用if()else()判断时间这样繁琐的流程。

另外,协程也存在一些问题,就是启用多个协程之后,务必注意代码执行顺序的问题,这个问题在以前可以使用Update()和LateUpdate()缓解,但是在协程里就不太好解决,所以尽量避免多个协程之间的耦合,在一个协程中完成一个独立的功能是一个良好的思路。


学习参考:
1、http://www.unity.5helpyou.com/2658.html
2、异步编程http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
3、协程http://gypsumlabs.github.io/2015/12/08/unity-tips-coroutine/

在以下代码基础上:using UnityEngine; using System; using System.Text; using System.Collections; using System.Security.Cryptography; using NativeWebSocket; using System.Globalization; public class XunfeiISEController : MonoBehaviour { // 配置参数 - 从讯飞控制台获取 const string APP_ID = "d11f22d5"; const string API_KEY = "6c1a2fc3bac9767a92aeb80098c89b61"; const string API_SECRET = "YjJjZmM1MjI3N2ZjMmFiZmQ2Y2E2NWI1"; const string HOST = "ise-api.xfyun.cn"; const string PATH = "/v2/open-ise"; WebSocket websocket; bool isConnected = false; IEnumerator Start() { // 1. 生成鉴权URL string authUrl = GenerateAuthUrl(); Debug.Log("Connecting to: " + authUrl); // 2. 创建WebSocket连接 websocket = new WebSocket(authUrl); // 设置事件回调 websocket.OnOpen += () => { Debug.Log("WebSocket connected!"); isConnected = true; }; websocket.OnError += (err) => { Debug.LogError("Error: " + err); isConnected = false; }; websocket.OnClose += (code) => { Debug.Log("Connection closed: " + code); isConnected = false; }; websocket.OnMessage += (data) => { string json = Encoding.UTF8.GetString(data); Debug.Log("Received: " + json); // 结果解析逻辑 }; // 3. 异步连接 websocket.Connect(); // 4. 等待连接建立(最多5秒) float timeout = 5f; float startTime = Time.time; while (!isConnected && Time.time - startTime < timeout) { yield return null; } if (!isConnected) { Debug.LogError("Connection timeout"); yield break; } // 5. 发送初始化参数帧 SendInitFrame(); } // 生成鉴权URL(严格遵循示例格式) string GenerateAuthUrl() { // 获取RFC1123格式的GMT时间(与示例格式一致) string date = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); Debug.Log("Original Date: " + date); // 构建签名字符串(注意格式) string signatureOrigin = $"host: {HOST}\ndate: {date}\nGET {PATH} HTTP/1.1"; Debug.Log("Signature Origin:\n" + signatureOrigin); // 计算HMAC-SHA256签名 byte[] signatureBytes = HMACSHA256( Encoding.UTF8.GetBytes(API_SECRET), Encoding.UTF8.GetBytes(signatureOrigin) ); string signature = Convert.ToBase64String(signatureBytes); Debug.Log("Signature: " + signature); // 构建Authorization头 string authorization = $"api_key=\"{API_KEY}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature}\""; Debug.Log("Authorization: " + authorization); // Base64编码参数 string encodedAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization)); //string encodedDate = Convert.ToBase64String(Encoding.UTF8.GetBytes(date)); //string encodedHost = Convert.ToBase64String(Encoding.UTF8.GetBytes(HOST)); // 构造完整URL(与示例格式完全一致) return $"wss://{HOST}{PATH}?" + $"authorization={encodedAuth}&" + $"date={date}&" + $"host={HOST}"; } // 发送初始化帧(严格遵循文档格式) void SendInitFrame() { // 评测文本(注意空格和标点) string textContent = "When you don't know what you're doing, it's helpful to begin by learning about what you should not do."; // 构建初始化帧数据结构(使用原始JSON格式) string initJson = @"{ ""common"": { ""app_id"": """ + APP_ID + @""" }, ""business"": { ""aue"": ""raw"", ""auf"": ""audio/L16;rate=16000"", ""category"": ""read_sentence"", ""cmd"": ""ssb"", ""ent"": ""en_vip"", ""sub"": ""ise"", ""text"": """ + Convert.ToBase64String(Encoding.UTF8.GetBytes(textContent)) + @""", ""ttp_skip"": true }, ""data"": { ""status"": 0 } }"; // 发送初始化帧 websocket.Send(Encoding.UTF8.GetBytes(initJson)); Debug.Log("Sent init frame: " + initJson); } void Update() { // 处理消息队列 if (websocket != null && isConnected) { #if !UNITY_WEBGL || UNITY_EDITOR websocket.DispatchMessageQueue(); #endif } } void OnDestroy() { if (websocket != null) { websocket.Close(); } } // HMAC-SHA256计算 byte[] HMACSHA256(byte[] key, byte[] data) { using (var hmac = new HMACSHA256(key)) return hmac.ComputeHash(data); } } 增加发送音频数据以及评测结果反馈
08-08
内容概要:本文围绕六自由度机械臂的人工神经网络(ANN)设计展开,重点研究了正向与逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程,并通过Matlab代码实现相关算法。文章结合理论推导与仿真实践,利用人工神经网络对复杂的非线性关系进行建模与逼近,提升机械臂运动控制的精度与效率。同时涵盖了路径规划中的RRT算法与B样条优化方法,形成从运动学到动力学再到轨迹优化的完整技术链条。; 适合人群:具备一定机器人学、自动控制理论基础,熟悉Matlab编程,从事智能控制、机器人控制、运动学六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)建模等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握机械臂正/逆运动学的数学建模与ANN求解方法;②理解拉格朗日-欧拉法在动力学建模中的应用;③实现基于神经网络的动力学补偿与高精度轨迹跟踪控制;④结合RRT与B样条完成平滑路径规划与优化。; 阅读建议:建议读者结合Matlab代码动手实践,先从运动学建模入手,逐步深入动力学分析与神经网络训练,注重理论推导与仿真实验的结合,以充分理解机械臂控制系统的设计流程与优化策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值