您的位置:首页 > 移动开发 > Unity3D

简化Unity开发的利器coroutine

2015-09-23 16:21 651 查看

代码实例

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

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());
}
}


好了,有了这些你已经可以把代码写的足够简单了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: