简化Unity开发的利器coroutine

代码实例

这是一段没有使用协程功能,实现跑马灯功能的代码片段。(下面有完整版本)

    bool isShow = false;
    float alpha = 0;
    string prepareStr;
    void Update () {
        if (isShow == false) {
            if (Text != "") {
                alpha += Time.deltaTime * alphaSpeed;
                Image image = GetComponent<Image>();
                Color c = image.color;
                c.a = alpha;
                image.color = c;
                if (alpha >= 1) {
                    isShow = true;
                    prepareStr = Text;
                    m_text.text = prepareStr;
                    m_textWidth = CalcTextWidth(prepareStr);
                    m_textRect.offsetMax = new Vector2 (m_textWidth + 50, 0);
                    Text = "";
                }
                else return;
            }
            else return;
        }

        if (m_textRect.position.x + m_textWidth / 2 < 0) {
            alpha -= Time.deltaTime * alphaSpeed;
            Image image = GetComponent<Image>();
            image.color = new Color(255, 255, 255, alpha);
            if (alpha <= 0.0f) {
                isShow = false;
            }
        } else {
            float x = Time.deltaTime * 20;
            m_textRect.Translate (-x, 0, 0);
        }
    }

为了不让主线程阻塞,必须把死循环逻辑打破,通过增加临时变量将死循环变成多个执行片。上面的代码逻辑看了就头晕。接下来看看使用协程如果简化代码逻辑。(完整代码)

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class MarqueeScript : MonoBehaviour {

    [TextArea(3, 10)]
    public string text;
    public Font font;
    public int fontSize = 24;
    public float alphaSpeed = 5;
    public float textSpeed = 20;

    RectTransform _textRect;

    // Use this for initialization
    void Start () {
        GameObject panel = new GameObject ("Panel");
        RectTransform rect = panel.AddComponent<RectTransform> ();
        rect.SetParent (transform);
        rect.localScale = Vector3.one;
        rect.anchorMin = Vector2.zero;
        rect.anchorMax = Vector2.one;
        rect.offsetMin = new Vector2 (8, 8);
        rect.offsetMax = new Vector2 (-8, -8);

        Image image = panel.AddComponent<Image> ();
        image.color = Color.white;
        Mask mask = panel.AddComponent<Mask> ();
        mask.showMaskGraphic = false;

        GameObject textComp = new GameObject ("Text");
        _textRect = textComp.AddComponent<RectTransform> ();
        _textRect.SetParent (rect);
        _textRect.localScale = Vector3.one;
        _textRect.pivot = Vector2.zero;
        _textRect.anchorMin = new Vector2 (1, 0);
        _textRect.anchorMax = Vector2.one;
        _textRect.offsetMin = Vector2.zero;
        _textRect.offsetMax = Vector2.zero;

        Text m_text = textComp.AddComponent<Text> ();
        m_text.text = text;
        m_text.font = font; // Resources.GetBuiltinResource<Font>("Arial.ttf");
        m_text.fontSize = fontSize;
        m_text.resizeTextForBestFit = true;
        m_text.alignment = TextAnchor.MiddleLeft;
        m_text.horizontalOverflow = HorizontalWrapMode.Overflow;

        image = GetComponent<Image>();
        Color c = image.color;
        c.a = 0;
        image.color = c;

        StartCoroutine (UpdateControl());
    }

    IEnumerator UpdateControl()    {
        yield return null;
        Image image = GetComponent<Image>();
        while (text.Length > 0) {
            // fade in
            while(image.color.a < 1f) {
                Color c = image.color;
                c.a += Time.deltaTime * alphaSpeed;
                image.color = c;
                yield return null;
            }
            // move text
            float width = CalcTextWidth(text);
            _textRect.offsetMin = Vector2.zero;
            _textRect.offsetMax = new Vector2 (width, 0);
            yield return null;
            while(_textRect.position.x > -width) {
                float x = Time.deltaTime * textSpeed;
                _textRect.Translate (-x, 0, 0);
                yield return null;
            }
            // fade out
            while(image.color.a > 0f) {
                Color c = image.color;
                c.a -= Time.deltaTime * alphaSpeed;
                image.color = c;
                yield return null;
            }
            yield return new WaitForSeconds(3.0f);
        }
    }

    float CalcTextWidth(string text)
    {
        font.RequestCharactersInTexture(text, fontSize, FontStyle.Normal);
        CharacterInfo characterInfo;
        float width = 0.0f;
        for(int i = 0; i < text.Length; i++){
            font.GetCharacterInfo(text[i], out characterInfo, fontSize);
            width += characterInfo.advance;
        }
        return width;        
    }
}

把上面脚本保存为MarqueeScript.cs,使用增加一个Panel(UGUI)控件,添加该脚本,设置要显示的文字内容。接下来就可以看到文字开始跑动了。

代码解析

核心函数UpdateControl()。 里面的逻辑很清晰淡入,跑动文字,淡出,每个部分都是一个死循环,最外面一个大循环。
来看里面的关键地方:yield return null;
他最巧妙的地方在于你需要返回的地方用yield return null;语句就可以了。当下一帧到来时代码会从该语句的下一行开始继续执行。就算你第一次接触他,只要你对比上下代码也很快就能上手使用了。

开发流程

  • 启动一个协程使用:
StartCoroutine (function1());
  • 声明协程方法:
IEnumerator function1() {
}
  • 编写逻辑,无需考虑退出。例如:
IEnumerator function1() {
    for(int i = 0; i < 100; i++) {
       print(i);
       Thread.Sleep(3000);
    }
}
  • 在需要的地方插入yield语句,或修改阻塞语句。例如:
IEnumerator function1() {
    for(int i = 0; i < 100; i++) {
       print(i);
       yield return new WaitForSeconds(3.0f);// Thread.Sleep(3000);
    }
}

OK, 基本流程就是这样。

用法总结

yield return后面还可以跟以下返回值:null、WaitForEndOfFrame、WaitForFixedUpdate、WaitForSeconds
下图描述了每中情况下的激活执行的时机:
这里写图片描述
yield return后面还可以跟WWW对象,表示等网页加载完成后继续执行。 例如:

// Get the latest webcam shot from outside "Friday's" in Times Square
using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
    IEnumerator Start() {
        WWW www = new WWW(url);
        yield return www;
        Renderer renderer = GetComponent<Renderer>();
    renderer.material.mainTexture = www.texture;
}

yield return后面还可以跟其他协程对象,表示等其他协程执行完成后继续执行。例如:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    IEnumerator a1() {
        print ("a1");
        yield return StartCoroutine (b1());
        print ("a2");
    }

    IEnumerator b1() {
        for (int i = 0; i < 4; i++) {
            print ("b");
            yield return new WaitForSeconds (0.5f);
        }
    }
    void Start()
    {
        StartCoroutine (a1());
    }
}

好了,有了这些你已经可以把代码写的足够简单了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值