Автор Тема: Coroutines (Корутины)  (Прочитано 5093 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Апрель 11, 2015, 11:40:57 am
Прочитано 5093 раз

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Coroutone - это метод разбивки какой-либо операции или процесса, на равномерное выполнение в каждом кадре.
В итоге получается, что вместо того, чтобы выполнить за один кадр, весь объём действий (что вызовет заметный фриз приложения), это будет выполняться понемногу, каждый кадр, пока не выполнится весь объём действий.

Обычно Coroutine - это функция вида:
StartCoroutine(Coroutine()); //первоначальный запуск корутины

//Сама функция корутины:
public IEnumerator Coroutine()
{
    //{ возможный цикл
      // Выполняемые действия

        yield return (условие, управляющее корутиной);
   //} возможный цикл

    //StartCoroutine(Coroutine()); - возможный запуск корутины снова,
}

Возможные прерывания корутины:
 yield return null; - прерывает выполнение корутины до следующего кадра.
 yield break; завершает выполнение корутины.
 yield return new WaitForSeconds( количество секунд ); - прерывает на время.
 yield return new WaitForEndOfFrame(); - прерывает выполнение до конца кадра.
 yield return new WaitForFixedUpdate(); - прерывает выполнение до кадра, в котором обновляется физика.
 yield return www; - прерывание по факту завершения получения в переменной www, интернет-контента
(например, по факту  завершения загрузки чего-либо)


А так же остановка корутин:
StopAllCoroutines();
StopCoroutine("имя останавливаемой корутины");


Полезные подробности по корутинам, можно почитать тут:
http://docs.unity3d.com/Documentation/ScriptReference/Coroutine.html
http://docs.unity3d.com/Documentation/ScriptReference/YieldInstruction.html
http://docs.unity3d.com/Documentation/ScriptReference/MonoBehaviour.StartCoroutine.html?from=YieldInstruction
Сопрограммы (Coroutone)
http://habrahabr.ru/post/216185/
http://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html



Корутина с бесконечным циклом

Бесконечная корутина никогда сама не остановится, и будет выполнятся до тех пор, пока её искуственно не остановят,
или не завершится выполнение всего приложения.
Такая корутина может быть полезной, для каких-то постоянных действий. Например, для периодических проверок чего-либо.

private IEnumerator MainCoroutine(){

        // тут выполняются одноразовые действие, при запуске корутины.
       

        // бесконечный цикл
        while(true){

     // тут выполняются регулярные действия

            // остановка выполнения функции на 10 миллисекунд
            yield return new WaitForSeconds(0.01f);
        }
}


// запускаем корутину
StartCoroutine(MainCoroutine());




« Последнее редактирование: Ноябрь 17, 2015, 23:51:13 pm от Mimi Neko »

Апрель 11, 2015, 11:43:31 am
Ответ #1

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Возврат значений из Корутины
Порой с возвратом значений из корутины, у новичков могут возникать серьёзные трудности.
Разобраться с этой проблемой, помогут данные паттерны:

Возврат Через делегата - callback функцию, вызывающую метод-приёмник:

public int x = 0;

void Start(){
//запуск корутины "MainCoroutine", с передачей ей функции "setX",
//которая должна принять возвращаемое значение:
StartCoroutine(MainCoroutine(Callback));
}

//Корутина
private IEnumerator MainCoroutine(Action<int> cb){

int newX = 0;
//вычисления над newX:
newX += 33;


cb(newX);//возврат результата через callback функцию
yield return null;
}

//метод, принимающий возвращаемое из корутины значение:
public void Callback(int newX){

x = newX;
Debug.Log("END "+ x);
}
« Последнее редактирование: Апрель 11, 2015, 11:56:41 am от Mimi Neko »

Апрель 11, 2015, 11:44:32 am
Ответ #2

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Возврат значения через лямбды:
public int x = 0;

void Start(){

//запуск корутины "MainCoroutine", с передачей в качестве параметра лямбды,
//которая получает результат, и выгружает его в переменную "x":
StartCoroutine(MainCoroutine(result => x = result));
Debug.Log("END "+ x);
}

//Корутина
private IEnumerator MainCoroutine(Action<int> cb){

int newX = 0;
//вычисления над newX:
newX += 33;

cb(newX);//возврат результата через callback функцию
yield return null;
}

Апрель 11, 2015, 11:47:58 am
Ответ #3

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Ещё вариант возвращение через делегата:

public int x = 0;

void Start(){

//запуск корутины "MainCoroutine", с передачей в качестве параметра лямбды,
//которая получает результат, и выгружает его в переменную "x":
StartCoroutine(MainCoroutine(result => x = result));
Debug.Log("END "+ x);
}

//Корутина
private IEnumerator MainCoroutine(Action<int> cb){

x += 33;
cb(x);//возврат результата через cb-делегата
yield return null;
}

Апрель 11, 2015, 11:51:03 am
Ответ #4

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Прямое изменение значения переменной из корутины, с вызовом каллбэк-метода по окончании:

private int x = 0;

//Метод запуска корутины (по нажатию кнопки UI):
public void test(){

StartCoroutine(MainCoroutine(Callback));
}

//Каллбэк-метод, вызываемый по окончании работы корутины:
public void Callback(){

Debug.Log("END "+ x);
}


//Корутина
private IEnumerator MainCoroutine(Action cb){

while(x < 10){

x++;
Debug.Log(x);
yield return new WaitForSeconds(0.2f);
}
Debug.Log("End!");
cb();//вызов каллбэк-метода
yield break;//выход из корутины (не обязательно?!)
}

Апрель 11, 2015, 11:53:28 am
Ответ #5

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Инкапсуляция метода, выполняемого по окончании, в сам метод StartCoroutine:

private int x = 0;

public void test(){

StartCoroutine(MainCoroutine(()=>Debug.Log("END "+ x)));
}


//Корутина
private IEnumerator MainCoroutine(Action cb){

while(x < 10){

x++;
Debug.Log(x);
yield return new WaitForSeconds(0.2f);
}
Debug.Log("End!");
cb();
yield break;
}

Апрель 11, 2015, 14:26:19 pm
Ответ #6

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
зарезервировано

Апрель 11, 2015, 15:13:18 pm
Ответ #7

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Получение данных через WWW с помощью Корутин

Простейший пример из скрипт референс
(работает только в методе Start, потому что для него не требуется принудительно стартовать корутину)

   
public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";

IEnumerator Start() {
        WWW www = new WWW(url);
        yield return www;
        renderer.material.mainTexture = www.texture;
}

http://docs.unity3d.com/ru/current/ScriptReference/WWW.html

Апрель 11, 2015, 16:02:04 pm
Ответ #8

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Получение данных из php файла, через вызов корутины и www:

public string url = "http://mainsuite.com/test.php";

public void TestWww(){

StartCoroutine(wwwRequest());
}

private IEnumerator wwwRequest(){

        WWW www = new WWW(url);
yield return www;
        Debug.Log(www.isDone);
if(www.isDone) Debug.Log(www.text);
}


Этот же пример, но с циклом:
private IEnumerator wwwRequest(){

        WWW www = new WWW(url);
while(!www.isDone) yield return www;
        Debug.Log(www.isDone);
if(www.isDone) Debug.Log(www.text);
}


Отправка запроса php скрипту на сервер, получение ответа, и его обработка:public void TestWww(){

StartCoroutine(wwwRequest2(url));
}

private IEnumerator wwwRequest(string request){

        var www = new WWW(request);
        while(!www.isDone) yield return www;

        if(!string.IsNullOrEmpty(www.error)){

            //Если есть ошибки - передаём в CallBack функцию информацию об ошибке, и прерываем корутину:
            cb("error=" + www.error);
            yield break;
        }

        //Если ошибок нет - передаём в CallBack функцию, полученые данные в виде текстовой строки:
        cb(www.text);
}

//CallBack функция:
private void cb(string txt){

Debug.Log(txt);
}

Апрель 11, 2015, 16:06:01 pm
Ответ #9

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
« Последнее редактирование: Ноябрь 15, 2017, 18:40:52 pm от Mimi Neko »

Ноябрь 15, 2017, 19:11:32 pm
Ответ #10

Mimi Neko

  • Администратор
  • Старожил форума

  • Оффлайн
  • *****

  • 2453
  • Репутация:
    153
    • Просмотр профиля
Корутины и наследование

Имеется родительский класс Parent.cs:  (он может ни быть к чему-либо прикреплен, а может просто где-то лежать в папке)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Parent : MonoBehaviour {


//void Start (){
// StartCoroutine(MainCoroutine(0));//вызов этой корутины, в этом же классе
//}

        //объявление корутины в родительском классе:
public IEnumerator MainCoroutine(int a){

while(a < 5){
a++;
Debug.Log(a);
yield return null;
}
Debug.Log("end "+ a);
}

}

Так же имеется дочерний от него класс Children.cs:  (он висит на неком объекте)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Children : Parent {



void Start (){

//корутина с таким именем, запустится из родительского класса только в том случае,
//если её нет в дочернем.
StartCoroutine(MainCoroutine(0));//запуск первой доступной корутины
StartCoroutine(base.MainCoroutine(0));//запуск корутины только родительского класса
}

        //Переопределение корутины родительского класса в дочернем:
public IEnumerator MainCoroutine(int a){

while(a < 10){
a++;
Debug.Log(a);
yield return null;
}
Debug.Log("end "+ a);
}
}

 - в результате, в дочерних классах можно запускать корутину, не зависимо от того, определена (точнее, переопределена) ли она в дочернем, или нет.
так же возможно запускать непосредственно корутину у родительского класса.
« Последнее редактирование: Ноябрь 15, 2017, 20:04:45 pm от Mimi Neko »