简化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()); } }
好了,有了这些你已经可以把代码写的足够简单了。
相关文章推荐
- Unity3D之Mecanim动画系统学习笔记(七):IK(反向动力学)动画
- Unity中用路点画线
- unity学习前篇
- Unity3d UGUI 动态加载按钮
- 在Unity3D中实现安卓平台的本地通知推送
- Unity3D:NGUI UIGrid 排序、动态加载与销毁
- Unity3d中的属性(Attributes)整理
- Unity导入FBX自动进行动画切分
- Unity导入FBX自动进行动画切分
- 所谓的Unity多线程
- unity5 人皮渲染 Skin Shading
- 最近用unity5弄的一些渲染
- Unity3D学习笔记(十八)使用插件NGUI制作小地图
- Unity API理解注释
- Unity3D PoolManager缓冲池插件
- Unity Animation
- unity3d实现场景小地图制作
- unity3d实现Loading进度条异步加载场景
- Unity3d报告奇怪的错误CompareBaseObjectsInternal can only be called from the main thread.
- unity3d 学习笔记四 skybox(天空盒) light(光源) halo(光晕)